diff options
author | Mike Frysinger <vapier@gentoo.org> | 2011-10-03 03:18:10 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2011-10-03 03:18:10 +0000 |
commit | 68df26c121ac53be83409bb5ff18d4484dbbe039 (patch) | |
tree | 68a3c46fd3cb58468f07fc13810ce6c6628b1a84 /qcheck.c | |
parent | add xregcomp helpers (diff) | |
download | portage-utils-68df26c121ac53be83409bb5ff18d4484dbbe039.tar.gz portage-utils-68df26c121ac53be83409bb5ff18d4484dbbe039.tar.bz2 portage-utils-68df26c121ac53be83409bb5ff18d4484dbbe039.zip |
rewrite qcheck to use *at style funcs
Diffstat (limited to 'qcheck.c')
-rw-r--r-- | qcheck.c | 484 |
1 files changed, 263 insertions, 221 deletions
@@ -1,7 +1,7 @@ /* * Copyright 2005-2010 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 - * $Header: /var/cvsroot/gentoo-projects/portage-utils/qcheck.c,v 1.49 2011/10/03 01:25:54 vapier Exp $ + * $Header: /var/cvsroot/gentoo-projects/portage-utils/qcheck.c,v 1.50 2011/10/03 03:18:10 vapier Exp $ * * Copyright 2005-2010 Ned Ludd - <solar@gentoo.org> * Copyright 2005-2010 Mike Frysinger - <vapier@gentoo.org> @@ -34,11 +34,11 @@ static const char * const qcheck_opts_help[] = { "Undo prelink when calculating checksums", COMMON_OPTS_HELP }; -static const char qcheck_rcsid[] = "$Id: qcheck.c,v 1.49 2011/10/03 01:25:54 vapier Exp $"; +static const char qcheck_rcsid[] = "$Id: qcheck.c,v 1.50 2011/10/03 03:18:10 vapier Exp $"; #define qcheck_usage(ret) usage(ret, QCHECK_FLAGS, qcheck_long_opts, qcheck_opts_help, lookup_applet_idx("qcheck")) -short bad_only = 0; -#define qcprintf(fmt, args...) if (!bad_only) printf( _( fmt ), ## args) +static bool bad_only = false; +#define qcprintf(fmt, args...) if (!bad_only) printf(_(fmt), ## args) static void qcheck_cleanup(regex_t **regex_head, const size_t regex_count) { @@ -54,21 +54,242 @@ static void qcheck_cleanup(regex_t **regex_head, const size_t regex_count) free(regex_head); } +static int qcheck_process_contents(int portroot_fd, int pkg_fd, + const char *catname, const char *pkgname, regex_t **regex_head, + size_t regex_count, bool qc_update, bool chk_afk, + bool chk_hash, bool chk_mtime, bool undo_prelink) +{ + int fd; + FILE *fp, *fpx; + size_t num_files, num_files_ok, num_files_unknown, num_files_ignored; + char *buffer, *line; + size_t linelen; + struct stat st, cst; + + fpx = NULL; + + fd = openat(pkg_fd, "CONTENTS", O_RDONLY|O_CLOEXEC); + if (fd == -1) + return EXIT_SUCCESS; + if (fstat(fd, &cst)) { + close(fd); + return EXIT_SUCCESS; + } + if ((fp = fdopen(fd, "r")) == NULL) { + close(fd); + return EXIT_SUCCESS; + } + + num_files = num_files_ok = num_files_unknown = num_files_ignored = 0; + qcprintf("%sing %s%s/%s%s ...\n", + (qc_update ? "Updat" : "Check"), + GREEN, catname, pkgname, NORM); + if (qc_update) { + fd = openat(pkg_fd, "CONTENTS~", O_RDWR|O_CLOEXEC|O_CREAT|O_TRUNC, 0644); + if (fd == -1 || (fpx = fdopen(fd, "w")) == NULL) { + fclose(fp); + warnp("unable to fopen(%s/%s, w)", pkgname, "CONTENTS~"); + return EXIT_FAILURE; + } + } + + buffer = line = NULL; + while (getline(&line, &linelen, fp) != -1) { + contents_entry *e; + free(buffer); + buffer = xstrdup(line); + e = contents_parse_line(line); + if (!e) + continue; + + /* run our little checks */ + ++num_files; + if (regex_count) { + size_t j; + for (j = 0; j < regex_count; ++j) + if (!regexec(regex_head[j], e->name, 0, NULL, 0)) + break; + if (j < regex_count) { + --num_files; + ++num_files_ignored; + continue; + } + } + if (fstatat(portroot_fd, e->name + 1, &st, AT_SYMLINK_NOFOLLOW)) { + /* make sure file exists */ + if (chk_afk) { + qcprintf(" %sAFK%s: %s\n", RED, NORM, e->name); + } else { + --num_files; + ++num_files_ignored; + if (qc_update) + fputs(buffer, fpx); + } + continue; + } + if (e->digest && S_ISREG(st.st_mode)) { + /* validate digest (handles MD5 / SHA1) */ + uint8_t hash_algo; + char *hashed_file; + hash_cb_t hash_cb = undo_prelink ? hash_cb_prelink_undo : hash_cb_default; + switch (strlen(e->digest)) { + case 32: hash_algo = HASH_MD5; break; + case 40: hash_algo = HASH_SHA1; break; + default: hash_algo = 0; break; + } + if (!hash_algo) { + if (chk_hash) { + qcprintf(" %sUNKNOWN DIGEST%s: '%s' for '%s'\n", RED, NORM, e->digest, e->name); + ++num_files_unknown; + } else { + --num_files; + ++num_files_ignored; + if (qc_update) + fputs(buffer, fpx); + } + continue; + } + hashed_file = (char*)hash_file_cb(e->name, hash_algo, hash_cb); + if (!hashed_file) { + ++num_files_unknown; + free(hashed_file); + if (qc_update) { + fputs(buffer, fpx); + if (!verbose) + continue; + } + qcprintf(" %sPERM %4o%s: %s\n", RED, (unsigned int)(st.st_mode & 07777), NORM, e->name); + continue; + } else if (strcmp(e->digest, hashed_file)) { + if (chk_hash) { + const char *digest_disp; + if (qc_update) + fprintf(fpx, "obj %s %s %lu\n", e->name, hashed_file, st.st_mtime); + switch (hash_algo) { + case HASH_MD5: digest_disp = "MD5"; break; + case HASH_SHA1: digest_disp = "SHA1"; break; + default: digest_disp = "UNK"; break; + } + qcprintf(" %s%s-DIGEST%s: %s", RED, digest_disp, NORM, e->name); + if (verbose) + qcprintf(" (recorded '%s' != actual '%s')", e->digest, hashed_file); + qcprintf("\n"); + } else { + --num_files; + ++num_files_ignored; + if (qc_update) + fputs(buffer, fpx); + } + free(hashed_file); + continue; + } else if (e->mtime && e->mtime != st.st_mtime) { + if (chk_mtime) { + qcprintf(" %sMTIME%s: %s", RED, NORM, e->name); + if (verbose) + qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime); + qcprintf("\n"); + + /* This can only be an obj, dir and sym have no digest */ + if (qc_update) + fprintf(fpx, "obj %s %s %lu\n", e->name, e->digest, st.st_mtime); + } else { + --num_files; + ++num_files_ignored; + if (qc_update) + fputs(buffer, fpx); + } + free(hashed_file); + continue; + } else { + if (qc_update) + fputs(buffer, fpx); + free(hashed_file); + } + } else if (e->mtime && e->mtime != st.st_mtime) { + if (chk_mtime) { + qcprintf(" %sMTIME%s: %s", RED, NORM, e->name); + if (verbose) + qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime); + qcprintf("\n"); + + /* This can only be a sym */ + if (qc_update) + fprintf(fpx, "sym %s -> %s %lu\n", e->name, e->sym_target, st.st_mtime); + } else { + --num_files; + ++num_files_ignored; + if (qc_update) + fputs(buffer, fpx); + } + continue; + } else { + if (qc_update) + fputs(buffer, fpx); + } + ++num_files_ok; + } + free(line); + free(buffer); + fclose(fp); + + if (qc_update) { + if (fchown(fd, cst.st_uid, cst.st_gid)) + /* meh */; + if (fchmod(fd, cst.st_mode)) + /* meh */; + fclose(fpx); + if (renameat(pkg_fd, "CONTENTS~", pkg_fd, "CONTENTS")) + unlinkat(pkg_fd, "CONTENTS~", 0); + if (!verbose) + return EXIT_SUCCESS; + } + if (bad_only && num_files_ok != num_files) { + if (verbose) + printf("%s/%s\n", catname, pkgname); + else { + depend_atom *atom = NULL; + char *buf; + xasprintf(&buf, "%s/%s", catname, pkgname); + if ((atom = atom_explode(buf)) != NULL) { + printf("%s/%s\n", catname, atom->PN); + atom_implode(atom); + } else { + printf("%s/%s\n", catname, pkgname); + } + free(buf); + } + } + qcprintf(" %2$s*%1$s %3$s%4$zu%1$s out of %3$s%5$zu%1$s file%6$s are good", + NORM, BOLD, BLUE, num_files_ok, num_files, + (num_files > 1 ? "s" : "")); + if (num_files_unknown) + qcprintf(" (Unable to digest %2$s%3$zu%1$s file%4$s)", + NORM, BLUE, num_files_unknown, + (num_files_unknown > 1 ? "s" : "")); + if (num_files_ignored) + qcprintf(" (%2$s%3$zu%1$s file%4$s ignored)", + NORM, BLUE, num_files_ignored, + (num_files_ignored > 1 ? "s were" : " was")); + qcprintf("\n"); + + if (num_files_ok != num_files) + return EXIT_FAILURE; + else + return EXIT_SUCCESS; +} + int qcheck_main(int argc, char **argv) { DIR *dir, *dirp; int i, ret; + int portroot_fd, vdb_fd, cat_fd, pkg_fd; struct dirent *dentry, *de; - char search_all = 0; - char qc_update = 0; - char chk_afk = 1; - char chk_hash = 1; - char chk_mtime = 1; + bool search_all = 0; + bool qc_update = false; + bool chk_afk = true; + bool chk_hash = true; + bool chk_mtime = true; bool undo_prelink = false; - struct stat st; - size_t num_files, num_files_ok, num_files_unknown, num_files_ignored; - char buf[_Q_PATH_MAX], filename[_Q_PATH_MAX]; - char buffer[_Q_PATH_MAX]; regex_t **regex_head = NULL; size_t regex_count = 0; @@ -78,7 +299,7 @@ int qcheck_main(int argc, char **argv) while ((i = GETOPT_LONG(QCHECK, qcheck, "")) != -1) { switch (i) { COMMON_GETOPTS_CASES(qcheck) - case 'a': search_all = 1; break; + case 'a': search_all = true; break; case 'e': exact = 1; break; case 's': { regex_head = xrealloc(regex_head, (regex_count + 1) * sizeof(*regex_head)); @@ -87,42 +308,47 @@ int qcheck_main(int argc, char **argv) ++regex_count; } break; - case 'u': qc_update = 1; break; - case 'A': chk_afk = 0; break; - case 'B': bad_only = 1; break; - case 'H': chk_hash = 0; break; - case 'T': chk_mtime = 0; break; + case 'u': qc_update = true; break; + case 'A': chk_afk = false; break; + case 'B': bad_only = true; break; + case 'H': chk_hash = false; break; + case 'T': chk_mtime = false; break; case 'p': undo_prelink = prelink_available(); break; } } if ((argc == optind) && !search_all) qcheck_usage(EXIT_FAILURE); - snprintf(buf, sizeof(buf), "%s/%s", portroot, portvdb); - xchdir(buf); - if ((dir = opendir(".")) == NULL) - errp("unable to read '.' !?"); + portroot_fd = open(portroot, O_RDONLY|O_CLOEXEC); + if (portroot_fd == -1) + errp("unable to read %s !?", portroot); + vdb_fd = openat(portroot_fd, portvdb + 1, O_RDONLY|O_CLOEXEC); + if (vdb_fd == -1 || (dir = fdopendir(vdb_fd)) == NULL) + errp("unable to read %s !?", portvdb); ret = EXIT_SUCCESS; /* open /var/db/pkg */ while ((dentry = q_vdb_get_next_dir(dir))) { - if (chdir(dentry->d_name) != 0) + cat_fd = openat(vdb_fd, dentry->d_name, O_RDONLY|O_CLOEXEC); + if (cat_fd == -1) continue; - if ((dirp = opendir(".")) == NULL) + if ((dirp = fdopendir(cat_fd)) == NULL) { + close(cat_fd); continue; + } /* open the cateogry */ while ((de = readdir(dirp)) != NULL) { - FILE *fp, *fpx; if (*de->d_name == '.') continue; - fp = fpx = NULL; /* see if this cat/pkg is requested */ if (!search_all) { + char *buf = NULL; for (i = optind; i < argc; ++i) { - snprintf(buf, sizeof(buf), "%s/%s", dentry->d_name, de->d_name); + free(buf); + xasprintf(&buf, "%s/%s", dentry->d_name, de->d_name); if (!exact) { if (rematch(argv[i], buf, REG_EXTENDED) == 0) break; @@ -141,211 +367,27 @@ int qcheck_main(int argc, char **argv) break; if ((strcmp(argv[i], strstr(swap, "/") + 1) == 0) || (strcmp(argv[i], strstr(buf, "/") + 1) == 0)) break; - } + } } + free(buf); if (i == argc) continue; } - snprintf(buf, sizeof(buf), "%s%s/%s/%s/CONTENTS", portroot, portvdb, dentry->d_name, de->d_name); - if ((fp = fopen(buf, "re")) == NULL) + pkg_fd = openat(cat_fd, de->d_name, O_RDONLY|O_CLOEXEC); + if (pkg_fd == -1) continue; - strncat(buf, "~", sizeof(buf)-strlen(buf)-1); - num_files = num_files_ok = num_files_unknown = num_files_ignored = 0; - qcprintf("%sing %s%s/%s%s ...\n", - (qc_update ? "Updat" : "Check"), - GREEN, dentry->d_name, de->d_name, NORM); - if (qc_update) { - if ((fpx = fopen(buf, "we")) == NULL) { - fclose(fp); - warnp("unable to fopen(%s, w)", buf); - continue; - } - } - while ((fgets(buf, sizeof(buf), fp)) != NULL) { - contents_entry *e; - /* safe. buf and buffer are the same size.. */ - strcpy(buffer, buf); - e = contents_parse_line(buf); - if (!e) - continue; - if (strcmp(portroot, "/") != 0) { - snprintf(filename, sizeof(filename), "%s%s", portroot, e->name); - e->name = filename; - } - - /* run our little checks */ - ++num_files; - if (regex_count) { - size_t j; - for (j = 0; j < regex_count; ++j) - if (!regexec(regex_head[j], e->name, 0, NULL, 0)) - break; - if (j < regex_count) { - --num_files; - ++num_files_ignored; - continue; - } - } - if (lstat(e->name, &st)) { - /* make sure file exists */ - if (chk_afk) { - qcprintf(" %sAFK%s: %s\n", RED, NORM, e->name); - } else { - --num_files; - ++num_files_ignored; - if (qc_update) - fputs(buffer, fpx); - } - continue; - } - if (e->digest && S_ISREG(st.st_mode)) { - /* validate digest (handles MD5 / SHA1) */ - uint8_t hash_algo; - char *hashed_file; - hash_cb_t hash_cb = undo_prelink ? hash_cb_prelink_undo : hash_cb_default; - switch (strlen(e->digest)) { - case 32: hash_algo = HASH_MD5; break; - case 40: hash_algo = HASH_SHA1; break; - default: hash_algo = 0; break; - } - if (!hash_algo) { - if (chk_hash) { - qcprintf(" %sUNKNOWN DIGEST%s: '%s' for '%s'\n", RED, NORM, e->digest, e->name); - ++num_files_unknown; - } else { - --num_files; - ++num_files_ignored; - if (qc_update) - fputs(buffer, fpx); - } - continue; - } - hashed_file = (char*)hash_file_cb(e->name, hash_algo, hash_cb); - if (!hashed_file) { - ++num_files_unknown; - free(hashed_file); - if (qc_update) { - fputs(buffer, fpx); - if (!verbose) - continue; - } - qcprintf(" %sPERM %4o%s: %s\n", RED, (unsigned int)(st.st_mode & 07777), NORM, e->name); - continue; - } else if (strcmp(e->digest, hashed_file)) { - if (chk_hash) { - const char *digest_disp; - if (qc_update) - fprintf(fpx, "obj %s %s %lu\n", e->name, hashed_file, st.st_mtime); - switch (hash_algo) { - case HASH_MD5: digest_disp = "MD5"; break; - case HASH_SHA1: digest_disp = "SHA1"; break; - default: digest_disp = "UNK"; break; - } - qcprintf(" %s%s-DIGEST%s: %s", RED, digest_disp, NORM, e->name); - if (verbose) - qcprintf(" (recorded '%s' != actual '%s')", e->digest, hashed_file); - qcprintf("\n"); - } else { - --num_files; - ++num_files_ignored; - if (qc_update) - fputs(buffer, fpx); - } - free(hashed_file); - continue; - } else if (e->mtime && e->mtime != st.st_mtime) { - if (chk_mtime) { - qcprintf(" %sMTIME%s: %s", RED, NORM, e->name); - if (verbose) - qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime); - qcprintf("\n"); - - /* This can only be an obj, dir and sym have no digest */ - if (qc_update) - fprintf(fpx, "obj %s %s %lu\n", e->name, e->digest, st.st_mtime); - } else { - --num_files; - ++num_files_ignored; - if (qc_update) - fputs(buffer, fpx); - } - free(hashed_file); - continue; - } else { - if (qc_update) - fputs(buffer, fpx); - free(hashed_file); - } - } else if (e->mtime && e->mtime != st.st_mtime) { - if (chk_mtime) { - qcprintf(" %sMTIME%s: %s", RED, NORM, e->name); - if (verbose) - qcprintf(" (recorded '%lu' != actual '%lu')", e->mtime, (unsigned long)st.st_mtime); - qcprintf("\n"); - - /* This can only be a sym */ - if (qc_update) - fprintf(fpx, "sym %s -> %s %lu\n", e->name, e->sym_target, st.st_mtime); - } else { - --num_files; - ++num_files_ignored; - if (qc_update) - fputs(buffer, fpx); - } - continue; - } else { - if (qc_update) - fputs(buffer, fpx); - } - ++num_files_ok; - } - fclose(fp); - if (qc_update) { - fclose(fpx); - snprintf(buf, sizeof(buf), "%s%s/%s/%s/CONTENTS", portroot, portvdb, - dentry->d_name, de->d_name); - strcpy(buffer, buf); - strncat(buffer, "~", sizeof(buffer)-strlen(buf)-1); - rename(buffer, buf); - if (!verbose) - continue; - } - if (bad_only && num_files_ok != num_files) { - if (verbose) - printf("%s/%s\n", dentry->d_name, de->d_name); - else { - depend_atom *atom = NULL; - snprintf(buf, sizeof(buf), "%s/%s", dentry->d_name, de->d_name); - if ((atom = atom_explode(buf)) != NULL) { - printf("%s/%s\n", dentry->d_name, atom->PN); - atom_implode(atom); - } else { - printf("%s/%s\n", dentry->d_name, de->d_name); - } - } - } - qcprintf(" %2$s*%1$s %3$s%4$zu%1$s out of %3$s%5$zu%1$s file%6$s are good", - NORM, BOLD, BLUE, num_files_ok, num_files, - (num_files > 1 ? "s" : "")); - if (num_files_unknown) - qcprintf(" (Unable to digest %2$s%3$zu%1$s file%4$s)", - NORM, BLUE, num_files_unknown, - (num_files_unknown > 1 ? "s" : "")); - if (num_files_ignored) - qcprintf(" (%2$s%3$zu%1$s file%4$s ignored)", - NORM, BLUE, num_files_ignored, - (num_files_ignored > 1 ? "s were" : " was")); - qcprintf("\n"); - if (num_files_ok != num_files) - ret = EXIT_FAILURE; + ret = qcheck_process_contents(portroot_fd, pkg_fd, + dentry->d_name, de->d_name, regex_head, regex_count, + qc_update, chk_afk, chk_hash, chk_mtime, undo_prelink); + close(pkg_fd); } closedir(dirp); - xchdir(".."); } qcheck_cleanup(regex_head, regex_count); + close(portroot_fd); return ret; } |