Branch data Line data Source code
1 : : #include "precizer.h"
2 : : #include <stdarg.h>
3 : :
4 : : /**
5 : : * @brief Retrieve a const pointer to a flag descriptor by index
6 : : *
7 : : * Uses the mem helper to obtain a typed read-only view of the Flags array and
8 : : * performs bounds checking. Returns NULL if the descriptor is missing, type
9 : : * verification fails, or the index is out of range.
10 : : */
11 : 368 : static const Flags *lookup(
12 : : const memory *flags,
13 : : size_t index)
14 : : {
15 : 368 : const Flags *flag = m_data_ro(Flags,flags);
16 : :
17 [ + - - + ]: 368 : if(flag == NULL || index >= flags->length)
18 : : {
19 : 0 : return(NULL);
20 : : }
21 : :
22 : 368 : return(&flag[index]);
23 : : }
24 : :
25 : : /**
26 : : * @brief Prints combinations of change flags for a file.
27 : : *
28 : : * This function evaluates a bitmask of change flags and prints the corresponding descriptions
29 : : * (e.g., "lsize", "asize", "ctime", "mtime") along with their metadata differences. The output uses
30 : : * the provided logger level, mirroring show_metadata behavior.
31 : : *
32 : : * Flag meanings:
33 : : * - lsize: logical file size (`st_size`)
34 : : * - asize: allocated size on disk (POSIX 512-byte blocks via blocks_to_bytes)
35 : : * - ctime: status change timestamp
36 : : * - mtime: content modification timestamp
37 : : *
38 : : */
39 : 317 : static void print_changes(
40 : : LOGMODES level,
41 : : const File *file)
42 : : {
43 : 317 : const unsigned int log_level = (unsigned int)(level | UNDECOR);
44 : :
45 [ + + + + ]: 317 : if(!((rational_logger_mode & VERBOSE) || config->watch_timestamps == true))
46 : : {
47 : 225 : return;
48 : : }
49 : :
50 : 92 : m_create(Flags,flags);
51 : :
52 : 92 : m_resize(flags,4);
53 : :
54 : 92 : Flags *flags_data_rewritable = m_data(Flags,flags);
55 : :
56 [ - + ]: 92 : if(flags_data_rewritable == NULL)
57 : : {
58 : 0 : m_del(flags);
59 : 0 : return;
60 : : }
61 : :
62 : 92 : flags_data_rewritable[0] = (Flags){SIZE_CHANGED,"lsize"};
63 : 92 : flags_data_rewritable[1] = (Flags){ALLOCATED_SIZE_CHANGED,"asize"};
64 : 92 : flags_data_rewritable[2] = (Flags){STATUS_CHANGED_TIME,"ctime"};
65 : 92 : flags_data_rewritable[3] = (Flags){MODIFICATION_TIME_CHANGED,"mtime"};
66 : :
67 : 92 : unsigned int flags_found = 0;
68 : :
69 : : /* Check each flag */
70 [ + + ]: 460 : for(size_t i = 0; i < flags->length; i++)
71 : : {
72 : 368 : const Flags *flag = lookup(flags,i);
73 : :
74 [ - + ]: 368 : if(flag == NULL)
75 : : {
76 : 0 : break;
77 : : }
78 : :
79 [ + + ]: 368 : if(file->db_record_vs_file_metadata_changes & flag->flag_value)
80 : : {
81 : : /* Add separator if not the first flag */
82 [ + + ]: 178 : if(flags_found > 0)
83 : : {
84 : 96 : slog(log_level," & ");
85 : : } else {
86 : 82 : slog(log_level,", changed: ");
87 : : }
88 : :
89 : 178 : slog(log_level,"%s",flag->flag_name);
90 : :
91 : 178 : show_metadata(level,flag->flag_value,&file->db->saved_stat,&file->stat);
92 : :
93 : 178 : flags_found++;
94 : : }
95 : : }
96 : :
97 : 92 : m_del(flags);
98 : : }
99 : :
100 : : /**
101 : : * @brief Show traversal banners once before the first visible log line of the current root
102 : : *
103 : : * The traversal-start banner includes `summary->root`, so multi-root output
104 : : * tells the user which configured root is currently producing messages
105 : : */
106 : 3721 : static void show_banners(
107 : : bool *first_iteration,
108 : : const TraversalSummary *summary)
109 : : {
110 [ - + ]: 3721 : if(first_iteration == NULL)
111 : : {
112 : 0 : return;
113 : : }
114 : :
115 : 3721 : bool show_traversal_started = false;
116 : 3721 : bool show_update_warning = false;
117 : 3721 : bool show_changes_will_be_reflected = false;
118 : 3721 : bool show_files_will_be_added = false;
119 : :
120 [ + + ]: 3721 : if(*first_iteration == true)
121 : : {
122 : 402 : *first_iteration = false;
123 : :
124 [ + - ]: 402 : if(summary->stats_only_pass == false)
125 : : {
126 : 402 : show_traversal_started = true;
127 : :
128 [ + + ]: 402 : if(config->db_contains_data == true)
129 : : {
130 [ + - ]: 139 : if(config->update == true)
131 : : {
132 : 139 : show_update_warning = true;
133 : : }
134 : :
135 : 139 : show_changes_will_be_reflected = true;
136 : :
137 : : } else {
138 : :
139 : 263 : show_files_will_be_added = true;
140 : : }
141 : : }
142 : : }
143 : :
144 [ + + ]: 3721 : if(show_update_warning == true)
145 : : {
146 : 139 : slog(EVERY,"Update mode enabled for DB %s\n",confstr(db_file_name));
147 : : }
148 : :
149 [ + + ]: 3721 : if(show_traversal_started == true)
150 : : {
151 : 402 : slog(EVERY,"File traversal started for %s\n",m_text(summary->root));
152 : : }
153 : :
154 [ + + ]: 3721 : if(show_changes_will_be_reflected == true)
155 : : {
156 : 139 : slog(EVERY,"@{bold}Changes reported during this scan against the DB %s:@{reset}\n",confstr(db_file_name));
157 : : }
158 : :
159 [ + + ]: 3721 : if(show_files_will_be_added == true)
160 : : {
161 : 263 : slog(EVERY,"@{bold}Items reported during this traversal against the DB %s:@{reset}\n",confstr(db_file_name));
162 : : }
163 : : }
164 : :
165 : : /**
166 : : * @brief Log one traversal line and emit root-aware banners when needed
167 : : *
168 : : * Uses the call-site metadata from the slog_show macro. The first visible line
169 : : * for a root also triggers the traversal banners and marks the current
170 : : * traversal summary as having produced visible output
171 : : *
172 : : */
173 : : __attribute__((format(printf,8,9)))
174 : 4491 : void slog_show_impl(
175 : : const char *filename,
176 : : const char *funcname,
177 : : int line,
178 : : const unsigned int level,
179 : : const bool respect_quiet,
180 : : bool *first_iteration,
181 : : TraversalSummary *summary,
182 : : const char *fmt,
183 : : ...)
184 : : {
185 [ + + ]: 4491 : if(rational_logger_mode & SILENT)
186 : : {
187 : 770 : return;
188 : : }
189 : :
190 [ + + + + ]: 3745 : if(respect_quiet == true && config->quiet_ignored == true)
191 : : {
192 : 24 : return;
193 : : }
194 : :
195 : 3721 : show_banners(first_iteration,summary);
196 : :
197 : 3721 : summary->at_least_one_file_was_shown = true;
198 : :
199 : 3721 : char *line_text = NULL;
200 : :
201 : : va_list args;
202 : 3721 : va_start(args,fmt);
203 : 3721 : int line_len = vasprintf(&line_text,fmt,args);
204 : 3721 : va_end(args);
205 : :
206 [ + - - + ]: 3721 : if(line_len < 0 || line_text == NULL)
207 : : {
208 : 0 : free(line_text);
209 : 0 : return;
210 : : }
211 : :
212 : 3721 : rational_logger(level,filename,(size_t)line,funcname,"%s",line_text);
213 : :
214 : 3721 : free(line_text);
215 : : }
216 : :
217 : : /**
218 : : * @brief Show the visible status line for one file path
219 : : *
220 : : * Prints the relative path together with the action or warning that applies to the file.
221 : : * Also triggers the shared traversal banner before the first visible file or directory line
222 : : *
223 : : * @param[in] relative_path Relative path descriptor being reported
224 : : * @param[in,out] first_iteration Banner sentinel for the first visible output line
225 : : * @param[in,out] summary Traversal state used by slog_show()
226 : : * @param[in] file Per-file state that determines which message is printed
227 : : */
228 : 3786 : void show_file(
229 : : const memory *relative_path,
230 : : bool *first_iteration,
231 : : TraversalSummary *summary,
232 : : const File *file)
233 : : {
234 : 3786 : const char *runtime_relative_path = m_text(relative_path);
235 : :
236 [ + + ]: 3786 : if(file->ignore == true)
237 : : {
238 [ + + ]: 118 : if(file->db->relative_path_was_in_db_before_processing == false)
239 : : {
240 : 110 : slog_show(EVERY|UNDECOR,true,first_iteration,summary,"ignored & not added %s\n",runtime_relative_path);
241 : : } else {
242 : 8 : slog_show(EVERY|UNDECOR,true,first_iteration,summary,"ignored & not updated %s\n",runtime_relative_path);
243 : : }
244 : :
245 [ + + ]: 3668 : } else if(file->read_error == true){
246 : :
247 : 3 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary,"error \"%s\" when reading %s\n",strerror(file->read_errno),runtime_relative_path);
248 : :
249 [ + + ]: 3665 : } else if(file->is_readable == false){
250 : :
251 : 6 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary,"inaccessible file %s\n",runtime_relative_path);
252 : :
253 [ + + ]: 3659 : } else if(file->db->relative_path_was_in_db_before_processing == false){
254 : :
255 : : /* Add new */
256 : :
257 [ + - ]: 3326 : if(file->new_db_record_inserted == true)
258 : : {
259 [ + + ]: 3326 : if(file->include == true)
260 : : {
261 : 30 : slog_show(EVERY|UNDECOR,true,first_iteration,summary,"added as included %s\n",runtime_relative_path);
262 : :
263 [ + + ]: 3296 : } else if(file->locked_checksum_file == true){
264 : :
265 : 141 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"locked checksum %s\n",runtime_relative_path);
266 : :
267 [ + + ]: 3155 : } else if(file->zero_size_file == true){
268 : :
269 : 4 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"added as empty %s\n",runtime_relative_path);
270 : :
271 : : } else {
272 : :
273 : 3151 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"added %s\n",runtime_relative_path);
274 : : }
275 : : }
276 : :
277 : : } else {
278 : :
279 : : /* Update existing */
280 : :
281 [ + + ]: 333 : if(file->locked_checksum_mismatch == true)
282 : : {
283 : :
284 : 12 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary,"@{red}checksum locked & mismatch, data corrupted@{reset} %s\n",runtime_relative_path);
285 : :
286 [ + + ]: 321 : } else if(file->lock_checksum_violation == true){
287 : :
288 : 16 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary,"@{red}checksum locked, data corruption detected@{reset}");
289 : :
290 : 16 : print_changes(EVERY|REMEMBER,file);
291 : :
292 : 16 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary," %s\n",runtime_relative_path);
293 : :
294 [ + + + + ]: 305 : } else if(file->db_record_updated == true && file->include == true){
295 : :
296 : 6 : slog_show(EVERY|UNDECOR,true,first_iteration,summary,"updated as included");
297 : :
298 : 6 : print_changes(EVERY,file);
299 : :
300 : 6 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
301 : :
302 [ + + + + ]: 299 : } else if(file->db_record_updated == true && file->zero_size_file == true){
303 : :
304 : 2 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"updated as empty");
305 : :
306 : 2 : print_changes(EVERY,file);
307 : :
308 : 2 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
309 : :
310 [ + + ]: 297 : } else if(file->rehash == true){
311 : :
312 [ + + ]: 267 : if(file->rehashing_from_the_beginning == true)
313 : : {
314 [ + - ]: 2 : if(file->hash_interrupted == false)
315 : : {
316 : 2 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"rehashed from the beginning");
317 : :
318 : 2 : print_changes(EVERY,file);
319 : :
320 : 2 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
321 : : }
322 : :
323 [ + + ]: 265 : } else if(file->db->saved_offset > 0){
324 : :
325 : 4 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"rehash resumed from %s %s\n",bkbmbgbtbpbeb((const size_t)file->db->saved_offset,FULL_VIEW),runtime_relative_path);
326 : :
327 [ + + ]: 261 : } else if(file->locked_checksum_file == true){
328 : :
329 [ + - ]: 47 : if(file->hash_interrupted == false)
330 : : {
331 : 47 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"rehashed & locked checksum matched");
332 : :
333 : 47 : print_changes(EVERY,file);
334 : :
335 : 47 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
336 : : }
337 : :
338 [ + - ]: 214 : } else if(file->db_record_updated == true){
339 : :
340 [ + - ]: 214 : if(file->hash_interrupted == false)
341 : : {
342 : 214 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"rehashed");
343 : :
344 : 214 : print_changes(EVERY,file);
345 : :
346 : 214 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
347 : : }
348 : : }
349 : :
350 [ + - ]: 30 : } else if(file->db_record_updated == true){
351 : :
352 : 30 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"updated metadata");
353 : :
354 : 30 : print_changes(EVERY,file);
355 : :
356 : 30 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
357 : : }
358 : : }
359 : :
360 [ + + ]: 3786 : if(file->hash_interrupted == true)
361 : : {
362 : 2 : slog_show(EVERY,false,first_iteration,summary,"hashing gracefully interrupted at %s %s\n",bkbmbgbtbpbeb((size_t)file->checksum_offset,FULL_VIEW),runtime_relative_path);
363 : : }
364 : 3786 : }
|