Branch data Line data Source code
1 : : #include "precizer.h"
2 : :
3 : : /**
4 : : * @brief Compare two FTS entries by filename
5 : : * @param first Pointer to first FTSENT structure
6 : : * @param second Pointer to second FTSENT structure
7 : : * @return Integer less than, equal to, or greater than zero if first is found,
8 : : * respectively, to be less than, to match, or be greater than second
9 : : */
10 : 5126 : static int compare_by_name(
11 : : const FTSENT **first,
12 : : const FTSENT **second)
13 : : {
14 : 5126 : return strcmp((*first)->fts_name,(*second)->fts_name);
15 : : }
16 : :
17 : 15880 : static Return match_include_ignore(
18 : : const char *relative_path,
19 : : bool *include,
20 : : bool *ignore,
21 : : bool *include_showed_once,
22 : : bool *ignore_showed_once)
23 : : {
24 : 15880 : *include = false;
25 : 15880 : *ignore = false;
26 : :
27 : 15880 : Include match_include_response = match_include_pattern(relative_path,include_showed_once);
28 : :
29 [ + + ]: 15880 : if(DO_NOT_INCLUDE == match_include_response)
30 : : {
31 : 15812 : Ignore match_ignore_response = match_ignore_pattern(relative_path,ignore_showed_once);
32 : :
33 [ + + ]: 15812 : if(IGNORE == match_ignore_response)
34 : : {
35 : 298 : *ignore = true;
36 : :
37 [ - + ]: 15514 : } else if(FAIL_REGEXP_IGNORE == match_ignore_response){
38 : :
39 : 0 : slog(ERROR,"Fail ignore REGEXP for a string: %s\n",relative_path);
40 : 0 : provide(FAILURE);
41 : : }
42 : :
43 [ - + ]: 68 : } else if(FAIL_REGEXP_INCLUDE == match_include_response){
44 : :
45 : 0 : slog(ERROR,"Fail include REGEXP for a string: %s\n",relative_path);
46 : 0 : provide(FAILURE);
47 : :
48 [ + - ]: 68 : } else if(INCLUDE == match_include_response){
49 : :
50 : 68 : *include = true;
51 : : }
52 : :
53 : 15880 : provide(SUCCESS);
54 : : }
55 : :
56 : : /**
57 : : * @brief Traverse configured paths and process files for one pass.
58 : : *
59 : : * Supports two modes controlled by summary->stats_only_pass:
60 : : * - true: collect counters and allocated size only;
61 : : * - false: hash files, update DB rows, and collect timing/hash metrics.
62 : : *
63 : : * @param summary Traversal state that is reset and populated by this call.
64 : : * @return SUCCESS, WARNING, or FAILURE.
65 : : */
66 : 602 : Return file_list(TraversalSummary *summary)
67 : : {
68 : : /* Status returned by this function through provide()
69 : : Default value assumes successful completion */
70 : 602 : Return status = SUCCESS;
71 : :
72 : : // Don't do anything
73 [ + + ]: 602 : if(config->compare == true)
74 : : {
75 : 140 : provide(status);
76 : : }
77 : :
78 [ + + + + ]: 462 : if(config->progress == false && summary->stats_only_pass == true)
79 : : {
80 : : // Don't do anything
81 : 172 : provide(status);
82 : : }
83 : :
84 : : // Flags that reflect the presence of any changes
85 : : // since the last research
86 : :
87 : : // Print traversal/update banners only once
88 : 290 : bool first_iteration = true;
89 : :
90 : : // Prevent duplicate --ignore info messages
91 : 290 : bool ignore_showed_once = false;
92 : :
93 : : // Prevent duplicate --include info messages
94 : 290 : bool include_showed_once = false;
95 : :
96 : : // Prevent duplicate lock-checksum info messages
97 : 290 : bool lock_checksum_showed_once = false;
98 : :
99 : : // Signals integrity issues for locked files
100 : 290 : bool lock_checksum_violation_detected = false;
101 : :
102 : 290 : FTS *file_systems = NULL;
103 : 290 : FTSENT *p = NULL;
104 : :
105 : 290 : int fts_options = FTS_PHYSICAL;
106 : :
107 [ + + ]: 290 : if(config->start_device_only == true)
108 : : {
109 : 2 : fts_options |= FTS_XDEV;
110 : : }
111 : :
112 : : // Reset traversal counters and timing for this pass.
113 : 290 : summary->count_dirs = 0;
114 : :
115 : : // Number of regular files seen in this pass.
116 : 290 : summary->count_files = 0;
117 : :
118 : : // Number of symlinks seen in this pass.
119 : 290 : summary->count_symlnks = 0;
120 : :
121 : : // Sum of allocated bytes for encountered files.
122 : 290 : summary->total_allocated_bytes = 0;
123 : :
124 : : // Sum of bytes hashed by SHA512 during this pass.
125 : 290 : summary->total_hashed_bytes = 0;
126 : :
127 : : // Track whether any output was produced
128 : 290 : summary->at_least_one_file_was_shown = false;
129 : :
130 : : // Sum of per-file hashing elapsed time in nanoseconds.
131 : 290 : summary->total_hashing_elapsed_ns = 0LL;
132 : :
133 [ - + ]: 290 : if((file_systems = fts_open(config->paths,fts_options,compare_by_name)) == NULL)
134 : : {
135 : 0 : slog(ERROR,"fts_open() error\n");
136 : 0 : provide(FAILURE);
137 : : }
138 : :
139 : : /*
140 : : * Determine the absolute path prefix.
141 : : * We are only interested in relative paths in the database.
142 : : * To obtain a relative path, trim the prefix from the absolute path.
143 : : */
144 : 290 : char *runtime_root = NULL;
145 : : #if 0 // Old multiPATH solution
146 : : /**
147 : : * Index of the path prefix
148 : : * All full runtime paths are stored in the table "paths".
149 : : * A real path can be retrieved due to its index ID
150 : : */
151 : : sqlite3_int64 runtime_root_index = -1;
152 : : #endif
153 : :
154 : : // Limit recursion to the depth determined in config->maxdepth
155 [ + + ]: 290 : if(config->maxdepth > -1)
156 : : {
157 : 4 : slog(EVERY,"Recursion depth limited to: %d\n",config->maxdepth);
158 : : }
159 : :
160 [ + + + - ]: 290 : if(summary->stats_only_pass == true && config->progress == true)
161 : : {
162 : 59 : slog(EVERY,"File system traversal initiated to calculate file count and storage usage\n");
163 : : }
164 : :
165 : 290 : bool continue_the_loop = true;
166 : :
167 : : // Allocate space for a memory structure
168 : 290 : create(unsigned char,file_buffer);
169 : :
170 [ + + ]: 290 : if(summary->stats_only_pass == false)
171 : : {
172 : 231 : status = resize(file_buffer,file_buffer_memory());
173 : :
174 [ - + ]: 231 : if(SUCCESS != status)
175 : : {
176 : 0 : provide(status);
177 : : }
178 : : }
179 : :
180 : : #ifdef TESTITALL_TEST_HOOKS
181 [ + + ]: 290 : if(summary->stats_only_pass == false)
182 : : {
183 : 231 : signal_wait_at_point(1U);
184 : : }
185 : : #endif
186 : :
187 [ + + + - ]: 30188 : while((p = fts_read(file_systems)) != NULL && continue_the_loop == true)
188 : : {
189 : : /* Interrupt the loop smoothly */
190 : : /* Interrupt when Ctrl+C */
191 [ + + ]: 29902 : if(global_interrupt_flag == true)
192 : : {
193 : 4 : break;
194 : : }
195 : :
196 : : /* Get absolute path prefix from FTSENT structure and current runtime path */
197 [ + + ]: 29898 : if(p->fts_level == FTS_ROOTLEVEL)
198 : : {
199 : 574 : size_t new_size = (size_t)(p->fts_pathlen + 1) * sizeof(char);
200 : :
201 : : // All below run once per new path prefix
202 : 574 : char *tmp = (char *)realloc(runtime_root,new_size);
203 : :
204 [ - + ]: 574 : if(NULL == tmp)
205 : : {
206 : 0 : report("Memory allocation failed, requested size: %zu bytes",new_size);
207 : 0 : status = FAILURE;
208 : 0 : break;
209 : : } else {
210 : 574 : runtime_root = tmp;
211 : : }
212 : :
213 : : // Remember temporary string in long-lasting variable
214 : 574 : memcpy(runtime_root,p->fts_path,(size_t)p->fts_pathlen);
215 : :
216 [ + - ]: 574 : if(p->fts_pathlen > 0)
217 : : {
218 : 574 : runtime_root[p->fts_pathlen] = '\0';
219 : : }
220 : :
221 : : // Remove unnecessary trailing slash at the end of the directory path
222 : 574 : remove_trailing_slash(runtime_root);
223 : :
224 : : #if 0 // Old multiPATH solution
225 : : // If several paths were passed as arguments,
226 : : // then the counting of the path prefix index
227 : : // will start from zero
228 : : if(SUCCESS != (status = db_get_runtime_root_index(config,
229 : : runtime_root,
230 : : &runtime_root_index)))
231 : : {
232 : : continue_the_loop = false;
233 : : break;
234 : : }
235 : : #endif
236 : : }
237 : :
238 [ + + + + ]: 29898 : if(config->maxdepth > -1 && p->fts_level > config->maxdepth + 1)
239 : : {
240 [ + + ]: 42 : if(p->fts_info == FTS_D)
241 : : {
242 : 14 : (void)fts_set(file_systems,p,FTS_SKIP);
243 : : }
244 : :
245 : 42 : continue;
246 : : }
247 : :
248 [ + + + - : 29856 : switch(p->fts_info)
+ ]
249 : : {
250 : 12966 : case FTS_D:
251 : : {
252 : 12966 : const char *relative_path = extract_relative_path(p->fts_path,runtime_root);
253 : :
254 : : // Captures files explicitly skipped or forced by regexp filters
255 : : // Ignored with --ignore= or admitted with --include=
256 : 12966 : bool ignore = false;
257 : :
258 : : // Included with --include=
259 : 12966 : bool include = false;
260 : :
261 : 12966 : status = match_include_ignore(relative_path,
262 : : &include,
263 : : &ignore,
264 : : &include_showed_once,
265 : : &ignore_showed_once);
266 : :
267 [ - + ]: 12966 : if(SUCCESS != status)
268 : : {
269 : 0 : continue_the_loop = false;
270 : 0 : break;
271 : : }
272 : :
273 [ + + ]: 12966 : if(summary->stats_only_pass == false)
274 : : {
275 : 10104 : directory_show(relative_path,
276 : : &first_iteration,
277 : : summary,
278 : : ignore,
279 : : include);
280 : : }
281 : :
282 [ + + ]: 12966 : if(ignore == true)
283 : : {
284 : : /*
285 : : * Use FTS_SKIP for an ignored directory only when no --include patterns were provided
286 : : * If any --include is present, keep traversing even ignored directories
287 : : * At this point only the directory path itself was checked, not paths under it
288 : : * Otherwise FTS_SKIP would cut off child files that should be brought back by --include
289 : : */
290 [ + + ]: 208 : if(config->include_specified == false)
291 : : {
292 : 22 : (void)fts_set(file_systems,p,FTS_SKIP);
293 : : }
294 : 208 : break;
295 : : }
296 : :
297 [ + + ]: 12758 : if(summary->stats_only_pass == false)
298 : : {
299 : : // Check access and skip subtrees that are not readable.
300 : 9988 : status = verify_directory_access(file_systems,
301 : : p,
302 : : runtime_root,
303 : : &first_iteration,
304 : : summary);
305 : : }
306 : :
307 [ - + ]: 12758 : if(SUCCESS != status)
308 : : {
309 : 0 : continue_the_loop = false;
310 : 0 : break;
311 : : }
312 : :
313 : 12758 : summary->count_dirs++;
314 : 12758 : break;
315 : : }
316 : 3686 : case FTS_F:
317 : : {
318 : 3686 : CmpctStat stat = {0};
319 : :
320 : 3686 : (void)stat_copy(p->fts_statp,&stat);
321 : :
322 : 3686 : summary->total_allocated_bytes += blocks_to_bytes(stat.st_blocks);
323 : 3686 : summary->count_files++;
324 : :
325 [ + + ]: 3686 : if(summary->stats_only_pass == true)
326 : : {
327 : 772 : continue;
328 : : }
329 : :
330 : : /* Write all columns from DB row to the structure DBrow
331 : : and clean the structure to prevent reuse */
332 : 2914 : DBrow _dbrow = {0};
333 : 2914 : DBrow *dbrow = &_dbrow;
334 : :
335 : 2914 : const char *relative_path = extract_relative_path(p->fts_path,runtime_root);
336 : :
337 : : /* Get all file's metadata from the database */
338 : : #if 0 // Old multiPATH solution
339 : : run(db_read_file_data_from(dbrow,&runtime_root_index,relative_path));
340 : : #else
341 [ + - - + ]: 2914 : run(db_read_file_data_from(dbrow,relative_path));
342 : : #endif
343 : :
344 [ - + ]: 2914 : if(SUCCESS != status)
345 : : {
346 : 0 : continue_the_loop = false;
347 : 2914 : break;
348 : : }
349 : :
350 : 2914 : const bool path_known = dbrow->relative_path_was_in_db_before_processing == true;
351 : :
352 : 2914 : const bool has_saved_offset = dbrow->saved_offset > 0;
353 : :
354 : : // Validate whether logical size, allocated blocks, and ctime/mtime
355 : : // changed since the previous scan.
356 : : // Default value is:
357 : 2914 : Changed db_record_vs_file_metadata_changes = NOT_EQUAL;
358 : :
359 : : // Tracks whether the current relative path existed in DB before current file processing
360 [ + + ]: 2914 : if(path_known == true)
361 : : {
362 : : // Validate whether logical size, allocated blocks, and ctime/mtime
363 : : // changed since the previous scan.
364 : 1280 : db_record_vs_file_metadata_changes = file_compare_metadata_equivalence(&(dbrow->saved_stat),&stat);
365 : : }
366 : :
367 : 2914 : bool file_metadata_identical = db_record_vs_file_metadata_changes == IDENTICAL;
368 : :
369 : : // Flag that marks files matched by the checksum lock pattern
370 : 2914 : bool locked_checksum_file = false;
371 : :
372 : 2914 : LockChecksum lock_checksum_response = match_checksum_lock_pattern(relative_path,&lock_checksum_showed_once);
373 : :
374 [ - + ]: 2914 : if(FAIL_REGEXP_LOCK_CHECKSUM == lock_checksum_response)
375 : : {
376 : 0 : slog(ERROR,"Fail lock-checksum REGEXP for a string: %s\n",relative_path);
377 : 0 : status = FAILURE;
378 : 0 : continue_the_loop = false;
379 : 0 : break;
380 [ + + ]: 2914 : } else if(LOCK_CHECKSUM == lock_checksum_response){
381 : 152 : locked_checksum_file = true;
382 : : }
383 : :
384 : : // Indicates that the checksum-locked file has already been fully hashed and recorded
385 : 2914 : bool lock_checksum_ready = locked_checksum_file == true
386 [ + + ]: 152 : && path_known == true
387 [ + + + - ]: 3066 : && has_saved_offset == false;
388 : :
389 : : // Captures files explicitly skipped or forced by regexp filters
390 : : // Ignored with --ignore= or admitted with --include=
391 : 2914 : bool ignore = false;
392 : :
393 : : // Included with --include=
394 : 2914 : bool include = false;
395 : :
396 : 2914 : status = match_include_ignore(relative_path,
397 : : &include,
398 : : &ignore,
399 : : &include_showed_once,
400 : : &ignore_showed_once);
401 : :
402 [ - + ]: 2914 : if(SUCCESS != status)
403 : : {
404 : 0 : continue_the_loop = false;
405 : 0 : break;
406 : : }
407 : :
408 : : // Ensure checksum-locked files are tracked even if matched by ignore pattern
409 [ + + - + : 2914 : if(ignore == true && locked_checksum_file == true && path_known == false)
- - ]
410 : : {
411 : 0 : ignore = false;
412 : : }
413 : :
414 : : // Determine read access for non-ignored paths
415 : 2914 : FileAccessStatus access_status = FILE_ACCESS_DENIED;
416 : 2914 : bool is_readable = false;
417 : :
418 : : /* Check file access */
419 [ + + ]: 2914 : if(ignore == false)
420 : : {
421 : 2824 : access_status = file_check_access(p->fts_path,(size_t)p->fts_pathlen,R_OK);
422 : :
423 [ - + ]: 2824 : if(access_status == FILE_ACCESS_ERROR)
424 : : {
425 : 0 : status = FAILURE;
426 : 0 : continue_the_loop = false;
427 : 0 : break;
428 : : }
429 : :
430 : 2824 : is_readable = (access_status == FILE_ACCESS_ALLOWED);
431 : : }
432 : :
433 : : // Used to skip files whose metadata and checksum are already up to date
434 : 2914 : bool unchanged_and_complete = path_known == true
435 [ + + ]: 1280 : && file_metadata_identical == true
436 [ + + + + ]: 4194 : && has_saved_offset == false;
437 : :
438 [ + + + + : 2914 : if(unchanged_and_complete == true && !(config->rehash_locked == true && lock_checksum_ready == true))
+ + ]
439 : : {
440 : : // Relative path already in DB and doesn't require any change
441 : : break;
442 : : }
443 : :
444 : : // Derived flags to qualify the type of metadata change
445 : 1878 : bool size_changed = (db_record_vs_file_metadata_changes & SIZE_CHANGED) != 0;
446 : :
447 : 1878 : bool timestamps_changed = (db_record_vs_file_metadata_changes & (STATUS_CHANGED_TIME | MODIFICATION_TIME_CHANGED)) != 0;
448 : :
449 : 1878 : bool timestamps_only_changed = path_known == true
450 [ + + ]: 244 : && file_metadata_identical == false
451 [ + + ]: 222 : && config->watch_timestamps == false
452 [ + + ]: 168 : && size_changed == false
453 [ + + + - ]: 2122 : && has_saved_offset == false;
454 : :
455 : : // Decision whether to rehash the file contents using
456 : : // the SHA512 algorithm. Defaults to rehash.
457 : 1878 : bool rehash = true;
458 : :
459 [ + + ]: 1878 : if(timestamps_only_changed == true)
460 : : {
461 : : // ctime/mtime changed only: update DB without rehash
462 : 124 : rehash = false;
463 : : }
464 : :
465 [ + + + + ]: 1878 : if(lock_checksum_ready == true && config->rehash_locked == true)
466 : : {
467 : 30 : rehash = true;
468 : : }
469 : :
470 : 1878 : sqlite3_int64 offset = 0; // Offset bytes
471 : 1878 : SHA512_Context mdContext = {0};
472 : :
473 : : /* For a file which had been changed before creation
474 : : of its checksum has been already finished */
475 : 1878 : bool rehashing_from_the_beginning = false;
476 : :
477 : : // Can we resume hashing from a previous partial state?
478 : 1878 : bool can_resume_partial_hash = has_saved_offset == true
479 [ + + + + ]: 1878 : && file_metadata_identical == true;
480 : :
481 : : // Indicates that a previous partial hash is now invalid and must restart
482 : 1878 : bool partial_hash_invalidated = has_saved_offset == true
483 [ + + + + ]: 1878 : && file_metadata_identical == false;
484 : :
485 [ + + ]: 1878 : if(can_resume_partial_hash == true)
486 : : {
487 : : // Continue hashing
488 : 2 : offset = dbrow->saved_offset;
489 : 2 : memcpy(&mdContext,&(dbrow->saved_mdContext),sizeof(SHA512_Context));
490 : :
491 [ + + ]: 1876 : } else if(partial_hash_invalidated == true){
492 : : /* The SHA512 hashing of the file had not been
493 : : finished previously and the file has been changed */
494 : 2 : rehashing_from_the_beginning = true;
495 : : }
496 : :
497 : : // Marks zero-length files to avoid unnecessary hashing
498 : 1878 : bool zero_size_file = false;
499 : :
500 : : /**
501 : : * Indicates files that cannot be read/seeks (e.g. sysfs)
502 : : *
503 : : * On some special file systems (such as /sys, which has
504 : : * the SYSFS_MAGIC constant == 0x62656572), standard
505 : : * file operations like fopen, fseek, and lseek
506 : : * cannot be used for reading and seeking.
507 : : * While information about the file itself will be
508 : : * recorded in the primary database, due to the
509 : : * nature of such files, their hash sum is never
510 : : * read and is stored as NULL
511 : : */
512 : 1878 : bool wrong_file_type = false;
513 : :
514 : : // Read error reported by sha512sum for this path
515 : 1878 : bool read_error = false;
516 : : // errno snapshot from the read error (valid when read_error is true).
517 : 1878 : int read_errno = 0;
518 : :
519 [ + + ]: 1878 : if(stat.st_size == 0)
520 : : {
521 : 4 : zero_size_file = true;
522 : 4 : rehash = false;
523 : : }
524 : :
525 : : // Locked checksum files must not diverge once sealed
526 : 1878 : bool lock_checksum_violation = lock_checksum_ready == true
527 [ + + + + ]: 1910 : && (size_changed == true
528 [ + + ]: 32 : || (config->watch_timestamps == true
529 [ + + ]: 20 : && config->rehash_locked == false
530 [ + - ]: 2 : && timestamps_changed == true));
531 : :
532 : : // Timestamps drift on a locked file may be ignored depending on config
533 : 1878 : bool locked_timestamp_drift_only = lock_checksum_ready == true
534 [ + + ]: 42 : && config->watch_timestamps == false
535 [ + + ]: 22 : && config->rehash_locked == false
536 [ + - ]: 10 : && timestamps_changed == true
537 [ + + + + ]: 1920 : && size_changed == false;
538 : :
539 [ + + ]: 1878 : if(locked_timestamp_drift_only == true)
540 : : {
541 : 2 : break;
542 : : }
543 : :
544 : : // Buffer for current file SHA512 digest
545 : 1876 : unsigned char sha512[SHA512_DIGEST_LENGTH] = {0};
546 : :
547 : 1876 : bool hash_failed = false;
548 : 1876 : bool hash_interrupted = false;
549 : 1876 : bool locked_checksum_mismatch = false; // Detects corruption when rehashing locked files
550 : :
551 : 1876 : bool should_process = is_readable == true
552 [ + - ]: 1804 : && ignore == false
553 [ + + + + ]: 3680 : && lock_checksum_violation == false;
554 : :
555 [ + + ]: 1876 : if(should_process == true)
556 : : {
557 [ + + ]: 1792 : if(rehash == true)
558 : : {
559 [ + - ]: 1672 : if(SUCCESS == status)
560 : : {
561 : 1672 : status = sha512sum(p->fts_path,
562 : 1672 : (size_t)p->fts_pathlen,
563 : : file_buffer,
564 : : sha512,
565 : : &offset,
566 : : summary,
567 : : &mdContext,
568 : : #ifdef TESTITALL_TEST_HOOKS
569 : : stat.st_size,
570 : : #endif
571 : : &read_error,
572 : : &read_errno,
573 : : &wrong_file_type);
574 : : }
575 : :
576 [ + - ]: 1672 : if(TRIUMPH & status)
577 : : {
578 : : /* If the sha512sum has been interrupted smoothly when Ctrl+C */
579 [ + + + - ]: 1672 : if(offset > 0 && global_interrupt_flag == true)
580 : : {
581 : 2 : hash_interrupted = true;
582 : : }
583 : :
584 : : } else {
585 : 0 : continue_the_loop = false;
586 : 0 : hash_failed = true;
587 : : }
588 : :
589 : : } else {
590 : 120 : memcpy(&sha512,&(dbrow->sha512),sizeof(sha512));
591 : : }
592 : :
593 [ + - ]: 1792 : if(hash_failed == false
594 [ + + ]: 1792 : && read_error == false
595 [ + + ]: 1791 : && config->rehash_locked == true
596 [ + - ]: 28 : && lock_checksum_ready == true
597 [ + - ]: 28 : && rehash == true
598 [ + - ]: 28 : && (TRIUMPH & status)
599 [ + - ]: 28 : && wrong_file_type == false
600 [ + - ]: 28 : && zero_size_file == false
601 [ + - ]: 28 : && offset == 0)
602 : : {
603 [ + + ]: 28 : if(memcmp(sha512,dbrow->sha512,SHA512_DIGEST_LENGTH) != 0)
604 : : {
605 : 6 : locked_checksum_mismatch = true;
606 : : }
607 : : }
608 : : }
609 : :
610 : 1876 : bool db_record_inserted = false;
611 : 1876 : bool db_record_updated = false;
612 : 1876 : bool show_log = false;
613 : :
614 [ + + ]: 1876 : if(is_readable != true
615 [ + - ]: 1804 : || ignore == true
616 [ + + ]: 1804 : || lock_checksum_violation == true
617 [ + - ]: 1792 : || hash_failed == true
618 [ + + ]: 1792 : || read_error == true
619 [ + + ]: 1791 : || locked_checksum_mismatch == true)
620 : : {
621 : 91 : show_log = true;
622 : :
623 : : /* When a checksum-locked file changed;
624 : : blocks rehash/DB update and flags corruption */
625 [ + + + + : 91 : if((lock_checksum_violation == true || locked_checksum_mismatch == true) && read_error == false)
+ - ]
626 : : {
627 : 18 : lock_checksum_violation_detected = true;
628 : : }
629 : :
630 [ + + ]: 1785 : } else if(path_known == true){
631 : : /* Update in DB */
632 : :
633 : 432 : bool allow_locked_update = lock_checksum_violation == false
634 [ + - + + ]: 238 : && (locked_checksum_file == false
635 [ - + ]: 22 : || config->rehash_locked == true
636 [ # # ]: 0 : || has_saved_offset == true);
637 : :
638 : 216 : bool should_update_db = path_known == true
639 [ + - ]: 216 : && allow_locked_update == true
640 [ + - + - ]: 648 : && (offset > dbrow->saved_offset
641 [ + + - + ]: 216 : || (has_saved_offset == true && offset == 0)
642 [ + + ]: 212 : || file_metadata_identical == false);
643 : :
644 [ + + ]: 216 : if(should_update_db == true)
645 : : {
646 : : /* Update record in DB */
647 [ + - ]: 198 : if(TRIUMPH & status)
648 : : {
649 : 198 : status = db_update_the_record_by_id(&(dbrow->ID),
650 : : &offset,
651 : : sha512,
652 : : &stat,
653 : : &mdContext,
654 : : &zero_size_file,
655 : : &wrong_file_type);
656 : :
657 [ - + ]: 198 : if((TRIUMPH & status) == 0)
658 : : {
659 : 0 : continue_the_loop = false;
660 : 0 : break;
661 : : }
662 : 198 : db_record_updated = true;
663 : : }
664 : : }
665 : :
666 : 216 : show_log = true;
667 : : } else {
668 : :
669 : : /* Insert into DB */
670 [ + - ]: 1569 : if(TRIUMPH & status)
671 : : {
672 : : #if 0 // Old multiPATH solution
673 : : status = db_insert_the_record(&runtime_root_index,
674 : : relative_path,
675 : : &offset,
676 : : sha512,
677 : : &stat,
678 : : &mdContext,
679 : : &zero_size_file,
680 : : &wrong_file_type);
681 : : #else
682 : 1569 : status = db_insert_the_record(relative_path,
683 : : &offset,
684 : : sha512,
685 : : &stat,
686 : : &mdContext,
687 : : &zero_size_file,
688 : : &wrong_file_type);
689 : : #endif
690 : :
691 [ - + ]: 1569 : if((TRIUMPH & status) == 0)
692 : : {
693 : 0 : continue_the_loop = false;
694 : 0 : break;
695 : : }
696 : 1569 : db_record_inserted = true;
697 : : }
698 : :
699 : 1569 : show_log = true;
700 : : }
701 : :
702 [ + - ]: 1876 : if(show_log == true)
703 : : {
704 : : // Print out of a file name and its changes
705 : 1876 : show_file(dbrow,
706 : : relative_path,
707 : : &stat,
708 : : &first_iteration,
709 : : summary,
710 : : db_record_vs_file_metadata_changes,
711 : : rehashing_from_the_beginning,
712 : : ignore,
713 : : include,
714 : : locked_checksum_file,
715 : : lock_checksum_violation,
716 : : locked_checksum_mismatch,
717 : : hash_interrupted,
718 : : offset,
719 : : rehash,
720 : : is_readable,
721 : : zero_size_file,
722 : : db_record_inserted,
723 : : db_record_updated,
724 : : read_error,
725 : : read_errno);
726 : : }
727 : :
728 : 1876 : break;
729 : : }
730 : 240 : case FTS_SL:
731 : 240 : summary->count_symlnks++;
732 : 240 : break;
733 : 0 : case FTS_DNR:
734 : : case FTS_ERR:
735 : : case FTS_NS:
736 : : {
737 [ # # ]: 0 : if(summary->stats_only_pass == true)
738 : : {
739 : 0 : break;
740 : : }
741 : :
742 : 0 : const char *relative_path = extract_relative_path(p->fts_path,runtime_root);
743 : :
744 [ # # ]: 0 : if(p->fts_info == FTS_DNR)
745 : : {
746 : 0 : slog_show(EVERY|UNDECOR|REMEMBER,false,&first_iteration,summary,"inaccessible directory %s\n",relative_path);
747 : :
748 [ # # ]: 0 : } else if(p->fts_info == FTS_NS){
749 : :
750 : 0 : slog_show(EVERY|UNDECOR|REMEMBER,false,&first_iteration,summary,"cannot stat \"%s\" when reading %s\n",strerror(p->fts_errno),relative_path);
751 : :
752 : : } else {
753 : :
754 : 0 : slog_show(EVERY|UNDECOR|REMEMBER,false,&first_iteration,summary,"fts error \"%s\" when reading %s\n",strerror(p->fts_errno),relative_path);
755 : : }
756 : :
757 : 0 : break;
758 : : }
759 : 12964 : default:
760 : 12964 : break;
761 : : }
762 : : }
763 : :
764 : 290 : del(file_buffer);
765 : :
766 : 290 : free(runtime_root);
767 : :
768 : 290 : fts_close(file_systems);
769 : :
770 : : // Print completion banner only when traversal emitted visible path-level lines.
771 : : // Print preflight totals only for the stats-only pass from main().
772 [ + + ]: 290 : if(SUCCESS == status)
773 : : {
774 [ + + ]: 288 : if(summary->at_least_one_file_was_shown == true)
775 : : {
776 : :
777 : 175 : slog(EVERY,"File traversal complete\n");
778 : : }
779 : :
780 [ + + ]: 288 : if(summary->stats_only_pass == true)
781 : : {
782 : 59 : show_statistics(summary);
783 : : }
784 : : }
785 : :
786 [ + + ]: 290 : if(lock_checksum_violation_detected == true)
787 : : {
788 : 14 : slog(EVERY,BOLD "Warning! Data corruption detected for checksum-locked file!" RESET "\n");
789 : :
790 [ + - ]: 14 : if(SUCCESS == status)
791 : : {
792 : 14 : status = WARNING;
793 : : }
794 : : }
795 : :
796 : 290 : provide(status);
797 : : }
|