From: Nathan Wagner Date: Sat, 1 Dec 2018 17:20:44 +0000 (+0000) Subject: improve syncfs pre-checks and directory handling X-Git-Tag: v0.3.0 X-Git-Url: https://pd.if.org/git/?p=zpackage;a=commitdiff_plain;h=a97e143d89bf67884ab2b35e77a3077cf5816af8 improve syncfs pre-checks and directory handling --- diff --git a/zpm-syncfs.c b/zpm-syncfs.c index ce0c0bb..4aa792d 100644 --- a/zpm-syncfs.c +++ b/zpm-syncfs.c @@ -30,7 +30,7 @@ struct config { int errabort, errors, verbose, dryrun, conflicts; int setuser, setgroup; int reverse, exitonerror; - int overwrite, absorb; + int overwrite, accept, acceptdir, ignoredirmd; }; struct nitem { @@ -247,92 +247,148 @@ static int report_conflicts(void *f, int ncols, char **vals, char **cols) { return 0; } -static int check_existing(void *f, int ncols, char **vals, char **cols) { - struct config *conf = f; - char *path; - struct stat st; +static int read_item(struct config *conf, int ncols, char **vals, char **cols, + struct nitem *n) { + char *val; + long lval; + struct passwd *pw; + struct group *gr; + struct nitem zero = { 0 }; - path = COL("dest"); - if (!path) { - return seterror(conf, "no path"); + *n = zero; + + val = COL("op"); + if (!val) { + seterror(conf, "can't determine op"); + return 0; + } + n->opstr = val; + n->op = getop(val); + if (!n->op) { + seterror(conf, "can't determine op"); + return 0; } - if (conf->dryrun) { - printf("checkfor %s\n", path); - fflush(stdout); + n->path = COL("path"); + if (!n->path) { + seterror(conf, "no file path"); + return 0; + } + if (strlen(n->path) == 0) { + seterror(conf, "zero length path not allowed"); return 0; } - if (conf->verbose) { - fprintf(stderr, "check for existing %s\n", path); + /* TODO config to dishonor setuid/setgid */ + n->dest = COL("dest"); + if (!n->dest) { + seterror(conf, "no file dest"); + return 0; } - if (lstat(path, &st) == 0) { - fprintf(stderr, "%s exists\n", path); - conf->errors++; - } else { - switch(errno) { - /* not an error, file shouldn't exist*/ - case ENOENT: break; - default: - fprintf(stderr, "unable to check %s: %s\n", - path, strerror(errno)); - conf->errors++; - break; - } + if (strlen(n->dest) == 0) { + seterror(conf, "zero length dest not allowed"); + return 0; } - return 0; -} -static int remove_files(void *f, int ncols, char **vals, char **cols) { - struct config *conf = f; - char *dest; - struct stat st; - int flags = 0; + val = COL("mode"); - dest = COL("dest"); - if (!dest) return seterror(conf,"no file dest"); + if (!val) { + seterror(conf, "can't determine mode"); + return 0; + } - if (conf->dryrun) { - char *ftype = COL("filetype"); - int t = *ftype; + n->mode = strtoul(val, NULL, 8); - switch(t) { - case 'd': printf("rmdir %s\n", dest); break; - default: printf("unlink %s\n", dest); break; - } - fflush(stdout); + val = COL("configuration"); + if (!val) { + seterror(conf, "can't determine config status"); return 0; } + lval = strtol(val, NULL, 10); - if (lstat(dest, &st) == -1) { - return seterror(conf,"can't stat"); - } + n->configuration = ((lval & 1) != 0); + n->oldwasconf = ((lval & 2) != 0); - if (S_ISDIR(st.st_mode)) { - flags = AT_REMOVEDIR; + val = COL("filetype"); + if (!val || strlen(val) == 0) { + seterror(conf, "can't determine file type"); + return 0; } - /* TODO check that expected filetype matches actual filetype */ + n->ftype = *val; - if (conf->verbose) { - fprintf(stderr, "%s(%s)\n", flags ? "rmdir" : "unlink", dest); + /* these can be null */ + n->ohash = COL("ohash"); + n->mds = COL("mds"); + n->omds = COL("omds"); + n->pkglist = COL("pkglist"); + + if (n->ftype == 'r') { + n->hash = COL("hash"); + if (!n->hash) { + seterror(conf, "can't get hash"); + return 0; + } + } else if (n->ftype == 'l') { + n->target = COL("target"); + if (!n->target) { + seterror(conf, "can't get target"); + return 0; + } + if (strlen(n->target) == 0) { + seterror(conf, "zero length target not allowed"); + return 0; + } + n->hash = n->target; } - errno = 0; + if (conf->setuser) { + val = COL("username"); + if (!val) { + seterror(conf, "no username"); + return 0; + } + pw = getpwnam(val); + if (!pw) { + seterror(conf, "no passwd entry"); + return 0; + } + n->uid = pw->pw_uid; + } else { + n->uid = geteuid(); + } - if (unlinkat(AT_FDCWD, dest, flags) == -1) { - switch (errno) { - case ENOENT: - break; - default: - return seterror(conf, "can't unlink"); + if (conf->setgroup) { + val = COL("groupname"); + if (!val) { + seterror(conf, "no groupname"); + return 0; + } + gr = getgrnam(val); + if (!gr) { + seterror(conf, "no group entry for %s", val); + return 0; } + n->gid = gr->gr_gid; + } else { + n->gid = getegid(); } - - return 0; -} -#define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__) + errno = 0; + double mtime = strtod(COL("mtime"),NULL); + if (errno) { + mtime = (double)time(NULL); + } + + n->mtime = (time_t)mtime; + + n->times[0].tv_sec = 0; + n->times[0].tv_nsec = UTIME_OMIT; + n->times[1].tv_sec = (time_t)llrint(floor(mtime)); + n->times[1].tv_nsec = lrint(floor(fmod(mtime,1.0)*1000000000)); + + return 1; +} /* file does not exist */ #define D_NOEXIST 0x1 @@ -437,149 +493,154 @@ static unsigned int file_compare(struct nitem *n, struct stat *st) { return diff; } -static int read_item(struct config *conf, int ncols, char **vals, char **cols, - struct nitem *n) { - char *val; - long lval; - struct passwd *pw; - struct group *gr; - struct nitem zero = { 0 }; - *n = zero; +/* 0 = not acceptable + * 1 = accept and create/update/remove + * 2 = accept as is + * 3 = remove and overwrite + * 4 = update metadata + */ +static int acceptable(struct config *conf, unsigned int diffs, int op) { + int exist = (!(diffs & D_NOEXIST)); + int sametype = (!(diffs & D_TYPE)); + int mdsame = (!(diffs & D_MD)); + int hashsame = (!(diffs & D_HASH)); + int isdir = (diffs & D_ISDIR); - val = COL("op"); - if (!val) { - seterror(conf, "can't determine op"); - return 0; - } - n->opstr = val; - n->op = getop(val); - if (!n->op) { - seterror(conf, "can't determine op"); - return 0; + if (!exist) { + return op == OP_REMOVE ? 2 : 1; } - n->path = COL("path"); - if (!n->path) { - seterror(conf, "no file path"); - return 0; + if (op == OP_UPDATE) { + return sametype ? 4 : 3; } - if (strlen(n->path) == 0) { - seterror(conf, "zero length path not allowed"); - return 0; + + if (op == OP_REMOVE) { + return 1; } - /* TODO config to dishonor setuid/setgid */ - n->dest = COL("dest"); - if (!n->dest) { - seterror(conf, "no file dest"); - return 0; + /* the hard cases, should be installing new, but already exists */ + + if (!sametype) { + return conf->overwrite ? 3 : 0; } - if (strlen(n->dest) == 0) { - seterror(conf, "zero length dest not allowed"); - return 0; + if (mdsame && (conf->accept || conf->overwrite)) { + return 1; } - val = COL("mode"); + if (isdir) { + if (mdsame || conf->ignoredirmd) { + return conf->acceptdir ? 2 : 0; + } + if (conf->overwrite) { + return 4; + } + } - if (!val) { - seterror(conf, "can't determine mode"); - return 0; + if (hashsame && (conf->accept || conf->overwrite)) { + return 1; } - n->mode = strtoul(val, NULL, 8); + return conf->overwrite ? 3 : 0; +} - val = COL("configuration"); - if (!val) { - seterror(conf, "can't determine config status"); - return 0; +static int check_existing(void *f, int ncols, char **vals, char **cols) { + struct config *conf = f; + struct stat st; + struct nitem nitem; + + if (!read_item(conf, ncols, vals, cols, &nitem)) { + fprintf(stderr, "can't read item\n"); + return conf->errabort; } - lval = strtol(val, NULL, 10); - n->configuration = ((lval & 1) != 0); - n->oldwasconf = ((lval & 2) != 0); + if (conf->verbose > 1) { + fprintf(stderr, "check for existing %s\n", nitem.path); + } - val = COL("filetype"); - if (!val || strlen(val) == 0) { - seterror(conf, "can't determine file type"); + if (lstat(nitem.path, &st) == -1) { + switch(errno) { + /* not an error, file shouldn't exist*/ + case ENOENT: break; + default: + fprintf(stderr, "unable to check %s: %s\n", + nitem.path, strerror(errno)); + conf->errors++; + break; + } return 0; } - n->ftype = *val; - /* these can be null */ - n->ohash = COL("ohash"); - n->mds = COL("mds"); - n->omds = COL("omds"); - n->pkglist = COL("pkglist"); + unsigned int diffs = file_compare(&nitem, &st); + if (diffs >= D_ERROR) { + return seterror(conf, "can't check %s", nitem.dest); + } - if (n->ftype == 'r') { - n->hash = COL("hash"); - if (!n->hash) { - seterror(conf, "can't get hash"); - return 0; - } - } else if (n->ftype == 'l') { - n->target = COL("target"); - if (!n->target) { - seterror(conf, "can't get target"); - return 0; - } - if (strlen(n->target) == 0) { - seterror(conf, "zero length target not allowed"); - return 0; + int action = acceptable(conf, diffs, nitem.op); + if (!action) { + if (conf->accept) { + fprintf(stderr, "%s exists and is not acceptable\n", nitem.path); + } else { + fprintf(stderr, "%s exists\n", nitem.path); } - n->hash = n->target; + conf->errors++; } - if (conf->setuser) { - val = COL("username"); - if (!val) { - seterror(conf, "no username"); - return 0; - } - pw = getpwnam(val); - if (!pw) { - seterror(conf, "no passwd entry"); - return 0; + return 0; +} + +static int remove_files(void *f, int ncols, char **vals, char **cols) { + struct config *conf = f; + char *dest; + struct stat st; + int flags = 0; + + dest = COL("dest"); + if (!dest) return seterror(conf,"no file dest"); + + if (conf->dryrun) { + char *ftype = COL("filetype"); + int t = *ftype; + + switch(t) { + case 'd': printf("rmdir %s\n", dest); break; + default: printf("unlink %s\n", dest); break; } - n->uid = pw->pw_uid; - } else { - n->uid = geteuid(); + fflush(stdout); + return 0; } - if (conf->setgroup) { - val = COL("groupname"); - if (!val) { - seterror(conf, "no groupname"); - return 0; - } - gr = getgrnam(val); - if (!gr) { - seterror(conf, "no group entry for %s", val); - return 0; - } - n->gid = gr->gr_gid; - } else { - n->gid = getegid(); + if (lstat(dest, &st) == -1) { + return seterror(conf,"can't stat"); } - errno = 0; - double mtime = strtod(COL("mtime"),NULL); - if (errno) { - mtime = (double)time(NULL); + if (S_ISDIR(st.st_mode)) { + flags = AT_REMOVEDIR; } + /* TODO check that expected filetype matches actual filetype */ - n->mtime = (time_t)mtime; + if (conf->verbose) { + fprintf(stderr, "%s(%s)\n", flags ? "rmdir" : "unlink", dest); + } - n->times[0].tv_sec = 0; - n->times[0].tv_nsec = UTIME_OMIT; - n->times[1].tv_sec = (time_t)llrint(floor(mtime)); - n->times[1].tv_nsec = lrint(floor(fmod(mtime,1.0)*1000000000)); + errno = 0; - return 1; + if (unlinkat(AT_FDCWD, dest, flags) == -1) { + switch (errno) { + case ENOENT: + break; + default: + return seterror(conf, "can't unlink"); + } + } + + return 0; } +#define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__) + + static int remove_dir(struct config *conf, char *path) { int rv; @@ -917,7 +978,7 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { int hashsame = (!(diffs & D_HASH)); int isdir = (diffs & D_ISDIR); int eisdir = (diffs & D_EISDIR); - int accept = conf->absorb; + int accept = conf->accept; int overwrite = conf->overwrite; int installing = (nitem.op == OP_NEW); update = (nitem.op == OP_UPDATE); @@ -1017,6 +1078,15 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { return 0; } + if (mdsame && isdir && conf->acceptdir) { + return 0; + } + + if (!mdsame && isdir && conf->ignoredirmd) { + /* TODO warn ignoring dir md */ + return 0; + } + if (mdsame && hashsame && !(accept || overwrite)) { /* error */ return seterror(conf, "file exists: %s", nitem.dest); @@ -1251,7 +1321,8 @@ int main(int ac, char **av){ conf.rootdir = 0; conf.reverse = 0; conf.overwrite = 0; - conf.absorb = 0; + conf.accept = 0; + conf.acceptdir = 1; if (geteuid() != 0) { conf.setuser = 0; @@ -1278,7 +1349,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:vOA")) != -1) { + while ((opt = getopt(ac, av, "f:d:c:nCR:vOAMD")) != -1) { switch (opt) { case 'd': localdbfile = optarg; break; case 'f': pkgdbfile = optarg; break; @@ -1288,7 +1359,9 @@ int main(int ac, char **av){ 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; + case 'A': conf.accept = 1; break; + case 'M': conf.ignoredirmd = 1; + case 'D': conf.acceptdir = 0; default: usage(); exit(EXIT_FAILURE); @@ -1338,7 +1411,7 @@ int main(int ac, char **av){ /* no point in running it if we're just going to * overwrite everything */ - if (!conf.overwrite && !conf.absorb && !conf.dryrun) { + if (!conf.overwrite && !conf.accept && !conf.dryrun) { runstage(&conf, "new", check_existing); }