1 |
commit: ede72d3cf08df8ffe7e59c4819d9a6c84ab1659f |
2 |
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
3 |
AuthorDate: Sat Apr 9 11:17:39 2022 +0000 |
4 |
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
5 |
CommitDate: Sat Apr 9 11:32:30 2022 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=ede72d3c |
7 |
|
8 |
qcheck: fix config-protect check, bug #837188 |
9 |
|
10 |
ensure we break out of the config-protect directories loop, instead of |
11 |
just skipping one directory, such that we don't produce bogus amounts of |
12 |
files, and also don't check despite we were told not to check (-P) |
13 |
|
14 |
Bug: https://bugs.gentoo.org/837188 |
15 |
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org> |
16 |
|
17 |
qcheck.c | 253 ++++++++++++++++++++++++++++------------------- |
18 |
tests/qcheck/list05.good | 2 +- |
19 |
tests/qcheck/list07.good | 2 +- |
20 |
3 files changed, 156 insertions(+), 101 deletions(-) |
21 |
|
22 |
diff --git a/qcheck.c b/qcheck.c |
23 |
index 9d9a86c..813c1f7 100644 |
24 |
--- a/qcheck.c |
25 |
+++ b/qcheck.c |
26 |
@@ -1,5 +1,5 @@ |
27 |
/* |
28 |
- * Copyright 2005-2020 Gentoo Foundation |
29 |
+ * Copyright 2005-2022 Gentoo Foundation |
30 |
* Distributed under the terms of the GNU General Public License v2 |
31 |
* |
32 |
* Copyright 2005-2010 Ned Ludd - <solar@g.o> |
33 |
@@ -74,30 +74,30 @@ static int |
34 |
qcheck_cb(tree_pkg_ctx *pkg_ctx, void *priv) |
35 |
{ |
36 |
struct qcheck_opt_state *state = priv; |
37 |
- FILE *fp_contents_update; |
38 |
- size_t num_files; |
39 |
- size_t num_files_ok; |
40 |
- size_t num_files_unknown; |
41 |
- size_t num_files_ignored; |
42 |
- struct stat st; |
43 |
- char *buffer; |
44 |
- char *line; |
45 |
- char *savep; |
46 |
- int cp_argc; |
47 |
- int cpm_argc; |
48 |
- char **cp_argv; |
49 |
- char **cpm_argv; |
50 |
- depend_atom *atom; |
51 |
- |
52 |
- fp_contents_update = NULL; |
53 |
- |
54 |
- /* Open contents */ |
55 |
+ struct stat st; |
56 |
+ depend_atom *atom; |
57 |
+ FILE *fp_contents_update = NULL; |
58 |
+ size_t num_files = 0; |
59 |
+ size_t num_files_ok = 0; |
60 |
+ size_t num_files_unknown = 0; |
61 |
+ size_t num_files_ignored = 0; |
62 |
+ char *buffer; |
63 |
+ char *line; |
64 |
+ char *savep; |
65 |
+ char *eprefix = NULL; |
66 |
+ size_t eprefix_len = 0; |
67 |
+ int cp_argc; |
68 |
+ int cpm_argc; |
69 |
+ char **cp_argv; |
70 |
+ char **cpm_argv; |
71 |
+ |
72 |
+ /* get CONTENTS from meta */ |
73 |
line = tree_pkg_meta_get(pkg_ctx, CONTENTS); |
74 |
if (line == NULL) |
75 |
return EXIT_FAILURE; |
76 |
|
77 |
atom = tree_get_atom(pkg_ctx, false); |
78 |
- num_files = num_files_ok = num_files_unknown = num_files_ignored = 0; |
79 |
+ |
80 |
qcprintf("%sing %s ...\n", |
81 |
(state->qc_update ? "Updat" : "Check"), |
82 |
atom_format(state->fmt, atom)); |
83 |
@@ -124,6 +124,10 @@ qcheck_cb(tree_pkg_ctx *pkg_ctx, void *priv) |
84 |
if (!state->chk_config_protect) { |
85 |
makeargv(config_protect, &cp_argc, &cp_argv); |
86 |
makeargv(config_protect_mask, &cpm_argc, &cpm_argv); |
87 |
+ |
88 |
+ eprefix = tree_pkg_meta_get(pkg_ctx, EPREFIX); |
89 |
+ if (eprefix != NULL) |
90 |
+ eprefix_len = strlen(eprefix); |
91 |
} |
92 |
|
93 |
buffer = NULL; |
94 |
@@ -136,8 +140,9 @@ qcheck_cb(tree_pkg_ctx *pkg_ctx, void *priv) |
95 |
if (!entry) |
96 |
continue; |
97 |
|
98 |
- /* run initial checks */ |
99 |
- ++num_files; |
100 |
+ num_files++; |
101 |
+ |
102 |
+ /* handle skips */ |
103 |
if (array_cnt(state->regex_arr)) { |
104 |
size_t n; |
105 |
regex_t *regex; |
106 |
@@ -145,15 +150,55 @@ qcheck_cb(tree_pkg_ctx *pkg_ctx, void *priv) |
107 |
if (!regexec(regex, entry->name, 0, NULL, 0)) |
108 |
break; |
109 |
if (n < array_cnt(state->regex_arr)) { |
110 |
- --num_files; |
111 |
- ++num_files_ignored; |
112 |
+ num_files--; |
113 |
+ num_files_ignored++; |
114 |
+ if (verbose) |
115 |
+ qcprintf(" %sSKIP%s %s: matches regex\n", |
116 |
+ YELLOW, NORM, entry->name); |
117 |
+ if (state->qc_update) |
118 |
+ fprintf(fp_contents_update, "%s\n", buffer); |
119 |
continue; |
120 |
} |
121 |
} |
122 |
+ |
123 |
+ /* handle CONFIG_PROTECT-ed files */ |
124 |
+ if (!state->chk_config_protect) { |
125 |
+ int i; |
126 |
+ char *p; |
127 |
+ |
128 |
+ /* compute path without EPREFIX */ |
129 |
+ p = entry->name; |
130 |
+ if (strlen(p) > eprefix_len) |
131 |
+ p += eprefix_len; |
132 |
+ |
133 |
+ /* if in CONFIG_PROTECT_MASK, handle like normal */ |
134 |
+ for (i = 1; i < cpm_argc; ++i) { |
135 |
+ if (strncmp(cpm_argv[i], p, strlen(cpm_argv[i])) == 0) |
136 |
+ break; |
137 |
+ } |
138 |
+ if (i == cpm_argc) { |
139 |
+ /* not explicitly unmasked, check if it's protected */ |
140 |
+ for (i = 1; i < cp_argc; ++i) { |
141 |
+ if (strncmp(cp_argv[i], p, strlen(cp_argv[i])) == 0) { |
142 |
+ num_files--; |
143 |
+ num_files_ignored++; |
144 |
+ if (verbose) |
145 |
+ qcprintf(" %sSKIP%s %s: protected via %s\n", |
146 |
+ YELLOW, NORM, entry->name, cp_argv[i]); |
147 |
+ if (state->qc_update) |
148 |
+ fprintf(fp_contents_update, "%s\n", buffer); |
149 |
+ break; |
150 |
+ } |
151 |
+ } |
152 |
+ if (i != cp_argc) |
153 |
+ continue; |
154 |
+ } |
155 |
+ } |
156 |
+ |
157 |
+ /* check file existence */ |
158 |
if (fstatat(pkg_ctx->cat_ctx->ctx->portroot_fd, entry->name + 1, |
159 |
- &st, AT_SYMLINK_NOFOLLOW)) |
160 |
+ &st, AT_SYMLINK_NOFOLLOW) != 0) |
161 |
{ |
162 |
- /* make sure file exists */ |
163 |
if (state->chk_afk) { |
164 |
if (errno == ENOENT) |
165 |
qcprintf(" %sAFK%s: %s\n", RED, NORM, entry->name); |
166 |
@@ -161,42 +206,27 @@ qcheck_cb(tree_pkg_ctx *pkg_ctx, void *priv) |
167 |
qcprintf(" %sERROR (%s)%s: %s\n", RED, |
168 |
strerror(errno), NORM, entry->name); |
169 |
} else { |
170 |
- --num_files; |
171 |
- ++num_files_ignored; |
172 |
+ num_files--; |
173 |
+ num_files_ignored++; |
174 |
+ if (verbose) |
175 |
+ qcprintf(" %sSKIP%s %s: %s\n", |
176 |
+ YELLOW, NORM, entry->name, strerror(errno)); |
177 |
if (state->qc_update) |
178 |
fprintf(fp_contents_update, "%s\n", buffer); |
179 |
} |
180 |
continue; |
181 |
} |
182 |
|
183 |
- /* Handle CONFIG_PROTECT-ed files */ |
184 |
- if (!state->chk_config_protect) { |
185 |
- int i; |
186 |
- /* If in CONFIG_PROTECT_MASK, handle like normal */ |
187 |
- for (i = 1; i < cpm_argc; ++i) |
188 |
- if (strncmp(cpm_argv[i], entry->name, strlen(cpm_argv[i])) == 0) |
189 |
- break; |
190 |
- if (i == cpm_argc) { |
191 |
- /* Not explicitly masked, so it's protected */ |
192 |
- for (i = 1; i < cp_argc; ++i) { |
193 |
- if (strncmp(cp_argv[i], entry->name, |
194 |
- strlen(cp_argv[i])) == 0) |
195 |
- { |
196 |
- num_files_ok++; |
197 |
- continue; |
198 |
- } |
199 |
- } |
200 |
- } |
201 |
- } |
202 |
- |
203 |
- /* For certain combinations of flags and filetypes, a file |
204 |
+ /* for certain combinations of flags and filetypes, a file |
205 |
* won't get checks and should be ignored */ |
206 |
if (!state->chk_mtime && entry->type == CONTENTS_SYM) { |
207 |
- --num_files; |
208 |
- ++num_files_ignored; |
209 |
+ num_files--; |
210 |
+ num_files_ignored++; |
211 |
+ if (verbose) |
212 |
+ qcprintf(" %sSKIP%s %s: symlink and no mtime check\n", |
213 |
+ YELLOW, NORM, entry->name); |
214 |
if (state->qc_update) |
215 |
fprintf(fp_contents_update, "%s\n", buffer); |
216 |
- |
217 |
continue; |
218 |
} |
219 |
|
220 |
@@ -207,31 +237,36 @@ qcheck_cb(tree_pkg_ctx *pkg_ctx, void *priv) |
221 |
* do check hashes, but only print mismatched digests as |
222 |
* 'ignored file'. */ |
223 |
if (entry->digest && S_ISREG(st.st_mode)) { |
224 |
+ char *f_digest; |
225 |
+ int hash_algo; |
226 |
+ |
227 |
/* Validate digest (handles MD5 / SHA1) |
228 |
* Digest-check 1/3: |
229 |
- * Should we check digests? */ |
230 |
- char *f_digest; |
231 |
- uint8_t hash_algo; |
232 |
+ * should we check digests? */ |
233 |
switch (strlen(entry->digest)) { |
234 |
- case 32: hash_algo = HASH_MD5; break; |
235 |
- case 40: hash_algo = HASH_SHA1; break; |
236 |
- default: hash_algo = 0; break; |
237 |
+ case 32: hash_algo = (int)HASH_MD5; break; |
238 |
+ case 40: hash_algo = (int)HASH_SHA1; break; |
239 |
+ default: hash_algo = 0; break; |
240 |
} |
241 |
|
242 |
- if (!hash_algo) { |
243 |
+ if (hash_algo == 0) { |
244 |
if (state->chk_hash) { |
245 |
qcprintf(" %sUNKNOWN DIGEST%s: '%s' for '%s'\n", |
246 |
- RED, NORM, entry->digest, entry->name); |
247 |
- ++num_files_unknown; |
248 |
+ RED, NORM, entry->digest, entry->name); |
249 |
+ num_files_unknown++; |
250 |
} else { |
251 |
- --num_files; |
252 |
- ++num_files_ignored; |
253 |
+ num_files--; |
254 |
+ num_files_ignored++; |
255 |
+ if (verbose) |
256 |
+ qcprintf(" %sSKIP%s %s: unknown digest\n", |
257 |
+ YELLOW, NORM, entry->name); |
258 |
if (state->qc_update) |
259 |
fprintf(fp_contents_update, "%s\n", buffer); |
260 |
} |
261 |
continue; |
262 |
} |
263 |
|
264 |
+ /* compute hash for file */ |
265 |
hash_cb_t hash_cb = |
266 |
state->undo_prelink ? hash_cb_prelink_undo : NULL; |
267 |
f_digest = hash_file_at_cb( |
268 |
@@ -239,45 +274,51 @@ qcheck_cb(tree_pkg_ctx *pkg_ctx, void *priv) |
269 |
entry->name + 1, hash_algo, hash_cb); |
270 |
|
271 |
/* Digest-check 2/3: |
272 |
- * Can we get a digest of the file? */ |
273 |
- if (!f_digest) { |
274 |
- ++num_files_unknown; |
275 |
+ * do we have digest of the file? */ |
276 |
+ if (f_digest == NULL) { |
277 |
+ num_files_unknown++; |
278 |
|
279 |
if (state->qc_update) |
280 |
fprintf(fp_contents_update, "%s\n", buffer); |
281 |
|
282 |
if (verbose) |
283 |
qcprintf(" %sPERM %4o%s: %s\n", |
284 |
- RED, (unsigned int)(st.st_mode & 07777), |
285 |
- NORM, entry->name); |
286 |
+ RED, (unsigned int)(st.st_mode & 07777), |
287 |
+ NORM, entry->name); |
288 |
|
289 |
continue; |
290 |
} |
291 |
|
292 |
/* Digest-check 3/3: |
293 |
- * Does the digest equal what portage recorded? */ |
294 |
+ * does the digest equal what portage recorded? */ |
295 |
if (strcmp(entry->digest, f_digest) != 0) { |
296 |
if (state->chk_hash) { |
297 |
+ const char *digest_disp; |
298 |
+ |
299 |
if (state->qc_update) |
300 |
- fprintf(fp_contents_update, "obj %s %s %"PRIu64"\n", |
301 |
- entry->name, f_digest, (uint64_t)st.st_mtime); |
302 |
+ fprintf(fp_contents_update, "obj %s %s %llu\n", |
303 |
+ entry->name, f_digest, |
304 |
+ (long long int)st.st_mtime); |
305 |
|
306 |
- const char *digest_disp; |
307 |
switch (hash_algo) { |
308 |
- case HASH_MD5: digest_disp = "MD5"; break; |
309 |
- case HASH_SHA1: digest_disp = "SHA1"; break; |
310 |
- default: digest_disp = "UNK"; break; |
311 |
+ case HASH_MD5: digest_disp = "MD5"; break; |
312 |
+ case HASH_SHA1: digest_disp = "SHA1"; break; |
313 |
+ default: digest_disp = "UNK"; break; |
314 |
} |
315 |
|
316 |
qcprintf(" %s%s-DIGEST%s: %s", |
317 |
- RED, digest_disp, NORM, entry->name); |
318 |
+ RED, digest_disp, NORM, entry->name); |
319 |
if (verbose) |
320 |
qcprintf(" (recorded '%s' != actual '%s')", |
321 |
- entry->digest, f_digest); |
322 |
+ entry->digest, f_digest); |
323 |
qcprintf("\n"); |
324 |
} else { |
325 |
- --num_files; |
326 |
- ++num_files_ignored; |
327 |
+ num_files--; |
328 |
+ num_files_ignored++; |
329 |
+ if (verbose) |
330 |
+ qcprintf(" %sSKIP%s %s: digest mismatch " |
331 |
+ "but check disabled\n", |
332 |
+ YELLOW, NORM, entry->name); |
333 |
if (state->qc_update) |
334 |
fprintf(fp_contents_update, "%s\n", buffer); |
335 |
} |
336 |
@@ -286,30 +327,44 @@ qcheck_cb(tree_pkg_ctx *pkg_ctx, void *priv) |
337 |
} |
338 |
} |
339 |
|
340 |
- /* Validate mtimes */ |
341 |
- if (state->chk_mtime && entry->mtime && entry->mtime != st.st_mtime) { |
342 |
- qcprintf(" %sMTIME%s: %s", RED, NORM, entry->name); |
343 |
- if (verbose) |
344 |
- qcprintf(" (recorded '%"PRIu64"' != actual '%"PRIu64"')", |
345 |
- (uint64_t)entry->mtime, (uint64_t)st.st_mtime); |
346 |
- qcprintf("\n"); |
347 |
- |
348 |
- /* Update mtime */ |
349 |
- if (state->qc_update) { |
350 |
- if (entry->type == CONTENTS_SYM) { |
351 |
- fprintf(fp_contents_update, "sym %s -> %s %"PRIu64"\n", |
352 |
- entry->name, entry->sym_target, |
353 |
- (uint64_t)st.st_mtime); |
354 |
- } else { |
355 |
- fprintf(fp_contents_update, "obj %s %s %"PRIu64"\n", |
356 |
- entry->name, entry->digest, (uint64_t)st.st_mtime); |
357 |
+ /* validate mtimes */ |
358 |
+ if (entry->mtime && entry->mtime != st.st_mtime) { |
359 |
+ if (state->chk_mtime) { |
360 |
+ qcprintf(" %sMTIME%s: %s", RED, NORM, entry->name); |
361 |
+ if (verbose) |
362 |
+ qcprintf(" (recorded '%llu' != actual '%llu')", |
363 |
+ (long long int)entry->mtime, |
364 |
+ (long long int)st.st_mtime); |
365 |
+ qcprintf("\n"); |
366 |
+ |
367 |
+ /* Update mtime */ |
368 |
+ if (state->qc_update) { |
369 |
+ if (entry->type == CONTENTS_SYM) { |
370 |
+ fprintf(fp_contents_update, "sym %s -> %s %llu\n", |
371 |
+ entry->name, entry->sym_target, |
372 |
+ (long long int)st.st_mtime); |
373 |
+ } else { |
374 |
+ fprintf(fp_contents_update, "obj %s %s %llu\n", |
375 |
+ entry->name, entry->digest, |
376 |
+ (long long int)st.st_mtime); |
377 |
+ } |
378 |
} |
379 |
- } |
380 |
|
381 |
- continue; |
382 |
+ continue; |
383 |
+ } else { |
384 |
+ num_files--; |
385 |
+ num_files_ignored++; |
386 |
+ if (verbose) |
387 |
+ qcprintf(" %sSKIP%s %s: mtime mismatch " |
388 |
+ "but check disabled\n", |
389 |
+ YELLOW, NORM, entry->name); |
390 |
+ if (state->qc_update) |
391 |
+ fprintf(fp_contents_update, "%s\n", buffer); |
392 |
+ continue; |
393 |
+ } |
394 |
} |
395 |
|
396 |
- /* Success! */ |
397 |
+ /* success! */ |
398 |
if (state->qc_update) |
399 |
fprintf(fp_contents_update, "%s\n", buffer); |
400 |
|
401 |
|
402 |
diff --git a/tests/qcheck/list05.good b/tests/qcheck/list05.good |
403 |
index 688c177..bb1e169 100644 |
404 |
--- a/tests/qcheck/list05.good |
405 |
+++ b/tests/qcheck/list05.good |
406 |
@@ -6,6 +6,6 @@ Checking a-b/pkg ... |
407 |
AFK: /missing-dir |
408 |
AFK: /missing-dir/missing-file |
409 |
AFK: /missing-dir/missing-sym |
410 |
- * 4 out of 11 files are good (2 files were ignored) |
411 |
+ * 3 out of 10 files are good (3 files were ignored) |
412 |
Checking virtual/pkg ... |
413 |
* 0 out of 0 files are good |
414 |
|
415 |
diff --git a/tests/qcheck/list07.good b/tests/qcheck/list07.good |
416 |
index 847b0b5..c80a073 100644 |
417 |
--- a/tests/qcheck/list07.good |
418 |
+++ b/tests/qcheck/list07.good |
419 |
@@ -1,4 +1,4 @@ |
420 |
Checking a-b/pkg ... |
421 |
- * 4 out of 4 files are good (9 files were ignored) |
422 |
+ * 3 out of 3 files are good (10 files were ignored) |
423 |
Checking virtual/pkg ... |
424 |
* 0 out of 0 files are good |