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 : 352 : static const Flags *lookup(
12 : : const memory *flags,
13 : : size_t index)
14 : : {
15 : 352 : const Flags *flag = m_data_ro(Flags,flags);
16 : :
17 [ + - - + ]: 352 : if(flag == NULL || index >= flags->length)
18 : : {
19 : 0 : return(NULL);
20 : : }
21 : :
22 : 352 : 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 : 243 : static void print_changes(
40 : : LOGMODES level,
41 : : const File *file)
42 : : {
43 : 243 : const unsigned int log_level = (unsigned int)(level | UNDECOR);
44 : :
45 [ + + + + ]: 243 : if(!((rational_logger_mode & VERBOSE) || config->watch_timestamps == true))
46 : : {
47 : 155 : return;
48 : : }
49 : :
50 : 88 : m_create(Flags,flags);
51 : :
52 : 88 : m_resize(flags,4);
53 : :
54 : 88 : Flags *flags_data_rewritable = m_data(Flags,flags);
55 : :
56 [ - + ]: 88 : if(flags_data_rewritable == NULL)
57 : : {
58 : 0 : m_del(flags);
59 : 0 : return;
60 : : }
61 : :
62 : 88 : flags_data_rewritable[0] = (Flags){SIZE_CHANGED,"lsize"};
63 : 88 : flags_data_rewritable[1] = (Flags){ALLOCATED_SIZE_CHANGED,"asize"};
64 : 88 : flags_data_rewritable[2] = (Flags){STATUS_CHANGED_TIME,"ctime"};
65 : 88 : flags_data_rewritable[3] = (Flags){MODIFICATION_TIME_CHANGED,"mtime"};
66 : :
67 : 88 : unsigned int flags_found = 0;
68 : :
69 : : /* Check each flag */
70 [ + + ]: 440 : for(size_t i = 0; i < flags->length; i++)
71 : : {
72 : 352 : const Flags *flag = lookup(flags,i);
73 : :
74 [ - + ]: 352 : if(flag == NULL)
75 : : {
76 : 0 : break;
77 : : }
78 : :
79 [ + + ]: 352 : 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 : 100 : slog(log_level," & ");
85 : : } else {
86 : 78 : 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 : 88 : 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 : 2583 : static void show_banners(
107 : : bool *first_iteration,
108 : : const TraversalSummary *summary)
109 : : {
110 [ - + ]: 2583 : if(first_iteration == NULL)
111 : : {
112 : 0 : return;
113 : : }
114 : :
115 : 2583 : bool show_traversal_started = false;
116 : 2583 : bool show_update_warning = false;
117 : 2583 : bool show_changes_will_be_reflected = false;
118 : 2583 : bool show_files_will_be_added = false;
119 : :
120 [ + + ]: 2583 : if(*first_iteration == true)
121 : : {
122 : 252 : *first_iteration = false;
123 : :
124 [ + - ]: 252 : if(summary->stats_only_pass == false)
125 : : {
126 : 252 : show_traversal_started = true;
127 : :
128 [ + + ]: 252 : if(config->db_contains_data == true)
129 : : {
130 [ + - ]: 111 : if(config->update == true)
131 : : {
132 : 111 : show_update_warning = true;
133 : : }
134 : :
135 : 111 : show_changes_will_be_reflected = true;
136 : :
137 : : } else {
138 : :
139 : 141 : show_files_will_be_added = true;
140 : : }
141 : : }
142 : : }
143 : :
144 [ + + ]: 2583 : if(show_update_warning == true)
145 : : {
146 : 111 : slog(EVERY,"Update mode enabled for DB %s\n",confstr(db_file_name));
147 : : }
148 : :
149 [ + + ]: 2583 : if(show_traversal_started == true)
150 : : {
151 : 252 : slog(EVERY,"File traversal started for %s\n",m_text(summary->root));
152 : : }
153 : :
154 [ + + ]: 2583 : if(show_changes_will_be_reflected == true)
155 : : {
156 : 111 : slog(EVERY,BOLD "Changes reported during this scan against the DB %s:" RESET "\n",confstr(db_file_name));
157 : : }
158 : :
159 [ + + ]: 2583 : if(show_files_will_be_added == true)
160 : : {
161 : 141 : 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 : 3329 : 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 [ + + ]: 3329 : if(rational_logger_mode & SILENT)
186 : : {
187 : 746 : return;
188 : : }
189 : :
190 [ + + - + ]: 2583 : if(respect_quiet == true && config->quiet_ignored == true)
191 : : {
192 : 0 : return;
193 : : }
194 : :
195 : 2583 : show_banners(first_iteration,summary);
196 : :
197 : 2583 : summary->at_least_one_file_was_shown = true;
198 : :
199 : 2583 : char *line_text = NULL;
200 : :
201 : : va_list args;
202 : 2583 : va_start(args,fmt);
203 : 2583 : int line_len = vasprintf(&line_text,fmt,args);
204 : 2583 : va_end(args);
205 : :
206 [ + - - + ]: 2583 : if(line_len < 0 || line_text == NULL)
207 : : {
208 : 0 : free(line_text);
209 : 0 : return;
210 : : }
211 : :
212 : 2583 : rational_logger(level,filename,(size_t)line,funcname,"%s",line_text);
213 : :
214 : 2583 : 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 : 2808 : void show_file(
229 : : const memory *relative_path,
230 : : bool *first_iteration,
231 : : TraversalSummary *summary,
232 : : const File *file)
233 : : {
234 : 2808 : const char *runtime_relative_path = m_text(relative_path);
235 : :
236 [ + + ]: 2808 : if(file->ignore == true)
237 : : {
238 [ + + ]: 70 : if(file->db->relative_path_was_in_db_before_processing == false)
239 : : {
240 : 64 : slog_show(EVERY|UNDECOR,true,first_iteration,summary,"ignore & do not add %s\n",runtime_relative_path);
241 : : } else {
242 : 6 : slog_show(EVERY|UNDECOR,true,first_iteration,summary,"ignored & do not update %s\n",runtime_relative_path);
243 : : }
244 : :
245 [ + + ]: 2738 : } 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 [ + + ]: 2735 : } else if(file->is_readable == false){
250 : :
251 : 4 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary,"inaccessible file %s\n",runtime_relative_path);
252 : :
253 [ + + ]: 2731 : } else if(file->db->relative_path_was_in_db_before_processing == false){
254 : :
255 : : /* Add new */
256 : :
257 [ + - ]: 2474 : if(file->new_db_record_inserted == true)
258 : : {
259 [ + + ]: 2474 : if(file->include == true)
260 : : {
261 : 24 : slog_show(EVERY|UNDECOR,true,first_iteration,summary,"add included %s\n",runtime_relative_path);
262 : :
263 [ + + ]: 2450 : } else if(file->locked_checksum_file == true){
264 : :
265 : 141 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"lock checksum %s\n",runtime_relative_path);
266 : :
267 [ + + ]: 2309 : } else if(file->zero_size_file == true){
268 : :
269 : 4 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"add as empty %s\n",runtime_relative_path);
270 : :
271 : : } else {
272 : :
273 : 2305 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"new %s\n",runtime_relative_path);
274 : : }
275 : : }
276 : :
277 : : } else {
278 : :
279 : : /* Update existing */
280 : :
281 [ + + ]: 257 : if(file->locked_checksum_mismatch == true)
282 : : {
283 : :
284 : 10 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary,RED "checksum locked & mismatch, data corrupted" RESET " %s\n",runtime_relative_path);
285 : :
286 [ + + ]: 247 : } else if(file->lock_checksum_violation == true){
287 : :
288 : 12 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary,RED "checksum locked, data corruption detected" RESET);
289 : :
290 : 12 : print_changes(EVERY|REMEMBER,file);
291 : :
292 : 12 : slog_show(EVERY|UNDECOR|REMEMBER,false,first_iteration,summary," %s\n",runtime_relative_path);
293 : :
294 [ + + + + ]: 235 : } else if(file->db_record_updated == true && file->include == true){
295 : :
296 : 6 : slog_show(EVERY|UNDECOR,true,first_iteration,summary,"update 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 [ + + + + ]: 229 : } else if(file->db_record_updated == true && file->zero_size_file == true){
303 : :
304 : 2 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"update 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 [ + + ]: 227 : } else if(file->rehash == true){
311 : :
312 [ + + ]: 111 : if(file->rehashing_from_the_beginning == true)
313 : : {
314 : 2 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"rehash from the beginning");
315 : :
316 : 2 : print_changes(EVERY,file);
317 : :
318 : 2 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
319 : :
320 [ + + ]: 109 : } else if(file->db->saved_offset > 0){
321 : :
322 : 4 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"continue to rehash from %s %s\n",bkbmbgbtbpbeb((const size_t)file->db->saved_offset,FULL_VIEW),runtime_relative_path);
323 : :
324 [ + + ]: 105 : } else if(file->locked_checksum_file == true){
325 : :
326 : 37 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"locked rehash ok");
327 : :
328 : 37 : print_changes(EVERY,file);
329 : :
330 : 37 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
331 : :
332 [ + - ]: 68 : } else if(file->db_record_updated == true){
333 : :
334 : 68 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"update & rehash");
335 : :
336 : 68 : print_changes(EVERY,file);
337 : :
338 : 68 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
339 : : }
340 : :
341 [ + - ]: 116 : } else if(file->db_record_updated == true){
342 : :
343 : 116 : slog_show(EVERY|UNDECOR,false,first_iteration,summary,"update stat");
344 : :
345 : 116 : print_changes(EVERY,file);
346 : :
347 : 116 : slog_show(EVERY|UNDECOR,false,first_iteration,summary," %s\n",runtime_relative_path);
348 : : }
349 : : }
350 : :
351 [ + + ]: 2808 : if(file->hash_interrupted == true)
352 : : {
353 : 2 : slog_show(EVERY,false,first_iteration,summary,"SHA512 checksum for the file %s has been gracefully interrupted at byte: %s\n",runtime_relative_path,bkbmbgbtbpbeb((size_t)file->checksum_offset,FULL_VIEW));
354 : : }
355 : 2808 : }
|