/[gentoo-projects]/portage-utils/qcheck.c
Gentoo

Contents of /portage-utils/qcheck.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.57 - (show annotations) (download) (as text)
Sat Nov 10 00:08:46 2012 UTC (20 months, 2 weeks ago) by vapier
Branch: MAIN
Changes since 1.56: +3 -3 lines
File MIME type: text/x-csrc
qcheck: fix ROOT handling when hashing files

1 /*
2 * Copyright 2005-2011 Gentoo Foundation
3 * Distributed under the terms of the GNU General Public License v2
4 * $Header: /var/cvsroot/gentoo-projects/portage-utils/qcheck.c,v 1.56 2012/10/28 04:52:56 vapier Exp $
5 *
6 * Copyright 2005-2010 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2005-2011 Mike Frysinger - <vapier@gentoo.org>
8 */
9
10 #ifdef APPLET_qcheck
11
12 #define QCHECK_FLAGS "aes:uABHTp" COMMON_FLAGS
13 static struct option const qcheck_long_opts[] = {
14 {"all", no_argument, NULL, 'a'},
15 {"exact", no_argument, NULL, 'e'},
16 {"skip", a_argument, NULL, 's'},
17 {"update", no_argument, NULL, 'u'},
18 {"noafk", no_argument, NULL, 'A'},
19 {"badonly", no_argument, NULL, 'B'},
20 {"nohash", no_argument, NULL, 'H'},
21 {"nomtime", no_argument, NULL, 'T'},
22 {"skip-protected", no_argument, NULL, 128},
23 {"prelink", no_argument, NULL, 'p'},
24 COMMON_LONG_OPTS
25 };
26 static const char * const qcheck_opts_help[] = {
27 "List all packages",
28 "Exact match (only CAT/PN or PN without PV)",
29 "Ignore files matching the regular expression <arg>",
30 "Update missing files, chksum and mtimes for packages",
31 "Ignore missing files",
32 "Only print pkgs containing bad files",
33 "Ignore differing/unknown file chksums",
34 "Ignore differing file mtimes",
35 "Ignore files in CONFIG_PROTECT-ed paths",
36 "Undo prelink when calculating checksums",
37 COMMON_OPTS_HELP
38 };
39 static const char qcheck_rcsid[] = "$Id: qcheck.c,v 1.56 2012/10/28 04:52:56 vapier Exp $";
40 #define qcheck_usage(ret) usage(ret, QCHECK_FLAGS, qcheck_long_opts, qcheck_opts_help, lookup_applet_idx("qcheck"))
41
42 #define qcprintf(fmt, args...) if (!state->bad_only) printf(_(fmt), ## args)
43
44 struct qcheck_opt_state {
45 int argc;
46 char **argv;
47 array_t *regex_arr;
48 bool bad_only;
49 bool search_all;
50 bool qc_update;
51 bool chk_afk;
52 bool chk_hash;
53 bool chk_mtime;
54 bool chk_config_protect;
55 bool undo_prelink;
56 bool exact;
57 };
58
59 static int qcheck_process_contents(q_vdb_pkg_ctx *pkg_ctx, struct qcheck_opt_state *state)
60 {
61 int fd;
62 FILE *fp, *fpx;
63 const char *catname = pkg_ctx->cat_ctx->name;
64 const char *pkgname = pkg_ctx->name;
65 size_t num_files, num_files_ok, num_files_unknown, num_files_ignored;
66 char *buffer, *line;
67 size_t linelen;
68 struct stat st, cst;
69 int cp_argc, cpm_argc;
70 char **cp_argv, **cpm_argv;
71
72 fpx = NULL;
73
74 fd = q_vdb_pkg_openat(pkg_ctx, "CONTENTS", O_RDONLY);
75 if (fd == -1)
76 return EXIT_SUCCESS;
77 if (fstat(fd, &cst)) {
78 close(fd);
79 return EXIT_SUCCESS;
80 }
81 if ((fp = fdopen(fd, "r")) == NULL) {
82 close(fd);
83 return EXIT_SUCCESS;
84 }
85
86 num_files = num_files_ok = num_files_unknown = num_files_ignored = 0;
87 qcprintf("%sing %s%s/%s%s ...\n",
88 (state->qc_update ? "Updat" : "Check"),
89 GREEN, catname, pkgname, NORM);
90 if (state->qc_update) {
91 fpx = q_vdb_pkg_fopenat_rw(pkg_ctx, "CONTENTS~");
92 if (fpx == NULL) {
93 fclose(fp);
94 warnp("unable to fopen(%s/%s, w)", pkgname, "CONTENTS~");
95 return EXIT_FAILURE;
96 }
97 }
98
99 if (!state->chk_config_protect) {
100 makeargv(config_protect, &cp_argc, &cp_argv);
101 makeargv(config_protect_mask, &cpm_argc, &cpm_argv);
102 }
103
104 buffer = line = NULL;
105 while (getline(&line, &linelen, fp) != -1) {
106 contents_entry *e;
107 free(buffer);
108 buffer = xstrdup(line);
109 e = contents_parse_line(line);
110 if (!e)
111 continue;
112
113 /* run our little checks */
114 ++num_files;
115 if (array_cnt(state->regex_arr)) {
116 size_t n;
117 regex_t *regex;
118 array_for_each(state->regex_arr, n, regex)
119 if (!regexec(regex, e->name, 0, NULL, 0))
120 break;
121 if (n < array_cnt(state->regex_arr)) {
122 --num_files;
123 ++num_files_ignored;
124 continue;
125 }
126 }
127 if (fstatat(pkg_ctx->cat_ctx->ctx->portroot_fd, e->name + 1, &st, AT_SYMLINK_NOFOLLOW)) {
128 /* make sure file exists */
129 if (state->chk_afk) {
130 qcprintf(" %sAFK%s: %s\n", RED, NORM, e->name);
131 } else {
132 --num_files;
133 ++num_files_ignored;
134 if (state->qc_update)
135 fputs(buffer, fpx);
136 }
137 continue;
138 }
139 if (e->digest && S_ISREG(st.st_mode)) {
140 if (!state->chk_config_protect) {
141 /* handle CONFIG_PROTECT-ed files */
142 int i;
143 /* if it's in CONFIG_PROTECT_MASK, check it like normal */
144 for (i = 1; i < cpm_argc; ++i)
145 if (strncmp(cpm_argv[i], e->name, strlen(cpm_argv[i])) == 0)
146 break;
147 if (i == cpm_argc) {
148 /* not explicitly masked, so if it's protected */
149 for (i = 1; i < cp_argc; ++i)
150 if (strncmp(cp_argv[i], e->name, strlen(cp_argv[i])) == 0)
151 goto cfg_protected;
152 }
153 }
154
155 /* validate digest (handles MD5 / SHA1) */
156 uint8_t hash_algo;
157 char *hashed_file;
158 hash_cb_t hash_cb = state->undo_prelink ? hash_cb_prelink_undo : hash_cb_default;
159 switch (strlen(e->digest)) {
160 case 32: hash_algo = HASH_MD5; break;
161 case 40: hash_algo = HASH_SHA1; break;
162 default: hash_algo = 0; break;
163 }
164 if (!hash_algo) {
165 if (state->chk_hash) {
166 qcprintf(" %sUNKNOWN DIGEST%s: '%s' for '%s'\n", RED, NORM, e->digest, e->name);
167 ++num_files_unknown;
168 } else {
169 --num_files;
170 ++num_files_ignored;
171 if (state->qc_update)
172 fputs(buffer, fpx);
173 }
174 continue;
175 }
176 hashed_file = (char *)hash_file_at_cb(pkg_ctx->cat_ctx->ctx->portroot_fd, e->name + 1, hash_algo, hash_cb);
177 if (!hashed_file) {
178 ++num_files_unknown;
179 free(hashed_file);
180 if (state->qc_update) {
181 fputs(buffer, fpx);
182 if (!verbose)
183 continue;
184 }
185 qcprintf(" %sPERM %4o%s: %s\n", RED, (unsigned int)(st.st_mode & 07777), NORM, e->name);
186 continue;
187 } else if (strcmp(e->digest, hashed_file)) {
188 if (state->chk_hash) {
189 const char *digest_disp;
190 if (state->qc_update)
191 fprintf(fpx, "obj %s %s %lu\n", e->name, hashed_file, st.st_mtime);
192 switch (hash_algo) {
193 case HASH_MD5: digest_disp = "MD5"; break;
194 case HASH_SHA1: digest_disp = "SHA1"; break;
195 default: digest_disp = "UNK"; break;
196 }
197 qcprintf(" %s%s-DIGEST%s: %s", RED, digest_disp, NORM, e->name);
198 if (verbose)
199 qcprintf(" (recorded '%s' != actual '%s')", e->digest, hashed_file);
200 qcprintf("\n");
201 } else {
202 --num_files;
203 ++num_files_ignored;
204 if (state->qc_update)
205 fputs(buffer, fpx);
206 }
207 free(hashed_file);
208 continue;
209 } else if (e->mtime && e->mtime != st.st_mtime) {
210 if (state->chk_mtime) {
211 qcprintf(" %sMTIME%s: %s", RED, NORM, e->name);
212 if (verbose)
213 qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime);
214 qcprintf("\n");
215
216 /* This can only be an obj, dir and sym have no digest */
217 if (state->qc_update)
218 fprintf(fpx, "obj %s %s %lu\n", e->name, e->digest, st.st_mtime);
219 } else {
220 --num_files;
221 ++num_files_ignored;
222 if (state->qc_update)
223 fputs(buffer, fpx);
224 }
225 free(hashed_file);
226 continue;
227 } else {
228 if (state->qc_update)
229 fputs(buffer, fpx);
230 free(hashed_file);
231 }
232 } else if (e->mtime && e->mtime != st.st_mtime) {
233 if (state->chk_mtime) {
234 qcprintf(" %sMTIME%s: %s", RED, NORM, e->name);
235 if (verbose)
236 qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime);
237 qcprintf("\n");
238
239 /* This can only be a sym */
240 if (state->qc_update)
241 fprintf(fpx, "sym %s -> %s %lu\n", e->name, e->sym_target, st.st_mtime);
242 } else {
243 --num_files;
244 ++num_files_ignored;
245 if (state->qc_update)
246 fputs(buffer, fpx);
247 }
248 continue;
249 } else {
250 if (state->qc_update)
251 fputs(buffer, fpx);
252 }
253 cfg_protected:
254 ++num_files_ok;
255 }
256 free(line);
257 free(buffer);
258 fclose(fp);
259
260 if (!state->chk_config_protect) {
261 freeargv(cp_argc, cp_argv);
262 freeargv(cpm_argc, cpm_argv);
263 }
264
265 if (state->qc_update) {
266 if (fchown(fd, cst.st_uid, cst.st_gid)) {
267 /* meh */;
268 }
269 if (fchmod(fd, cst.st_mode)) {
270 /* meh */;
271 }
272 fclose(fpx);
273 if (renameat(pkg_ctx->fd, "CONTENTS~", pkg_ctx->fd, "CONTENTS"))
274 unlinkat(pkg_ctx->fd, "CONTENTS~", 0);
275 if (!verbose)
276 return EXIT_SUCCESS;
277 }
278 if (state->bad_only && num_files_ok != num_files) {
279 if (verbose)
280 printf("%s/%s\n", catname, pkgname);
281 else {
282 depend_atom *atom = NULL;
283 char *buf;
284 xasprintf(&buf, "%s/%s", catname, pkgname);
285 if ((atom = atom_explode(buf)) != NULL) {
286 printf("%s/%s\n", catname, atom->PN);
287 atom_implode(atom);
288 } else {
289 printf("%s/%s\n", catname, pkgname);
290 }
291 free(buf);
292 }
293 }
294 qcprintf(" %2$s*%1$s %3$s%4$zu%1$s out of %3$s%5$zu%1$s file%6$s are good",
295 NORM, BOLD, BLUE, num_files_ok, num_files,
296 (num_files > 1 ? "s" : ""));
297 if (num_files_unknown)
298 qcprintf(" (Unable to digest %2$s%3$zu%1$s file%4$s)",
299 NORM, BLUE, num_files_unknown,
300 (num_files_unknown > 1 ? "s" : ""));
301 if (num_files_ignored)
302 qcprintf(" (%2$s%3$zu%1$s file%4$s ignored)",
303 NORM, BLUE, num_files_ignored,
304 (num_files_ignored > 1 ? "s were" : " was"));
305 qcprintf("\n");
306
307 if (num_files_ok != num_files)
308 return EXIT_FAILURE;
309 else
310 return EXIT_SUCCESS;
311 }
312
313 _q_static int qcheck_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv)
314 {
315 struct qcheck_opt_state *state = priv;
316 const char *catname = pkg_ctx->cat_ctx->name;
317 const char *pkgname = pkg_ctx->name;
318
319 /* see if this cat/pkg is requested */
320 if (!state->search_all) {
321 char *buf = NULL;
322 int i;
323
324 for (i = optind; i < state->argc; ++i) {
325 free(buf);
326 xasprintf(&buf, "%s/%s", catname, pkgname);
327 if (!state->exact) {
328 if (rematch(state->argv[i], buf, REG_EXTENDED) == 0)
329 break;
330 if (rematch(state->argv[i], pkgname, REG_EXTENDED) == 0)
331 break;
332 } else {
333 depend_atom *atom;
334 char swap[_Q_PATH_MAX];
335 if ((atom = atom_explode(buf)) == NULL) {
336 warn("invalid atom %s", buf);
337 continue;
338 }
339 snprintf(swap, sizeof(swap), "%s/%s", atom->CATEGORY, atom->PN);
340 atom_implode(atom);
341 if (strcmp(state->argv[i], swap) == 0 ||
342 strcmp(state->argv[i], buf) == 0)
343 break;
344 if (strcmp(state->argv[i], strstr(swap, "/") + 1) == 0 ||
345 strcmp(state->argv[i], strstr(buf, "/") + 1) == 0)
346 break;
347 }
348 }
349 free(buf);
350
351 if (i == state->argc)
352 return 0;
353 }
354
355 return qcheck_process_contents(pkg_ctx, priv);
356 }
357
358 int qcheck_main(int argc, char **argv)
359 {
360 int i, ret;
361 DECLARE_ARRAY(regex_arr);
362 struct qcheck_opt_state state = {
363 .argc = argc,
364 .argv = argv,
365 .regex_arr = regex_arr,
366 .bad_only = false,
367 .search_all = false,
368 .qc_update = false,
369 .chk_afk = true,
370 .chk_hash = true,
371 .chk_mtime = true,
372 .chk_config_protect = true,
373 .undo_prelink = false,
374 .exact = false,
375 };
376
377 DBG("argc=%d argv[0]=%s argv[1]=%s",
378 argc, argv[0], argc > 1 ? argv[1] : "NULL?");
379
380 while ((i = GETOPT_LONG(QCHECK, qcheck, "")) != -1) {
381 switch (i) {
382 COMMON_GETOPTS_CASES(qcheck)
383 case 'a': state.search_all = true; break;
384 case 'e': state.exact = true; break;
385 case 's': {
386 regex_t regex;
387 xregcomp(&regex, optarg, REG_EXTENDED|REG_NOSUB);
388 xarraypush(regex_arr, &regex, sizeof(regex));
389 break;
390 }
391 case 'u': state.qc_update = true; break;
392 case 'A': state.chk_afk = false; break;
393 case 'B': state.bad_only = true; break;
394 case 'H': state.chk_hash = false; break;
395 case 'T': state.chk_mtime = false; break;
396 case 128: state.chk_config_protect = false; break;
397 case 'p': state.undo_prelink = prelink_available(); break;
398 }
399 }
400 if ((argc == optind) && !state.search_all)
401 qcheck_usage(EXIT_FAILURE);
402
403 ret = q_vdb_foreach_pkg(qcheck_cb, &state, NULL);
404 xarrayfree(regex_arr);
405 return ret;
406 }
407
408 #else
409 DEFINE_APPLET_STUB(qcheck)
410 #endif

  ViewVC Help
Powered by ViewVC 1.1.20