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 : : }
|