LCOV - code coverage report
Current view: top level - ccache - manifest.c (source / functions) Hit Total Coverage
Test: ccache.lcov.info Lines: 0 347 0.0 %
Date: 2015-06-29 Functions: 0 16 0.0 %
Branches: 0 274 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2009-2015 Joel Rosdahl
       3                 :            :  *
       4                 :            :  * This program is free software; you can redistribute it and/or modify it
       5                 :            :  * under the terms of the GNU General Public License as published by the Free
       6                 :            :  * Software Foundation; either version 3 of the License, or (at your option)
       7                 :            :  * any later version.
       8                 :            :  *
       9                 :            :  * This program is distributed in the hope that it will be useful, but WITHOUT
      10                 :            :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      11                 :            :  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
      12                 :            :  * more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU General Public License along with
      15                 :            :  * this program; if not, write to the Free Software Foundation, Inc., 51
      16                 :            :  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      17                 :            :  */
      18                 :            : 
      19                 :            : #include "ccache.h"
      20                 :            : #include "hashtable_itr.h"
      21                 :            : #include "hashutil.h"
      22                 :            : #include "manifest.h"
      23                 :            : #include "murmurhashneutral2.h"
      24                 :            : 
      25                 :            : #include <zlib.h>
      26                 :            : 
      27                 :            : /*
      28                 :            :  * Sketchy specification of the manifest disk format:
      29                 :            :  *
      30                 :            :  * <magic>         magic number                        (4 bytes)
      31                 :            :  * <version>       file format version                 (1 byte unsigned int)
      32                 :            :  * <hash_size>     size of the hash fields (in bytes)  (1 byte unsigned int)
      33                 :            :  * <reserved>      reserved for future use             (2 bytes)
      34                 :            :  * ----------------------------------------------------------------------------
      35                 :            :  * <n>             number of include file paths        (4 bytes unsigned int)
      36                 :            :  * <path_0>        path to include file                (NUL-terminated string,
      37                 :            :  * ...                                                  at most 1024 bytes)
      38                 :            :  * <path_n-1>
      39                 :            :  * ----------------------------------------------------------------------------
      40                 :            :  * <n>             number of include file hash entries (4 bytes unsigned int)
      41                 :            :  * <index[0]>      index of include file path          (4 bytes unsigned int)
      42                 :            :  * <hash[0]>       hash of include file                (<hash_size> bytes)
      43                 :            :  * <size[0]>       size of include file                (4 bytes unsigned int)
      44                 :            :  * <mtime[0]>      mtime of include file               (8 bytes signed int)
      45                 :            :  * <ctime[0]>      ctime of include file               (8 bytes signed int)
      46                 :            :  * ...
      47                 :            :  * <index[n-1]>
      48                 :            :  * <hash[n-1]>
      49                 :            :  * <size[n-1]>
      50                 :            :  * <mtime[n-1]>
      51                 :            :  * <ctime[n-1]>
      52                 :            :  * ----------------------------------------------------------------------------
      53                 :            :  * <n>             number of object name entries       (4 bytes unsigned int)
      54                 :            :  * <m[0]>          number of include file hash indexes (4 bytes unsigned int)
      55                 :            :  * <index[0][0]>   include file hash index             (4 bytes unsigned int)
      56                 :            :  * ...
      57                 :            :  * <index[0][m[0]-1]>
      58                 :            :  * <hash[0]>       hash part of object name            (<hash_size> bytes)
      59                 :            :  * <size[0]>       size part of object name            (4 bytes unsigned int)
      60                 :            :  * ...
      61                 :            :  * <m[n-1]>        number of include file hash indexes
      62                 :            :  * <index[n-1][0]> include file hash index
      63                 :            :  * ...
      64                 :            :  * <index[n-1][m[n-1]]>
      65                 :            :  * <hash[n-1]>
      66                 :            :  * <size[n-1]>
      67                 :            :  */
      68                 :            : 
      69                 :            : static const uint32_t MAGIC = 0x63436d46U;
      70                 :            : static const uint32_t MAX_MANIFEST_ENTRIES = 100;
      71                 :            : static const uint32_t MAX_MANIFEST_FILE_INFO_ENTRIES = 10000;
      72                 :            : 
      73                 :            : #define ccache_static_assert(e) \
      74                 :            :         do { enum { ccache_static_assert__ = 1/(e) }; } while (false)
      75                 :            : 
      76                 :            : struct file_info {
      77                 :            :         /* Index to n_files. */
      78                 :            :         uint32_t index;
      79                 :            :         /* Hash of referenced file. */
      80                 :            :         uint8_t hash[16];
      81                 :            :         /* Size of referenced file. */
      82                 :            :         uint32_t size;
      83                 :            :         /* mtime of referenced file. */
      84                 :            :         int64_t mtime;
      85                 :            :         /* ctime of referenced file. */
      86                 :            :         int64_t ctime;
      87                 :            : };
      88                 :            : 
      89                 :            : struct object {
      90                 :            :         /* Number of entries in file_info_indexes. */
      91                 :            :         uint32_t n_file_info_indexes;
      92                 :            :         /* Indexes to file_infos. */
      93                 :            :         uint32_t *file_info_indexes;
      94                 :            :         /* Hash of the object itself. */
      95                 :            :         struct file_hash hash;
      96                 :            : };
      97                 :            : 
      98                 :            : struct manifest {
      99                 :            :         /* Version of decoded file. */
     100                 :            :         uint8_t version;
     101                 :            : 
     102                 :            :         /* Reserved for future use. */
     103                 :            :         uint16_t reserved;
     104                 :            : 
     105                 :            :         /* Size of hash fields (in bytes). */
     106                 :            :         uint8_t hash_size;
     107                 :            : 
     108                 :            :         /* Referenced include files. */
     109                 :            :         uint32_t n_files;
     110                 :            :         char **files;
     111                 :            : 
     112                 :            :         /* Information about referenced include files. */
     113                 :            :         uint32_t n_file_infos;
     114                 :            :         struct file_info *file_infos;
     115                 :            : 
     116                 :            :         /* Object names plus references to include file hashes. */
     117                 :            :         uint32_t n_objects;
     118                 :            :         struct object *objects;
     119                 :            : };
     120                 :            : 
     121                 :            : struct file_stats {
     122                 :            :         uint32_t size;
     123                 :            :         int64_t mtime;
     124                 :            :         int64_t ctime;
     125                 :            : };
     126                 :            : 
     127                 :            : static unsigned int
     128                 :          0 : hash_from_file_info(void *key)
     129                 :            : {
     130                 :            :         ccache_static_assert(sizeof(struct file_info) == 40); /* No padding. */
     131                 :          0 :         return murmurhashneutral2(key, sizeof(struct file_info), 0);
     132                 :            : }
     133                 :            : 
     134                 :            : static int
     135                 :          0 : file_infos_equal(void *key1, void *key2)
     136                 :            : {
     137                 :          0 :         struct file_info *fi1 = (struct file_info *)key1;
     138                 :          0 :         struct file_info *fi2 = (struct file_info *)key2;
     139 [ #  # ][ #  # ]:          0 :         return fi1->index == fi2->index
         [ #  # ][ #  # ]
                 [ #  # ]
     140                 :          0 :                && memcmp(fi1->hash, fi2->hash, 16) == 0
     141                 :          0 :                && fi1->size == fi2->size
     142                 :          0 :                && fi1->mtime == fi2->mtime
     143                 :          0 :                && fi1->ctime == fi2->ctime;
     144                 :            : }
     145                 :            : 
     146                 :            : static void
     147                 :          0 : free_manifest(struct manifest *mf)
     148                 :            : {
     149                 :            :         uint32_t i;
     150         [ #  # ]:          0 :         for (i = 0; i < mf->n_files; i++) {
     151                 :          0 :                 free(mf->files[i]);
     152                 :            :         }
     153                 :          0 :         free(mf->files);
     154                 :          0 :         free(mf->file_infos);
     155         [ #  # ]:          0 :         for (i = 0; i < mf->n_objects; i++) {
     156                 :          0 :                 free(mf->objects[i].file_info_indexes);
     157                 :            :         }
     158                 :          0 :         free(mf->objects);
     159                 :          0 :         free(mf);
     160                 :          0 : }
     161                 :            : 
     162                 :            : #define READ_BYTE(var) \
     163                 :            :         do { \
     164                 :            :                 int ch_; \
     165                 :            :                 ch_ = gzgetc(f); \
     166                 :            :                 if (ch_ == EOF) { \
     167                 :            :                         goto error; \
     168                 :            :                 } \
     169                 :            :                 (var) = ch_ & 0xFF; \
     170                 :            :         } while (false)
     171                 :            : 
     172                 :            : #define READ_INT(size, var) \
     173                 :            :         do { \
     174                 :            :                 int ch_; \
     175                 :            :                 size_t i_; \
     176                 :            :                 (var) = 0; \
     177                 :            :                 for (i_ = 0; i_ < (size); i_++) { \
     178                 :            :                         ch_ = gzgetc(f); \
     179                 :            :                         if (ch_ == EOF) { \
     180                 :            :                                 goto error; \
     181                 :            :                         } \
     182                 :            :                         (var) <<= 8; \
     183                 :            :                         (var) |= ch_ & 0xFF; \
     184                 :            :                 } \
     185                 :            :         } while (false)
     186                 :            : 
     187                 :            : #define READ_STR(var) \
     188                 :            :         do { \
     189                 :            :                 char buf_[1024]; \
     190                 :            :                 size_t i_; \
     191                 :            :                 int ch_; \
     192                 :            :                 for (i_ = 0; i_ < sizeof(buf_); i_++) { \
     193                 :            :                         ch_ = gzgetc(f); \
     194                 :            :                         if (ch_ == EOF) { \
     195                 :            :                                 goto error; \
     196                 :            :                         } \
     197                 :            :                         buf_[i_] = ch_; \
     198                 :            :                         if (ch_ == '\0') { \
     199                 :            :                                 break; \
     200                 :            :                         } \
     201                 :            :                 } \
     202                 :            :                 if (i_ == sizeof(buf_)) { \
     203                 :            :                         goto error; \
     204                 :            :                 } \
     205                 :            :                 (var) = x_strdup(buf_); \
     206                 :            :         } while (false)
     207                 :            : 
     208                 :            : #define READ_BYTES(n, var) \
     209                 :            :         do { \
     210                 :            :                 size_t i_; \
     211                 :            :                 int ch_; \
     212                 :            :                 for (i_ = 0; i_ < (n); i_++) { \
     213                 :            :                         ch_ = gzgetc(f); \
     214                 :            :                         if (ch_ == EOF) { \
     215                 :            :                                 goto error; \
     216                 :            :                         } \
     217                 :            :                         (var)[i_] = ch_; \
     218                 :            :                 } \
     219                 :            :         } while (false)
     220                 :            : 
     221                 :            : static struct manifest *
     222                 :          0 : create_empty_manifest(void)
     223                 :            : {
     224                 :            :         struct manifest *mf;
     225                 :            : 
     226                 :          0 :         mf = x_malloc(sizeof(*mf));
     227                 :          0 :         mf->hash_size = 16;
     228                 :          0 :         mf->n_files = 0;
     229                 :          0 :         mf->files = NULL;
     230                 :          0 :         mf->n_file_infos = 0;
     231                 :          0 :         mf->file_infos = NULL;
     232                 :          0 :         mf->n_objects = 0;
     233                 :          0 :         mf->objects = NULL;
     234                 :            : 
     235                 :          0 :         return mf;
     236                 :            : }
     237                 :            : 
     238                 :            : static struct manifest *
     239                 :          0 : read_manifest(gzFile f)
     240                 :            : {
     241                 :            :         struct manifest *mf;
     242                 :            :         uint32_t i, j;
     243                 :            :         uint32_t magic;
     244                 :            : 
     245                 :          0 :         mf = create_empty_manifest();
     246                 :            : 
     247 [ #  # ][ #  # ]:          0 :         READ_INT(4, magic);
     248         [ #  # ]:          0 :         if (magic != MAGIC) {
     249                 :          0 :                 cc_log("Manifest file has bad magic number %u", magic);
     250                 :          0 :                 free_manifest(mf);
     251                 :          0 :                 return NULL;
     252                 :            :         }
     253         [ #  # ]:          0 :         READ_BYTE(mf->version);
     254         [ #  # ]:          0 :         if (mf->version != MANIFEST_VERSION) {
     255                 :          0 :                 cc_log("Manifest file has unknown version %u", mf->version);
     256                 :          0 :                 free_manifest(mf);
     257                 :          0 :                 return NULL;
     258                 :            :         }
     259                 :            : 
     260         [ #  # ]:          0 :         READ_BYTE(mf->hash_size);
     261         [ #  # ]:          0 :         if (mf->hash_size != 16) {
     262                 :            :                 /* Temporary measure until we support different hash algorithms. */
     263                 :          0 :                 cc_log("Manifest file has unsupported hash size %u", mf->hash_size);
     264                 :          0 :                 free_manifest(mf);
     265                 :          0 :                 return NULL;
     266                 :            :         }
     267                 :            : 
     268 [ #  # ][ #  # ]:          0 :         READ_INT(2, mf->reserved);
     269                 :            : 
     270 [ #  # ][ #  # ]:          0 :         READ_INT(4, mf->n_files);
     271                 :          0 :         mf->files = x_calloc(mf->n_files, sizeof(*mf->files));
     272         [ #  # ]:          0 :         for (i = 0; i < mf->n_files; i++) {
     273 [ #  # ][ #  # ]:          0 :                 READ_STR(mf->files[i]);
         [ #  # ][ #  # ]
     274                 :            :         }
     275                 :            : 
     276 [ #  # ][ #  # ]:          0 :         READ_INT(4, mf->n_file_infos);
     277                 :          0 :         mf->file_infos = x_calloc(mf->n_file_infos, sizeof(*mf->file_infos));
     278         [ #  # ]:          0 :         for (i = 0; i < mf->n_file_infos; i++) {
     279 [ #  # ][ #  # ]:          0 :                 READ_INT(4, mf->file_infos[i].index);
     280 [ #  # ][ #  # ]:          0 :                 READ_BYTES(mf->hash_size, mf->file_infos[i].hash);
     281 [ #  # ][ #  # ]:          0 :                 READ_INT(4, mf->file_infos[i].size);
     282 [ #  # ][ #  # ]:          0 :                 READ_INT(8, mf->file_infos[i].mtime);
     283 [ #  # ][ #  # ]:          0 :                 READ_INT(8, mf->file_infos[i].ctime);
     284                 :            :         }
     285                 :            : 
     286 [ #  # ][ #  # ]:          0 :         READ_INT(4, mf->n_objects);
     287                 :          0 :         mf->objects = x_calloc(mf->n_objects, sizeof(*mf->objects));
     288         [ #  # ]:          0 :         for (i = 0; i < mf->n_objects; i++) {
     289 [ #  # ][ #  # ]:          0 :                 READ_INT(4, mf->objects[i].n_file_info_indexes);
     290                 :          0 :                 mf->objects[i].file_info_indexes =
     291                 :          0 :                         x_calloc(mf->objects[i].n_file_info_indexes,
     292                 :            :                                  sizeof(*mf->objects[i].file_info_indexes));
     293         [ #  # ]:          0 :                 for (j = 0; j < mf->objects[i].n_file_info_indexes; j++) {
     294 [ #  # ][ #  # ]:          0 :                         READ_INT(4, mf->objects[i].file_info_indexes[j]);
     295                 :            :                 }
     296 [ #  # ][ #  # ]:          0 :                 READ_BYTES(mf->hash_size, mf->objects[i].hash.hash);
     297 [ #  # ][ #  # ]:          0 :                 READ_INT(4, mf->objects[i].hash.size);
     298                 :            :         }
     299                 :            : 
     300                 :          0 :         return mf;
     301                 :            : 
     302                 :            : error:
     303                 :          0 :         cc_log("Corrupt manifest file");
     304                 :          0 :         free_manifest(mf);
     305                 :          0 :         return NULL;
     306                 :            : }
     307                 :            : 
     308                 :            : #define WRITE_INT(size, var) \
     309                 :            :         do { \
     310                 :            :                 uint8_t ch_; \
     311                 :            :                 size_t i_; \
     312                 :            :                 for (i_ = 0; i_ < (size); i_++) { \
     313                 :            :                         ch_ = ((var) >> (8 * ((size) - i_ - 1))); \
     314                 :            :                         if (gzputc(f, ch_) == EOF) { \
     315                 :            :                                 goto error; \
     316                 :            :                         } \
     317                 :            :                 } \
     318                 :            :         } while (false)
     319                 :            : 
     320                 :            : #define WRITE_STR(var) \
     321                 :            :         do { \
     322                 :            :                 if (gzputs(f, var) == EOF || gzputc(f, '\0') == EOF) { \
     323                 :            :                         goto error; \
     324                 :            :                 } \
     325                 :            :         } while (false)
     326                 :            : 
     327                 :            : #define WRITE_BYTES(n, var) \
     328                 :            :         do { \
     329                 :            :                 size_t i_; \
     330                 :            :                 for (i_ = 0; i_ < (n); i_++) { \
     331                 :            :                         if (gzputc(f, (var)[i_]) == EOF) { \
     332                 :            :                                 goto error; \
     333                 :            :                         } \
     334                 :            :                 } \
     335                 :            :         } while (false)
     336                 :            : 
     337                 :            : static int
     338                 :          0 : write_manifest(gzFile f, const struct manifest *mf)
     339                 :            : {
     340                 :            :         uint32_t i, j;
     341                 :            : 
     342 [ #  # ][ #  # ]:          0 :         WRITE_INT(4, MAGIC);
     343 [ #  # ][ #  # ]:          0 :         WRITE_INT(1, MANIFEST_VERSION);
     344 [ #  # ][ #  # ]:          0 :         WRITE_INT(1, 16);
     345 [ #  # ][ #  # ]:          0 :         WRITE_INT(2, 0);
     346                 :            : 
     347 [ #  # ][ #  # ]:          0 :         WRITE_INT(4, mf->n_files);
     348         [ #  # ]:          0 :         for (i = 0; i < mf->n_files; i++) {
     349 [ #  # ][ #  # ]:          0 :                 WRITE_STR(mf->files[i]);
     350                 :            :         }
     351                 :            : 
     352 [ #  # ][ #  # ]:          0 :         WRITE_INT(4, mf->n_file_infos);
     353         [ #  # ]:          0 :         for (i = 0; i < mf->n_file_infos; i++) {
     354 [ #  # ][ #  # ]:          0 :                 WRITE_INT(4, mf->file_infos[i].index);
     355 [ #  # ][ #  # ]:          0 :                 WRITE_BYTES(mf->hash_size, mf->file_infos[i].hash);
     356 [ #  # ][ #  # ]:          0 :                 WRITE_INT(4, mf->file_infos[i].size);
     357 [ #  # ][ #  # ]:          0 :                 WRITE_INT(8, mf->file_infos[i].mtime);
     358 [ #  # ][ #  # ]:          0 :                 WRITE_INT(8, mf->file_infos[i].ctime);
     359                 :            :         }
     360                 :            : 
     361 [ #  # ][ #  # ]:          0 :         WRITE_INT(4, mf->n_objects);
     362         [ #  # ]:          0 :         for (i = 0; i < mf->n_objects; i++) {
     363 [ #  # ][ #  # ]:          0 :                 WRITE_INT(4, mf->objects[i].n_file_info_indexes);
     364         [ #  # ]:          0 :                 for (j = 0; j < mf->objects[i].n_file_info_indexes; j++) {
     365 [ #  # ][ #  # ]:          0 :                         WRITE_INT(4, mf->objects[i].file_info_indexes[j]);
     366                 :            :                 }
     367 [ #  # ][ #  # ]:          0 :                 WRITE_BYTES(mf->hash_size, mf->objects[i].hash.hash);
     368 [ #  # ][ #  # ]:          0 :                 WRITE_INT(4, mf->objects[i].hash.size);
     369                 :            :         }
     370                 :            : 
     371                 :          0 :         return 1;
     372                 :            : 
     373                 :            : error:
     374                 :          0 :         cc_log("Error writing to manifest file");
     375                 :          0 :         return 0;
     376                 :            : }
     377                 :            : 
     378                 :            : static int
     379                 :          0 : verify_object(struct conf *conf, struct manifest *mf, struct object *obj,
     380                 :            :               struct hashtable *stated_files, struct hashtable *hashed_files)
     381                 :            : {
     382                 :            :         uint32_t i;
     383                 :            :         struct file_info *fi;
     384                 :            :         struct file_hash *actual;
     385                 :            :         struct file_stats *st;
     386                 :            :         struct mdfour hash;
     387                 :            :         int result;
     388                 :            :         char *path;
     389                 :            : 
     390         [ #  # ]:          0 :         for (i = 0; i < obj->n_file_info_indexes; i++) {
     391                 :          0 :                 fi = &mf->file_infos[obj->file_info_indexes[i]];
     392                 :          0 :                 path = mf->files[fi->index];
     393                 :          0 :                 st = hashtable_search(hashed_files, path);
     394         [ #  # ]:          0 :                 if (!st) {
     395                 :            :                         struct stat file_stat;
     396         [ #  # ]:          0 :                         if (x_stat(path, &file_stat) != 0) {
     397                 :          0 :                                 return 0;
     398                 :            :                         }
     399                 :          0 :                         st = x_malloc(sizeof(*st));
     400                 :          0 :                         st->size = file_stat.st_size;
     401                 :          0 :                         st->mtime = file_stat.st_mtime;
     402                 :          0 :                         st->ctime = file_stat.st_ctime;
     403                 :          0 :                         hashtable_insert(stated_files, x_strdup(path), st);
     404                 :            :                 }
     405                 :            : 
     406         [ #  # ]:          0 :                 if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) {
     407                 :            :                         /*
     408                 :            :                          * st->ctime is sometimes 0, so we can't check that both st->ctime and
     409                 :            :                          * st->mtime are greater than time_of_compilation. But it's sufficient to
     410                 :            :                          * check that either is.
     411                 :            :                          */
     412 [ #  # ][ #  # ]:          0 :                         if (fi->size == st->size
         [ #  # ][ #  # ]
     413                 :          0 :                             && fi->mtime == st->mtime
     414                 :          0 :                             && fi->ctime == st->ctime
     415                 :          0 :                             && MAX(st->mtime, st->ctime) >= time_of_compilation) {
     416                 :          0 :                                 cc_log("size/mtime/ctime hit for %s", path);
     417                 :          0 :                                 continue;
     418                 :            :                         }
     419                 :            :                         else {
     420                 :          0 :                                 cc_log("size/mtime/ctime miss for %s", path);
     421                 :            :                         }
     422                 :            :                 }
     423                 :            : 
     424                 :          0 :                 actual = hashtable_search(hashed_files, path);
     425         [ #  # ]:          0 :                 if (!actual) {
     426                 :          0 :                         actual = x_malloc(sizeof(*actual));
     427                 :          0 :                         hash_start(&hash);
     428                 :          0 :                         result = hash_source_code_file(conf, &hash, path);
     429         [ #  # ]:          0 :                         if (result & HASH_SOURCE_CODE_ERROR) {
     430                 :          0 :                                 cc_log("Failed hashing %s", path);
     431                 :          0 :                                 free(actual);
     432                 :          0 :                                 return 0;
     433                 :            :                         }
     434         [ #  # ]:          0 :                         if (result & HASH_SOURCE_CODE_FOUND_TIME) {
     435                 :          0 :                                 free(actual);
     436                 :          0 :                                 return 0;
     437                 :            :                         }
     438                 :          0 :                         hash_result_as_bytes(&hash, actual->hash);
     439                 :          0 :                         actual->size = hash.totalN;
     440                 :          0 :                         hashtable_insert(hashed_files, x_strdup(path), actual);
     441                 :            :                 }
     442 [ #  # ][ #  # ]:          0 :                 if (memcmp(fi->hash, actual->hash, mf->hash_size) != 0
     443                 :          0 :                     || fi->size != actual->size) {
     444                 :          0 :                         return 0;
     445                 :            :                 }
     446                 :            :         }
     447                 :            : 
     448                 :          0 :         return 1;
     449                 :            : }
     450                 :            : 
     451                 :            : static struct hashtable *
     452                 :          0 : create_string_index_map(char **strings, uint32_t len)
     453                 :            : {
     454                 :            :         uint32_t i;
     455                 :            :         struct hashtable *h;
     456                 :            :         uint32_t *index;
     457                 :            : 
     458                 :          0 :         h = create_hashtable(1000, hash_from_string, strings_equal);
     459         [ #  # ]:          0 :         for (i = 0; i < len; i++) {
     460                 :          0 :                 index = x_malloc(sizeof(*index));
     461                 :          0 :                 *index = i;
     462                 :          0 :                 hashtable_insert(h, x_strdup(strings[i]), index);
     463                 :            :         }
     464                 :          0 :         return h;
     465                 :            : }
     466                 :            : 
     467                 :            : static struct hashtable *
     468                 :          0 : create_file_info_index_map(struct file_info *infos, uint32_t len)
     469                 :            : {
     470                 :            :         uint32_t i;
     471                 :            :         struct hashtable *h;
     472                 :            :         struct file_info *fi;
     473                 :            :         uint32_t *index;
     474                 :            : 
     475                 :          0 :         h = create_hashtable(1000, hash_from_file_info, file_infos_equal);
     476         [ #  # ]:          0 :         for (i = 0; i < len; i++) {
     477                 :          0 :                 fi = x_malloc(sizeof(*fi));
     478                 :          0 :                 *fi = infos[i];
     479                 :          0 :                 index = x_malloc(sizeof(*index));
     480                 :          0 :                 *index = i;
     481                 :          0 :                 hashtable_insert(h, fi, index);
     482                 :            :         }
     483                 :          0 :         return h;
     484                 :            : }
     485                 :            : 
     486                 :            : static uint32_t
     487                 :          0 : get_include_file_index(struct manifest *mf, char *path,
     488                 :            :                        struct hashtable *mf_files)
     489                 :            : {
     490                 :            :         uint32_t *index;
     491                 :            :         uint32_t n;
     492                 :            : 
     493                 :          0 :         index = hashtable_search(mf_files, path);
     494         [ #  # ]:          0 :         if (index) {
     495                 :          0 :                 return *index;
     496                 :            :         }
     497                 :            : 
     498                 :          0 :         n = mf->n_files;
     499                 :          0 :         mf->files = x_realloc(mf->files, (n + 1) * sizeof(*mf->files));
     500                 :          0 :         mf->n_files++;
     501                 :          0 :         mf->files[n] = x_strdup(path);
     502                 :            : 
     503                 :          0 :         return n;
     504                 :            : }
     505                 :            : 
     506                 :            : static uint32_t
     507                 :          0 : get_file_hash_index(struct manifest *mf,
     508                 :            :                     char *path,
     509                 :            :                     struct file_hash *file_hash,
     510                 :            :                     struct hashtable *mf_files,
     511                 :            :                     struct hashtable *mf_file_infos)
     512                 :            : {
     513                 :            :         struct file_info fi;
     514                 :            :         uint32_t *fi_index;
     515                 :            :         uint32_t n;
     516                 :            :         struct stat file_stat;
     517                 :            : 
     518                 :          0 :         fi.index = get_include_file_index(mf, path, mf_files);
     519                 :          0 :         memcpy(fi.hash, file_hash->hash, sizeof(fi.hash));
     520                 :          0 :         fi.size = file_hash->size;
     521                 :            : 
     522                 :            :         /*
     523                 :            :          * file_stat.st_{m,c}time has a resolution of 1 second, so we can cache the
     524                 :            :          * file's mtime and ctime only if they're at least one second older than
     525                 :            :          * time_of_compilation.
     526                 :            :          *
     527                 :            :          * st->ctime may be 0, so we have to check time_of_compilation against
     528                 :            :          * MAX(mtime, ctime).
     529                 :            :          */
     530                 :            : 
     531 [ #  # ][ #  # ]:          0 :         if (stat(path, &file_stat) != -1
     532                 :          0 :             && time_of_compilation > MAX(file_stat.st_mtime, file_stat.st_ctime)) {
     533                 :          0 :                 fi.mtime = file_stat.st_mtime;
     534                 :          0 :                 fi.ctime = file_stat.st_ctime;
     535                 :            :         }
     536                 :            :         else {
     537                 :          0 :                 fi.mtime = -1;
     538                 :          0 :                 fi.ctime = -1;
     539                 :            :         }
     540                 :            : 
     541                 :          0 :         fi_index = hashtable_search(mf_file_infos, &fi);
     542         [ #  # ]:          0 :         if (fi_index) {
     543                 :          0 :                 return *fi_index;
     544                 :            :         }
     545                 :            : 
     546                 :          0 :         n = mf->n_file_infos;
     547                 :          0 :         mf->file_infos = x_realloc(mf->file_infos, (n + 1) * sizeof(*mf->file_infos));
     548                 :          0 :         mf->n_file_infos++;
     549                 :          0 :         mf->file_infos[n] = fi;
     550                 :            : 
     551                 :          0 :         return n;
     552                 :            : }
     553                 :            : 
     554                 :            : static void
     555                 :          0 : add_file_info_indexes(uint32_t *indexes, uint32_t size,
     556                 :            :                       struct manifest *mf, struct hashtable *included_files)
     557                 :            : {
     558                 :            :         struct hashtable_itr *iter;
     559                 :            :         uint32_t i;
     560                 :            :         char *path;
     561                 :            :         struct file_hash *file_hash;
     562                 :            :         struct hashtable *mf_files; /* path --> index */
     563                 :            :         struct hashtable *mf_file_infos; /* struct file_info --> index */
     564                 :            : 
     565         [ #  # ]:          0 :         if (size == 0) {
     566                 :          0 :                 return;
     567                 :            :         }
     568                 :            : 
     569                 :          0 :         mf_files = create_string_index_map(mf->files, mf->n_files);
     570                 :          0 :         mf_file_infos = create_file_info_index_map(mf->file_infos, mf->n_file_infos);
     571                 :          0 :         iter = hashtable_iterator(included_files);
     572                 :          0 :         i = 0;
     573                 :            :         do {
     574                 :          0 :                 path = hashtable_iterator_key(iter);
     575                 :          0 :                 file_hash = hashtable_iterator_value(iter);
     576                 :          0 :                 indexes[i] = get_file_hash_index(mf, path, file_hash, mf_files,
     577                 :            :                                                  mf_file_infos);
     578                 :          0 :                 i++;
     579         [ #  # ]:          0 :         } while (hashtable_iterator_advance(iter));
     580         [ #  # ]:          0 :         assert(i == size);
     581                 :            : 
     582                 :          0 :         hashtable_destroy(mf_file_infos, 1);
     583                 :          0 :         hashtable_destroy(mf_files, 1);
     584                 :            : }
     585                 :            : 
     586                 :            : static void
     587                 :          0 : add_object_entry(struct manifest *mf,
     588                 :            :                  struct file_hash *object_hash,
     589                 :            :                  struct hashtable *included_files)
     590                 :            : {
     591                 :            :         struct object *obj;
     592                 :            :         uint32_t n;
     593                 :            : 
     594                 :          0 :         n = mf->n_objects;
     595                 :          0 :         mf->objects = x_realloc(mf->objects, (n + 1) * sizeof(*mf->objects));
     596                 :          0 :         mf->n_objects++;
     597                 :          0 :         obj = &mf->objects[n];
     598                 :            : 
     599                 :          0 :         n = hashtable_count(included_files);
     600                 :          0 :         obj->n_file_info_indexes = n;
     601                 :          0 :         obj->file_info_indexes = x_malloc(n * sizeof(*obj->file_info_indexes));
     602                 :          0 :         add_file_info_indexes(obj->file_info_indexes, n, mf, included_files);
     603                 :          0 :         memcpy(obj->hash.hash, object_hash->hash, mf->hash_size);
     604                 :          0 :         obj->hash.size = object_hash->size;
     605                 :          0 : }
     606                 :            : 
     607                 :            : /*
     608                 :            :  * Try to get the object hash from a manifest file. Caller frees. Returns NULL
     609                 :            :  * on failure.
     610                 :            :  */
     611                 :            : struct file_hash *
     612                 :          0 : manifest_get(struct conf *conf, const char *manifest_path)
     613                 :            : {
     614                 :            :         int fd;
     615                 :          0 :         gzFile f = NULL;
     616                 :          0 :         struct manifest *mf = NULL;
     617                 :          0 :         struct hashtable *hashed_files = NULL; /* path --> struct file_hash */
     618                 :          0 :         struct hashtable *stated_files = NULL; /* path --> struct file_stats */
     619                 :            :         uint32_t i;
     620                 :          0 :         struct file_hash *fh = NULL;
     621                 :            : 
     622                 :          0 :         fd = open(manifest_path, O_RDONLY | O_BINARY);
     623         [ #  # ]:          0 :         if (fd == -1) {
     624                 :            :                 /* Cache miss. */
     625                 :          0 :                 cc_log("No such manifest file");
     626                 :          0 :                 goto out;
     627                 :            :         }
     628                 :          0 :         f = gzdopen(fd, "rb");
     629         [ #  # ]:          0 :         if (!f) {
     630                 :          0 :                 close(fd);
     631                 :          0 :                 cc_log("Failed to gzdopen manifest file");
     632                 :          0 :                 goto out;
     633                 :            :         }
     634                 :          0 :         mf = read_manifest(f);
     635         [ #  # ]:          0 :         if (!mf) {
     636                 :          0 :                 cc_log("Error reading manifest file");
     637                 :          0 :                 goto out;
     638                 :            :         }
     639                 :            : 
     640                 :          0 :         hashed_files = create_hashtable(1000, hash_from_string, strings_equal);
     641                 :          0 :         stated_files = create_hashtable(1000, hash_from_string, strings_equal);
     642                 :            : 
     643                 :            :         /* Check newest object first since it's a bit more likely to match. */
     644         [ #  # ]:          0 :         for (i = mf->n_objects; i > 0; i--) {
     645         [ #  # ]:          0 :                 if (verify_object(conf, mf, &mf->objects[i - 1],
     646                 :            :                                   stated_files, hashed_files)) {
     647                 :          0 :                         fh = x_malloc(sizeof(*fh));
     648                 :          0 :                         *fh = mf->objects[i - 1].hash;
     649                 :          0 :                         goto out;
     650                 :            :                 }
     651                 :            :         }
     652                 :            : 
     653                 :            : out:
     654         [ #  # ]:          0 :         if (hashed_files) {
     655                 :          0 :                 hashtable_destroy(hashed_files, 1);
     656                 :            :         }
     657         [ #  # ]:          0 :         if (stated_files) {
     658                 :          0 :                 hashtable_destroy(stated_files, 1);
     659                 :            :         }
     660         [ #  # ]:          0 :         if (f) {
     661                 :          0 :                 gzclose(f);
     662                 :            :         }
     663         [ #  # ]:          0 :         if (mf) {
     664                 :          0 :                 free_manifest(mf);
     665                 :            :         }
     666                 :          0 :         return fh;
     667                 :            : }
     668                 :            : 
     669                 :            : /*
     670                 :            :  * Put the object name into a manifest file given a set of included files.
     671                 :            :  * Returns true on success, otherwise false.
     672                 :            :  */
     673                 :            : bool
     674                 :          0 : manifest_put(const char *manifest_path, struct file_hash *object_hash,
     675                 :            :              struct hashtable *included_files)
     676                 :            : {
     677                 :          0 :         int ret = 0;
     678                 :            :         int fd1;
     679                 :            :         int fd2;
     680                 :          0 :         gzFile f2 = NULL;
     681                 :          0 :         struct manifest *mf = NULL;
     682                 :          0 :         char *tmp_file = NULL;
     683                 :            : 
     684                 :            :         /*
     685                 :            :          * We don't bother to acquire a lock when writing the manifest to disk. A
     686                 :            :          * race between two processes will only result in one lost entry, which is
     687                 :            :          * not a big deal, and it's also very unlikely.
     688                 :            :          */
     689                 :            : 
     690                 :          0 :         fd1 = open(manifest_path, O_RDONLY | O_BINARY);
     691         [ #  # ]:          0 :         if (fd1 == -1) {
     692                 :            :                 /* New file. */
     693                 :          0 :                 mf = create_empty_manifest();
     694                 :            :         } else {
     695                 :          0 :                 gzFile f1 = gzdopen(fd1, "rb");
     696         [ #  # ]:          0 :                 if (!f1) {
     697                 :          0 :                         cc_log("Failed to gzdopen manifest file");
     698                 :          0 :                         close(fd1);
     699                 :          0 :                         goto out;
     700                 :            :                 }
     701                 :          0 :                 mf = read_manifest(f1);
     702                 :          0 :                 gzclose(f1);
     703         [ #  # ]:          0 :                 if (!mf) {
     704                 :          0 :                         cc_log("Failed to read manifest file; deleting it");
     705                 :          0 :                         x_unlink(manifest_path);
     706                 :          0 :                         mf = create_empty_manifest();
     707                 :            :                 }
     708                 :            :         }
     709                 :            : 
     710         [ #  # ]:          0 :         if (mf->n_objects > MAX_MANIFEST_ENTRIES) {
     711                 :            :                 /*
     712                 :            :                  * Normally, there shouldn't be many object entries in the manifest since
     713                 :            :                  * new entries are added only if an include file has changed but not the
     714                 :            :                  * source file, and you typically change source files more often than
     715                 :            :                  * header files. However, it's certainly possible to imagine cases where
     716                 :            :                  * the manifest will grow large (for instance, a generated header file that
     717                 :            :                  * changes for every build), and this must be taken care of since
     718                 :            :                  * processing an ever growing manifest eventually will take too much time.
     719                 :            :                  * A good way of solving this would be to maintain the object entries in
     720                 :            :                  * LRU order and discarding the old ones. An easy way is to throw away all
     721                 :            :                  * entries when there are too many. Let's do that for now.
     722                 :            :                  */
     723                 :          0 :                 cc_log("More than %u entries in manifest file; discarding",
     724                 :            :                        MAX_MANIFEST_ENTRIES);
     725                 :          0 :                 free_manifest(mf);
     726                 :          0 :                 mf = create_empty_manifest();
     727         [ #  # ]:          0 :         } else if (mf->n_file_infos > MAX_MANIFEST_FILE_INFO_ENTRIES) {
     728                 :            :                 /* Rarely, file_info entries can grow large in pathological cases where
     729                 :            :                  * many included files change, but the main file does not. This also puts
     730                 :            :                  * an upper bound on the number of file_info entries.
     731                 :            :                  */
     732                 :          0 :                 cc_log("More than %u file_info entries in manifest file; discarding",
     733                 :            :                        MAX_MANIFEST_FILE_INFO_ENTRIES);
     734                 :          0 :                 free_manifest(mf);
     735                 :          0 :                 mf = create_empty_manifest();
     736                 :            :         }
     737                 :            : 
     738                 :          0 :         tmp_file = format("%s.tmp", manifest_path);
     739                 :          0 :         fd2 = create_tmp_fd(&tmp_file);
     740                 :          0 :         f2 = gzdopen(fd2, "wb");
     741         [ #  # ]:          0 :         if (!f2) {
     742                 :          0 :                 cc_log("Failed to gzdopen %s", tmp_file);
     743                 :          0 :                 goto out;
     744                 :            :         }
     745                 :            : 
     746                 :          0 :         add_object_entry(mf, object_hash, included_files);
     747         [ #  # ]:          0 :         if (write_manifest(f2, mf)) {
     748                 :          0 :                 gzclose(f2);
     749                 :          0 :                 f2 = NULL;
     750         [ #  # ]:          0 :                 if (x_rename(tmp_file, manifest_path) == 0) {
     751                 :          0 :                         ret = 1;
     752                 :            :                 } else {
     753                 :          0 :                         cc_log("Failed to rename %s to %s", tmp_file, manifest_path);
     754                 :          0 :                         goto out;
     755                 :            :                 }
     756                 :            :         } else {
     757                 :          0 :                 cc_log("Failed to write manifest file");
     758                 :            :                 goto out;
     759                 :            :         }
     760                 :            : 
     761                 :            : out:
     762         [ #  # ]:          0 :         if (mf) {
     763                 :          0 :                 free_manifest(mf);
     764                 :            :         }
     765         [ #  # ]:          0 :         if (tmp_file) {
     766                 :          0 :                 free(tmp_file);
     767                 :            :         }
     768         [ #  # ]:          0 :         if (f2) {
     769                 :          0 :                 gzclose(f2);
     770                 :            :         }
     771                 :          0 :         return ret;
     772                 :            : }
     773                 :            : 
     774                 :            : bool
     775                 :          0 : manifest_dump(const char *manifest_path, FILE *stream)
     776                 :            : {
     777                 :          0 :         struct manifest *mf = NULL;
     778                 :            :         int fd;
     779                 :          0 :         gzFile f = NULL;
     780                 :          0 :         bool ret = false;
     781                 :            :         unsigned i, j;
     782                 :            : 
     783                 :          0 :         fd = open(manifest_path, O_RDONLY | O_BINARY);
     784         [ #  # ]:          0 :         if (fd == -1) {
     785                 :          0 :                 fprintf(stderr, "No such manifest file: %s\n", manifest_path);
     786                 :          0 :                 goto out;
     787                 :            :         }
     788                 :          0 :         f = gzdopen(fd, "rb");
     789         [ #  # ]:          0 :         if (!f) {
     790                 :          0 :                 fprintf(stderr, "Failed to dzopen manifest file\n");
     791                 :          0 :                 close(fd);
     792                 :          0 :                 goto out;
     793                 :            :         }
     794                 :          0 :         mf = read_manifest(f);
     795         [ #  # ]:          0 :         if (!mf) {
     796                 :          0 :                 fprintf(stderr, "Error reading manifest file\n");
     797                 :          0 :                 goto out;
     798                 :            :         }
     799                 :            : 
     800                 :          0 :         fprintf(stream, "Magic: %c%c%c%c\n",
     801                 :          0 :                 (MAGIC >> 24) & 0xFF,
     802                 :          0 :                 (MAGIC >> 16) & 0xFF,
     803                 :          0 :                 (MAGIC >> 8) & 0xFF,
     804                 :            :                 MAGIC & 0xFF);
     805                 :          0 :         fprintf(stream, "Version: %u\n", mf->version);
     806                 :          0 :         fprintf(stream, "Hash size: %u\n", (unsigned)mf->hash_size);
     807                 :          0 :         fprintf(stream, "Reserved field: %u\n", (unsigned)mf->reserved);
     808                 :          0 :         fprintf(stream, "File paths (%u):\n", (unsigned)mf->n_files);
     809         [ #  # ]:          0 :         for (i = 0; i < mf->n_files; ++i) {
     810                 :          0 :                 fprintf(stream, "  %u: %s\n", i, mf->files[i]);
     811                 :            :         }
     812                 :          0 :         fprintf(stream, "File infos (%u):\n", (unsigned)mf->n_file_infos);
     813         [ #  # ]:          0 :         for (i = 0; i < mf->n_file_infos; ++i) {
     814                 :            :                 char *hash;
     815                 :          0 :                 fprintf(stream, "  %u:\n", i);
     816                 :          0 :                 fprintf(stream, "    Path index: %u\n", mf->file_infos[i].index);
     817                 :          0 :                 hash = format_hash_as_string(mf->file_infos[i].hash, -1);
     818                 :          0 :                 fprintf(stream, "    Hash: %s\n", hash);
     819                 :          0 :                 free(hash);
     820                 :          0 :                 fprintf(stream, "    Size: %u\n", mf->file_infos[i].size);
     821                 :          0 :                 fprintf(stream, "    Mtime: %lld\n", (long long)mf->file_infos[i].mtime);
     822                 :          0 :                 fprintf(stream, "    Ctime: %lld\n", (long long)mf->file_infos[i].ctime);
     823                 :            :         }
     824                 :          0 :         fprintf(stream, "Results (%u):\n", (unsigned)mf->n_objects);
     825         [ #  # ]:          0 :         for (i = 0; i < mf->n_objects; ++i) {
     826                 :            :                 char *hash;
     827                 :          0 :                 fprintf(stream, "  %u:\n", i);
     828                 :          0 :                 fprintf(stream, "    File hash indexes:");
     829         [ #  # ]:          0 :                 for (j = 0; j < mf->objects[i].n_file_info_indexes; ++j) {
     830                 :          0 :                         fprintf(stream, " %u", mf->objects[i].file_info_indexes[j]);
     831                 :            :                 }
     832                 :          0 :                 fprintf(stream, "\n");
     833                 :          0 :                 hash = format_hash_as_string(mf->objects[i].hash.hash, -1);
     834                 :          0 :                 fprintf(stream, "    Hash: %s\n", hash);
     835                 :          0 :                 free(hash);
     836                 :          0 :                 fprintf(stream, "    Size: %u\n", (unsigned)mf->objects[i].hash.size);
     837                 :            :         }
     838                 :            : 
     839                 :          0 :         ret = true;
     840                 :            : 
     841                 :            : out:
     842         [ #  # ]:          0 :         if (mf) {
     843                 :          0 :                 free_manifest(mf);
     844                 :            :         }
     845         [ #  # ]:          0 :         if (f) {
     846                 :          0 :                 gzclose(f);
     847                 :            :         }
     848                 :          0 :         return ret;
     849                 :            : }

Generated by: LCOV version 1.9