From dad7fbab0ce887964bdf403438ea48bde3e780fb Mon Sep 17 00:00:00 2001 From: Nathan Wagner Date: Mon, 8 Oct 2018 01:20:00 +0000 Subject: [PATCH] improve error reporting in syncfs --- zpm-syncfs.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 148 insertions(+), 14 deletions(-) diff --git a/zpm-syncfs.c b/zpm-syncfs.c index c77b310..ead450e 100644 --- a/zpm-syncfs.c +++ b/zpm-syncfs.c @@ -11,6 +11,7 @@ #include #include #include +#include /* needed for S_IFMT and AT_FDCWD */ #include @@ -28,6 +29,7 @@ struct config { int errabort, errors, verbose, dryrun, conflicts; int setuser, setgroup; int reverse, exitonerror; + int overwrite, absorb; }; static void usage() { @@ -117,10 +119,70 @@ char *column(char *col, int ncols, char **vals, char **cols) { return val; } -#define IERR(x) do { conf->errors++; conf->log->errmsg = strdup(x); return conf->errabort; } while (0) #define COL(x) column(x, ncols, vals, cols) #define SYSERR(x) do { conf->log->error = 2; return conf->errabort; } while (0) +static int seterror(struct config *conf, char *msgfmt, ...) { + char msg[1024]; + va_list ap; + + conf->errors++; + + va_start(ap, msgfmt); + vsnprintf(msg, sizeof msg, msgfmt, ap); + va_end(ap); + + msg[1023] = 0; + if (conf->log->errmsg) { + free(conf->log->errmsg); + } + + conf->log->errmsg = strdup(msg); + + if (conf->verbose) { + fprintf(stderr, "%s\n", msg); + } + + return conf->errabort; +} + +static int setsyserr(struct config *conf, char *msgfmt, ...) { + char msg[1024]; + va_list ap; + int printed; + + conf->errors++; + + va_start(ap, msgfmt); + printed = vsnprintf(msg, sizeof msg, msgfmt, ap); + va_end(ap); + + if (printed < 1) { + /* nothing we can really do */ + return conf->errabort; + } + + if ((size_t)printed < sizeof msg) { + snprintf(msg+printed, sizeof msg - printed, ": %s", + strerror(errno)); + } + + msg[1023] = 0; + if (conf->log->errmsg) { + free(conf->log->errmsg); + } + + conf->log->errmsg = strdup(msg); + + if (conf->verbose) { + fprintf(stderr, "%s\n", msg); + } + + return conf->errabort; +} + +#define IERR(x) return seterror(conf, x) + static char *ops[] = { "new", "remove", "update", 0 }; static int getop(char *opstr) { @@ -269,6 +331,57 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) { #define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__) +/* 1 = file doesn't exist, 2 = file is a directory, target isn't */ +/* 4 == ftype different */ +/* 8 = hash different when both are regular files */ +static unsigned int file_compare(char *path, char *hash, int ftype, uid_t uid, + gid_t gid, int perms) { + struct stat st; + int etype = 0, stat_type; + char ehash[ZPM_HASH_STRLEN+1]; + unsigned int diff = 0; + + switch (ftype) { + case 'd': etype = S_IFDIR; break; + case 'r': etype = S_IFREG; break; + default: etype = 0; break; + } + + errno = 0; + /* new file, so check type, hash, etc */ + if (lstat(path, &st) == 0) { + stat_type = st.st_mode & S_IFMT; + if (stat_type != etype) { + diff &= 4; + } + if (stat_type == S_IFDIR && etype != S_IFDIR) { + diff &= 2; + } + if (hash && etype == S_IFREG && stat_type == S_IFREG) { + zpm_hash(path, ehash, 0); + if (strcmp(hash, ehash) != 0) { + diff &= 8; + } + } + if (uid != st.st_uid) { + diff &= 16; + } + if (gid != st.st_gid) { + diff &= 32; + } + if (perms != (st.st_mode & 07777)) { + diff &= 64; + } + } else { + switch(errno) { + case ENOENT: diff &= 1; break; + default: diff &= 128; break; + } + } + + return diff; +} + static int install_files(void *f, int ncols, char **vals, char **cols) { struct config *conf = f; struct passwd *pw; @@ -350,31 +463,44 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { /* TODO should these be owned by the path owner if they don't exist? */ /* probably, though they then belong to the package, sort of */ if (!create_leading_dirs(dest)) { - fprintf(stderr, "unable to create leading directories for %s\n", - dest); - IERR("cld failure"); + return setsyserr(conf, "unable to create leading directories for %s\n", dest); + } + + unsigned int diffs = file_compare(dest, hash, ftype, uid, gid, mode); + + /* 1 = file doesn't exist, 2 = file is a directory, target isn't */ + /* 4 == ftype different */ + /* 8 = hash different when both are regular files */ + if (diffs == 128) { + return seterror(conf, "can't check %s", dest); } - if (ftype == 'd') { + if (diffs > 1) { + return seterror(conf, "absorb and overwrite not implemented"); + } + + if (ftype == 'd') { if (mkdir(dest, mode) == -1) { - IERR("can't mkdir"); + return setsyserr(conf, "can't create directory %s", + dest); } } else if (ftype == 'r') { struct zpm *source; source = conf->src ? conf->src : conf->log; + if (conf->verbose > 1) { fprintf(stderr, "extracting %8.8s to %s with mode %o\n", hash, dest, mode); } + if (!zpm_extract(source, hash, dest, mode)) { IERR("can't extract file"); } } else if (ftype == 'l') { char *target = COL("target"); if (!target) { - fprintf(stderr, "no target for symlink %s\n", path); - conf->errors++; - return conf->errabort; + return seterror(conf, "no target for symlink %s\n", + path); } if (strlen(target) == 0) { @@ -385,8 +511,7 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { fprintf(stderr, "symlink %s -> %s\n", dest, target); } if (symlink(target, dest) == -1) { - perror("symlink failed"); - IERR("can't symlink"); + return setsyserr(conf, "symlink failed"); } } else { fprintf(stderr, "unhandled filetype %c\n", ftype); @@ -394,7 +519,7 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { if (conf->setuser && conf->setgroup) { if (chown(dest, uid, gid) == -1) { - IERR("can't chown"); + return setsyserr(conf, "can't chown %s", dest); } } @@ -549,6 +674,8 @@ int main(int ac, char **av){ conf.src = 0; conf.rootdir = 0; conf.reverse = 0; + conf.overwrite = 0; + conf.absorb = 0; if (geteuid() != 0) { conf.setuser = 0; @@ -575,7 +702,7 @@ int main(int ac, char **av){ * args are pkgid triple, but will do a pkg find on the pkgdb */ - while ((opt = getopt(ac, av, "f:d:c:nCR:v")) != -1) { + while ((opt = getopt(ac, av, "f:d:c:nCR:vOA")) != -1) { switch (opt) { case 'd': localdbfile = optarg; break; case 'f': pkgdbfile = optarg; break; @@ -584,6 +711,8 @@ int main(int ac, char **av){ case 'C': conf.errabort = 0; break; case 'R': conf.rootdir = optarg; break; case 'N': conf.setuser = 0; conf.setgroup = 0; break; + case 'O': conf.overwrite = 1; break; + case 'A': conf.absorb = 1; break; default: usage(); exit(EXIT_FAILURE); @@ -628,7 +757,12 @@ int main(int ac, char **av){ conf.conflicts); conf.errors++; } else { - runstage(&conf, "new", check_existing); + /* no point in running it if we're just going to + * overwrite everything + */ + if (conf.overwrite == 0 || conf.absorb == 0) { + runstage(&conf, "new", check_existing); + } if (!conf.errors) { conf.exitonerror = 1; -- 2.40.0