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

Contents of /portage-utils/qcheck.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.59 - (show annotations) (download) (as text)
Tue Jan 7 19:48:45 2014 UTC (6 months, 3 weeks ago) by vapier
Branch: MAIN
Changes since 1.58: +3 -3 lines
File MIME type: text/x-csrc
qcache/qcheck: use O_CLOEXEC in more places

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.58 2013/04/21 04:28:10 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.58 2013/04/21 04:28:10 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|O_CLOEXEC);
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 %"PRIu64"u\n", e->name, hashed_file, (uint64_t)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 '%"PRIu64"u' != actual '%"PRIu64"u')", (uint64_t)e->mtime, (uint64_t)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 %"PRIu64"u\n", e->name, e->digest, (uint64_t)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 '%"PRIu64"u' != actual '%"PRIu64"u')",
237 (uint64_t)e->mtime, (uint64_t)st.st_mtime);
238 qcprintf("\n");
239
240 /* This can only be a sym */
241 if (state->qc_update)
242 fprintf(fpx, "sym %s -> %s %"PRIu64"u\n", e->name, e->sym_target, (uint64_t)st.st_mtime);
243 } else {
244 --num_files;
245 ++num_files_ignored;
246 if (state->qc_update)
247 fputs(buffer, fpx);
248 }
249 continue;
250 } else {
251 if (state->qc_update)
252 fputs(buffer, fpx);
253 }
254 cfg_protected:
255 ++num_files_ok;
256 }
257 free(line);
258 free(buffer);
259 fclose(fp);
260
261 if (!state->chk_config_protect) {
262 freeargv(cp_argc, cp_argv);
263 freeargv(cpm_argc, cpm_argv);
264 }
265
266 if (state->qc_update) {
267 if (fchown(fd, cst.st_uid, cst.st_gid)) {
268 /* meh */;
269 }
270 if (fchmod(fd, cst.st_mode)) {
271 /* meh */;
272 }
273 fclose(fpx);
274 if (renameat(pkg_ctx->fd, "CONTENTS~", pkg_ctx->fd, "CONTENTS"))
275 unlinkat(pkg_ctx->fd, "CONTENTS~", 0);
276 if (!verbose)
277 return EXIT_SUCCESS;
278 }
279 if (state->bad_only && num_files_ok != num_files) {
280 if (verbose)
281 printf("%s/%s\n", catname, pkgname);
282 else {
283 depend_atom *atom = NULL;
284 char *buf;
285 xasprintf(&buf, "%s/%s", catname, pkgname);
286 if ((atom = atom_explode(buf)) != NULL) {
287 printf("%s/%s\n", catname, atom->PN);
288 atom_implode(atom);
289 } else {
290 printf("%s/%s\n", catname, pkgname);
291 }
292 free(buf);
293 }
294 }
295 qcprintf(" %2$s*%1$s %3$s%4$zu%1$s out of %3$s%5$zu%1$s file%6$s are good",
296 NORM, BOLD, BLUE, num_files_ok, num_files,
297 (num_files > 1 ? "s" : ""));
298 if (num_files_unknown)
299 qcprintf(" (Unable to digest %2$s%3$zu%1$s file%4$s)",
300 NORM, BLUE, num_files_unknown,
301 (num_files_unknown > 1 ? "s" : ""));
302 if (num_files_ignored)
303 qcprintf(" (%2$s%3$zu%1$s file%4$s ignored)",
304 NORM, BLUE, num_files_ignored,
305 (num_files_ignored > 1 ? "s were" : " was"));
306 qcprintf("\n");
307
308 if (num_files_ok != num_files)
309 return EXIT_FAILURE;
310 else
311 return EXIT_SUCCESS;
312 }
313
314 _q_static int qcheck_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv)
315 {
316 struct qcheck_opt_state *state = priv;
317 const char *catname = pkg_ctx->cat_ctx->name;
318 const char *pkgname = pkg_ctx->name;
319
320 /* see if this cat/pkg is requested */
321 if (!state->search_all) {
322 char *buf = NULL;
323 int i;
324
325 for (i = optind; i < state->argc; ++i) {
326 free(buf);
327 xasprintf(&buf, "%s/%s", catname, pkgname);
328 if (!state->exact) {
329 if (rematch(state->argv[i], buf, REG_EXTENDED) == 0)
330 break;
331 if (rematch(state->argv[i], pkgname, REG_EXTENDED) == 0)
332 break;
333 } else {
334 depend_atom *atom;
335 char swap[_Q_PATH_MAX];
336 if ((atom = atom_explode(buf)) == NULL) {
337 warn("invalid atom %s", buf);
338 continue;
339 }
340 snprintf(swap, sizeof(swap), "%s/%s", atom->CATEGORY, atom->PN);
341 atom_implode(atom);
342 if (strcmp(state->argv[i], swap) == 0 ||
343 strcmp(state->argv[i], buf) == 0)
344 break;
345 if (strcmp(state->argv[i], strstr(swap, "/") + 1) == 0 ||
346 strcmp(state->argv[i], strstr(buf, "/") + 1) == 0)
347 break;
348 }
349 }
350 free(buf);
351
352 if (i == state->argc)
353 return 0;
354 }
355
356 return qcheck_process_contents(pkg_ctx, priv);
357 }
358
359 int qcheck_main(int argc, char **argv)
360 {
361 int i, ret;
362 DECLARE_ARRAY(regex_arr);
363 struct qcheck_opt_state state = {
364 .argc = argc,
365 .argv = argv,
366 .regex_arr = regex_arr,
367 .bad_only = false,
368 .search_all = false,
369 .qc_update = false,
370 .chk_afk = true,
371 .chk_hash = true,
372 .chk_mtime = true,
373 .chk_config_protect = true,
374 .undo_prelink = false,
375 .exact = false,
376 };
377
378 DBG("argc=%d argv[0]=%s argv[1]=%s",
379 argc, argv[0], argc > 1 ? argv[1] : "NULL?");
380
381 while ((i = GETOPT_LONG(QCHECK, qcheck, "")) != -1) {
382 switch (i) {
383 COMMON_GETOPTS_CASES(qcheck)
384 case 'a': state.search_all = true; break;
385 case 'e': state.exact = true; break;
386 case 's': {
387 regex_t regex;
388 xregcomp(&regex, optarg, REG_EXTENDED|REG_NOSUB);
389 xarraypush(regex_arr, &regex, sizeof(regex));
390 break;
391 }
392 case 'u': state.qc_update = true; break;
393 case 'A': state.chk_afk = false; break;
394 case 'B': state.bad_only = true; break;
395 case 'H': state.chk_hash = false; break;
396 case 'T': state.chk_mtime = false; break;
397 case 128: state.chk_config_protect = false; break;
398 case 'p': state.undo_prelink = prelink_available(); break;
399 }
400 }
401 if ((argc == optind) && !state.search_all)
402 qcheck_usage(EXIT_FAILURE);
403
404 ret = q_vdb_foreach_pkg(qcheck_cb, &state, NULL);
405 xarrayfree(regex_arr);
406 return ret;
407 }
408
409 #else
410 DEFINE_APPLET_STUB(qcheck)
411 #endif

  ViewVC Help
Powered by ViewVC 1.1.20