1 |
commit: 16e7280ec40c26f586f0db62f0d63960dd42a3d0 |
2 |
Author: Sam Besselink <sambesselink <AT> planet <DOT> nl> |
3 |
AuthorDate: Wed Oct 24 22:30:31 2018 +0000 |
4 |
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
5 |
CommitDate: Fri Dec 28 09:07:25 2018 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=16e7280e |
7 |
|
8 |
Refactor: use successive fail branches |
9 |
|
10 |
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org> |
11 |
|
12 |
qcheck.c | 231 ++++++++++++++++++++++++++++++++++----------------------------- |
13 |
1 file changed, 126 insertions(+), 105 deletions(-) |
14 |
|
15 |
diff --git a/qcheck.c b/qcheck.c |
16 |
index 8a83466..7b6e58c 100644 |
17 |
--- a/qcheck.c |
18 |
+++ b/qcheck.c |
19 |
@@ -50,8 +50,8 @@ struct qcheck_opt_state { |
20 |
static int |
21 |
qcheck_process_contents(q_vdb_pkg_ctx *pkg_ctx, struct qcheck_opt_state *state) |
22 |
{ |
23 |
- int fd; |
24 |
- FILE *fp, *fpx; |
25 |
+ int fd_contents; |
26 |
+ FILE *fp_contents, *fp_contents_update; |
27 |
const char *catname = pkg_ctx->cat_ctx->name; |
28 |
const char *pkgname = pkg_ctx->name; |
29 |
size_t num_files, num_files_ok, num_files_unknown, num_files_ignored; |
30 |
@@ -61,28 +61,30 @@ qcheck_process_contents(q_vdb_pkg_ctx *pkg_ctx, struct qcheck_opt_state *state) |
31 |
int cp_argc, cpm_argc; |
32 |
char **cp_argv, **cpm_argv; |
33 |
|
34 |
- fpx = NULL; |
35 |
+ fp_contents_update = NULL; |
36 |
|
37 |
- fd = q_vdb_pkg_openat(pkg_ctx, "CONTENTS", O_RDONLY|O_CLOEXEC, 0); |
38 |
- if (fd == -1) |
39 |
+ /* Open contents */ |
40 |
+ fd_contents = q_vdb_pkg_openat(pkg_ctx, "CONTENTS", O_RDONLY|O_CLOEXEC, 0); |
41 |
+ if (fd_contents == -1) |
42 |
return EXIT_SUCCESS; |
43 |
- if (fstat(fd, &cst)) { |
44 |
- close(fd); |
45 |
+ if (fstat(fd_contents, &cst)) { |
46 |
+ close(fd_contents); |
47 |
return EXIT_SUCCESS; |
48 |
} |
49 |
- if ((fp = fdopen(fd, "r")) == NULL) { |
50 |
- close(fd); |
51 |
+ if ((fp_contents = fdopen(fd_contents, "r")) == NULL) { |
52 |
+ close(fd_contents); |
53 |
return EXIT_SUCCESS; |
54 |
} |
55 |
|
56 |
+ /* Open contents_update, if needed */ |
57 |
num_files = num_files_ok = num_files_unknown = num_files_ignored = 0; |
58 |
qcprintf("%sing %s%s/%s%s ...\n", |
59 |
(state->qc_update ? "Updat" : "Check"), |
60 |
GREEN, catname, pkgname, NORM); |
61 |
if (state->qc_update) { |
62 |
- fpx = q_vdb_pkg_fopenat_rw(pkg_ctx, "CONTENTS~"); |
63 |
- if (fpx == NULL) { |
64 |
- fclose(fp); |
65 |
+ fp_contents_update = q_vdb_pkg_fopenat_rw(pkg_ctx, "CONTENTS~"); |
66 |
+ if (fp_contents_update == NULL) { |
67 |
+ fclose(fp_contents); |
68 |
warnp("unable to fopen(%s/%s, w)", pkgname, "CONTENTS~"); |
69 |
return EXIT_FAILURE; |
70 |
} |
71 |
@@ -94,21 +96,23 @@ qcheck_process_contents(q_vdb_pkg_ctx *pkg_ctx, struct qcheck_opt_state *state) |
72 |
} |
73 |
|
74 |
buffer = line = NULL; |
75 |
- while (getline(&line, &linelen, fp) != -1) { |
76 |
- contents_entry *e; |
77 |
+ while (getline(&line, &linelen, fp_contents) != -1) { |
78 |
+ contents_entry *entry; |
79 |
free(buffer); |
80 |
buffer = xstrdup(line); |
81 |
- e = contents_parse_line(line); |
82 |
- if (!e) |
83 |
+ |
84 |
+ entry = contents_parse_line(line); |
85 |
+ |
86 |
+ if (!entry) |
87 |
continue; |
88 |
|
89 |
- /* run our little checks */ |
90 |
+ /* run initial checks */ |
91 |
++num_files; |
92 |
if (array_cnt(state->regex_arr)) { |
93 |
size_t n; |
94 |
regex_t *regex; |
95 |
array_for_each(state->regex_arr, n, regex) |
96 |
- if (!regexec(regex, e->name, 0, NULL, 0)) |
97 |
+ if (!regexec(regex, entry->name, 0, NULL, 0)) |
98 |
break; |
99 |
if (n < array_cnt(state->regex_arr)) { |
100 |
--num_files; |
101 |
@@ -116,142 +120,159 @@ qcheck_process_contents(q_vdb_pkg_ctx *pkg_ctx, struct qcheck_opt_state *state) |
102 |
continue; |
103 |
} |
104 |
} |
105 |
- if (fstatat(pkg_ctx->cat_ctx->ctx->portroot_fd, e->name + 1, &st, AT_SYMLINK_NOFOLLOW)) { |
106 |
+ if (fstatat(pkg_ctx->cat_ctx->ctx->portroot_fd, entry->name + 1, &st, AT_SYMLINK_NOFOLLOW)) { |
107 |
/* make sure file exists */ |
108 |
if (state->chk_afk) { |
109 |
if (errno == ENOENT) |
110 |
- qcprintf(" %sAFK%s: %s\n", RED, NORM, e->name); |
111 |
+ qcprintf(" %sAFK%s: %s\n", RED, NORM, entry->name); |
112 |
else |
113 |
- qcprintf(" %sERROR (%s)%s: %s\n", RED, strerror(errno), NORM, e->name); |
114 |
+ qcprintf(" %sERROR (%s)%s: %s\n", RED, strerror(errno), NORM, entry->name); |
115 |
} else { |
116 |
--num_files; |
117 |
++num_files_ignored; |
118 |
if (state->qc_update) |
119 |
- fputs(buffer, fpx); |
120 |
+ fputs(buffer, fp_contents_update); |
121 |
} |
122 |
continue; |
123 |
} |
124 |
- if (e->digest && S_ISREG(st.st_mode)) { |
125 |
- if (!state->chk_config_protect) { |
126 |
- /* handle CONFIG_PROTECT-ed files */ |
127 |
- int i; |
128 |
- /* if it's in CONFIG_PROTECT_MASK, check it like normal */ |
129 |
- for (i = 1; i < cpm_argc; ++i) |
130 |
- if (strncmp(cpm_argv[i], e->name, strlen(cpm_argv[i])) == 0) |
131 |
- break; |
132 |
- if (i == cpm_argc) { |
133 |
- /* not explicitly masked, so if it's protected */ |
134 |
- for (i = 1; i < cp_argc; ++i) |
135 |
- if (strncmp(cp_argv[i], e->name, strlen(cp_argv[i])) == 0) |
136 |
- goto cfg_protected; |
137 |
- } |
138 |
+ |
139 |
+ /* Handle CONFIG_PROTECT-ed files */ |
140 |
+ if (!state->chk_config_protect) { |
141 |
+ int i; |
142 |
+ /* If in CONFIG_PROTECT_MASK, handle like normal */ |
143 |
+ for (i = 1; i < cpm_argc; ++i) |
144 |
+ if (strncmp(cpm_argv[i], entry->name, strlen(cpm_argv[i])) == 0) |
145 |
+ break; |
146 |
+ if (i == cpm_argc) { |
147 |
+ /* Not explicitly masked, so it's protected */ |
148 |
+ for (i = 1; i < cp_argc; ++i) |
149 |
+ if (strncmp(cp_argv[i], entry->name, strlen(cp_argv[i])) == 0) { |
150 |
+ num_files_ok++; |
151 |
+ continue; |
152 |
+ } |
153 |
} |
154 |
+ } |
155 |
+ |
156 |
+ /* For certain combinations of flags and filetypes, a file |
157 |
+ * won't get checks and should be ignored */ |
158 |
+ if (!state->chk_mtime && entry->type == CONTENTS_SYM) { |
159 |
+ --num_files; |
160 |
+ ++num_files_ignored; |
161 |
+ if (state->qc_update) |
162 |
+ fputs(buffer, fp_contents_update); |
163 |
|
164 |
- /* validate digest (handles MD5 / SHA1) */ |
165 |
+ continue; |
166 |
+ } |
167 |
+ |
168 |
+ /* Digest checks only work on regular files |
169 |
+ * Note: We don't check for state->chk_hash when entering |
170 |
+ * but rather in digest-check #3, because we only succeed |
171 |
+ * tests/qcheck/list04.good if when chk_hash is false, we |
172 |
+ * do check hashes, but only print mismatched digests as |
173 |
+ * 'ignored file'. */ |
174 |
+ if (entry->digest && S_ISREG(st.st_mode)) { |
175 |
+ /* Validate digest (handles MD5 / SHA1) |
176 |
+ * Digest-check 1/3: |
177 |
+ * Should we check digests? */ |
178 |
+ char *f_digest; |
179 |
uint8_t hash_algo; |
180 |
- char *hashed_file; |
181 |
- hash_cb_t hash_cb = state->undo_prelink ? hash_cb_prelink_undo : hash_cb_default; |
182 |
- switch (strlen(e->digest)) { |
183 |
+ switch (strlen(entry->digest)) { |
184 |
case 32: hash_algo = HASH_MD5; break; |
185 |
case 40: hash_algo = HASH_SHA1; break; |
186 |
default: hash_algo = 0; break; |
187 |
} |
188 |
+ |
189 |
if (!hash_algo) { |
190 |
if (state->chk_hash) { |
191 |
- qcprintf(" %sUNKNOWN DIGEST%s: '%s' for '%s'\n", RED, NORM, e->digest, e->name); |
192 |
+ qcprintf(" %sUNKNOWN DIGEST%s: '%s' for '%s'\n", RED, NORM, entry->digest, entry->name); |
193 |
++num_files_unknown; |
194 |
} else { |
195 |
--num_files; |
196 |
++num_files_ignored; |
197 |
if (state->qc_update) |
198 |
- fputs(buffer, fpx); |
199 |
+ fputs(buffer, fp_contents_update); |
200 |
} |
201 |
continue; |
202 |
} |
203 |
- hashed_file = (char *)hash_file_at_cb(pkg_ctx->cat_ctx->ctx->portroot_fd, e->name + 1, hash_algo, hash_cb); |
204 |
- if (!hashed_file) { |
205 |
+ |
206 |
+ hash_cb_t hash_cb = state->undo_prelink ? hash_cb_prelink_undo : hash_cb_default; |
207 |
+ f_digest = (char *)hash_file_at_cb(pkg_ctx->cat_ctx->ctx->portroot_fd, entry->name + 1, hash_algo, hash_cb); |
208 |
+ |
209 |
+ /* Digest-check 2/3: |
210 |
+ * Can we get a digest of the file? */ |
211 |
+ if (!f_digest) { |
212 |
++num_files_unknown; |
213 |
- free(hashed_file); |
214 |
- if (state->qc_update) { |
215 |
- fputs(buffer, fpx); |
216 |
- if (!verbose) |
217 |
- continue; |
218 |
- } |
219 |
- qcprintf(" %sPERM %4o%s: %s\n", RED, (unsigned int)(st.st_mode & 07777), NORM, e->name); |
220 |
+ free(f_digest); |
221 |
+ |
222 |
+ if (state->qc_update) |
223 |
+ fputs(buffer, fp_contents_update); |
224 |
+ |
225 |
+ if (verbose) |
226 |
+ qcprintf(" %sPERM %4o%s: %s\n", RED, (unsigned int)(st.st_mode & 07777), NORM, entry->name); |
227 |
+ |
228 |
continue; |
229 |
- } else if (strcmp(e->digest, hashed_file)) { |
230 |
+ } |
231 |
+ |
232 |
+ /* Digest-check 3/3: |
233 |
+ * Does the digest equal what portage recorded? */ |
234 |
+ if (strcmp(entry->digest, f_digest) != 0) { |
235 |
if (state->chk_hash) { |
236 |
- const char *digest_disp; |
237 |
if (state->qc_update) |
238 |
- fprintf(fpx, "obj %s %s %"PRIu64"\n", e->name, hashed_file, (uint64_t)st.st_mtime); |
239 |
+ fprintf(fp_contents_update, "obj %s %s %"PRIu64"\n", entry->name, f_digest, (uint64_t)st.st_mtime); |
240 |
+ |
241 |
+ const char *digest_disp; |
242 |
switch (hash_algo) { |
243 |
- case HASH_MD5: digest_disp = "MD5"; break; |
244 |
- case HASH_SHA1: digest_disp = "SHA1"; break; |
245 |
- default: digest_disp = "UNK"; break; |
246 |
+ case HASH_MD5: digest_disp = "MD5"; break; |
247 |
+ case HASH_SHA1: digest_disp = "SHA1"; break; |
248 |
+ default: digest_disp = "UNK"; break; |
249 |
} |
250 |
- qcprintf(" %s%s-DIGEST%s: %s", RED, digest_disp, NORM, e->name); |
251 |
+ |
252 |
+ qcprintf(" %s%s-DIGEST%s: %s", RED, digest_disp, NORM, entry->name); |
253 |
if (verbose) |
254 |
- qcprintf(" (recorded '%s' != actual '%s')", e->digest, hashed_file); |
255 |
+ qcprintf(" (recorded '%s' != actual '%s')", entry->digest, f_digest); |
256 |
qcprintf("\n"); |
257 |
} else { |
258 |
--num_files; |
259 |
++num_files_ignored; |
260 |
if (state->qc_update) |
261 |
- fputs(buffer, fpx); |
262 |
+ fputs(buffer, fp_contents_update); |
263 |
} |
264 |
- free(hashed_file); |
265 |
+ |
266 |
+ free(f_digest); |
267 |
continue; |
268 |
- } else if (e->mtime && e->mtime != st.st_mtime) { |
269 |
- if (state->chk_mtime) { |
270 |
- qcprintf(" %sMTIME%s: %s", RED, NORM, e->name); |
271 |
- if (verbose) |
272 |
- qcprintf(" (recorded '%"PRIu64"' != actual '%"PRIu64"')", (uint64_t)e->mtime, (uint64_t)st.st_mtime); |
273 |
- qcprintf("\n"); |
274 |
+ } |
275 |
|
276 |
- /* This can only be an obj, dir and sym have no digest */ |
277 |
- if (state->qc_update) |
278 |
- fprintf(fpx, "obj %s %s %"PRIu64"\n", e->name, e->digest, (uint64_t)st.st_mtime); |
279 |
+ free(f_digest); |
280 |
+ } |
281 |
+ |
282 |
+ /* Validate mtimes */ |
283 |
+ if (state->chk_mtime && entry->mtime && entry->mtime != st.st_mtime) { |
284 |
+ qcprintf(" %sMTIME%s: %s", RED, NORM, entry->name); |
285 |
+ if (verbose) |
286 |
+ qcprintf(" (recorded '%"PRIu64"' != actual '%"PRIu64"')", (uint64_t)entry->mtime, (uint64_t)st.st_mtime); |
287 |
+ qcprintf("\n"); |
288 |
+ |
289 |
+ /* Update mtime */ |
290 |
+ if (state->qc_update) { |
291 |
+ if (entry->type == CONTENTS_SYM) { |
292 |
+ fprintf(fp_contents_update, "sym %s -> %s %"PRIu64"\n", entry->name, entry->sym_target, (uint64_t)st.st_mtime); |
293 |
} else { |
294 |
- --num_files; |
295 |
- ++num_files_ignored; |
296 |
- if (state->qc_update) |
297 |
- fputs(buffer, fpx); |
298 |
+ fprintf(fp_contents_update, "obj %s %s %"PRIu64"\n", entry->name, entry->digest, (uint64_t)st.st_mtime); |
299 |
} |
300 |
- free(hashed_file); |
301 |
- continue; |
302 |
- } else { |
303 |
- if (state->qc_update) |
304 |
- fputs(buffer, fpx); |
305 |
- free(hashed_file); |
306 |
} |
307 |
- } else if (e->mtime && e->mtime != st.st_mtime) { |
308 |
- if (state->chk_mtime) { |
309 |
- qcprintf(" %sMTIME%s: %s", RED, NORM, e->name); |
310 |
- if (verbose) |
311 |
- qcprintf(" (recorded '%"PRIu64"' != actual '%"PRIu64"')", |
312 |
- (uint64_t)e->mtime, (uint64_t)st.st_mtime); |
313 |
- qcprintf("\n"); |
314 |
|
315 |
- /* This can only be a sym */ |
316 |
- if (state->qc_update) |
317 |
- fprintf(fpx, "sym %s -> %s %"PRIu64"\n", e->name, e->sym_target, (uint64_t)st.st_mtime); |
318 |
- } else { |
319 |
- --num_files; |
320 |
- ++num_files_ignored; |
321 |
- if (state->qc_update) |
322 |
- fputs(buffer, fpx); |
323 |
- } |
324 |
continue; |
325 |
- } else { |
326 |
- if (state->qc_update) |
327 |
- fputs(buffer, fpx); |
328 |
} |
329 |
- cfg_protected: |
330 |
- ++num_files_ok; |
331 |
+ |
332 |
+ /* Success! */ |
333 |
+ if (state->qc_update) |
334 |
+ fputs(buffer, fp_contents_update); |
335 |
+ |
336 |
+ num_files_ok++; |
337 |
} |
338 |
free(line); |
339 |
free(buffer); |
340 |
- fclose(fp); |
341 |
+ fclose(fp_contents); |
342 |
|
343 |
if (!state->chk_config_protect) { |
344 |
freeargv(cp_argc, cp_argv); |
345 |
@@ -259,13 +280,13 @@ qcheck_process_contents(q_vdb_pkg_ctx *pkg_ctx, struct qcheck_opt_state *state) |
346 |
} |
347 |
|
348 |
if (state->qc_update) { |
349 |
- if (fchown(fd, cst.st_uid, cst.st_gid)) { |
350 |
+ if (fchown(fd_contents, cst.st_uid, cst.st_gid)) { |
351 |
/* meh */; |
352 |
} |
353 |
- if (fchmod(fd, cst.st_mode)) { |
354 |
+ if (fchmod(fd_contents, cst.st_mode)) { |
355 |
/* meh */; |
356 |
} |
357 |
- fclose(fpx); |
358 |
+ fclose(fp_contents_update); |
359 |
if (renameat(pkg_ctx->fd, "CONTENTS~", pkg_ctx->fd, "CONTENTS")) |
360 |
unlinkat(pkg_ctx->fd, "CONTENTS~", 0); |
361 |
if (!verbose) |