X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=zpm-syncfs.c;h=ead450e3d182e7e929aaaee8f4d93e55996c87d4;hb=6b5150de70d70d49360fb3804af8136f442dffc1;hp=958fde80535b90e611366dbe8c6ab89c4c64ad5c;hpb=e3f65cc56eb4a6fc6549b1b49d1cde2edda36703;p=zpackage diff --git a/zpm-syncfs.c b/zpm-syncfs.c index 958fde8..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 @@ -25,9 +26,10 @@ struct config { struct zpm *src; char *dbfile; char *rootdir; - int errabort, errors, verbose, dryrun; + 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) { @@ -137,14 +199,26 @@ static int getop(char *opstr) { static int report_conflicts(void *f, int ncols, char **vals, char **cols) { struct config *conf = f; - char *path; - char *pkg; + char *path, *hash, *pkg, *conflict_type, *mds; pkg = COL("pkgid"); path = COL("path"); + conflict_type = COL("conflict"); + if (!strcmp(conflict_type, "hash")) { + hash = COL("hash"); + fprintf(stderr, "hash conflict: package %s path %s hash %.8s\n", + pkg, path, hash); + } else + if (!strcmp(conflict_type, "md")) { + mds = COL("mds"); + fprintf(stderr, "md conflict: package %s path %s md %s\n", + pkg, path, mds); + } else { + fprintf(stderr, "%s conflict: package %s path %s\n", + conflict_type, pkg, path); + } - conf->errors++; - fprintf(stderr, "%s owned by %s\n", path, pkg); + conf->conflicts++; return 0; } @@ -255,6 +329,59 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) { return 0; } +#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; @@ -336,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 (diffs > 1) { + return seterror(conf, "absorb and overwrite not implemented"); } - if (ftype == 'd') { + 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) { @@ -371,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); @@ -380,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); } } @@ -400,6 +539,65 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { return 0; } +static void check_conflicts(struct config *conf, char *conflict_type, + int (callback)(void *, int, char **, char **)) { + int rv; + char *errmsg; + sqlite3_str *s; + char *sql; + + s = sqlite3_str_new(conf->log->db); + sqlite3_str_appendall(s, "select *, "); + if (conf->rootdir) { + sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir); + } else { + sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))"); + } + sqlite3_str_appendall(s, " as dest from syncconflicts"); + + if (conflict_type) { + sqlite3_str_appendf(s," where conflict = %Q", conflict_type); + } + if (conf->reverse) { + sqlite3_str_appendall(s," order by length(path) desc, path desc,pkgid collate vercmp desc, conflict desc"); + } else { + sqlite3_str_appendall(s," order by length(path), path, pkgid collate vercmp, conflict"); + + } + + sql = sqlite3_str_value(s); + if (conf->verbose > 2) { + fprintf(stderr, "stage query: %s\n", sql); + } + + rv = zpm_exec(conf->log, sql, callback, conf, &errmsg); + + sqlite3_str_finish(s); + + if (rv) { + fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv)); + if (errmsg) { + fprintf(stderr, "database error: %s\n", errmsg); + conf->errors++; + } + if (conf->log->error == 1) { + fprintf(stderr, "unable to allocate memory\n"); + } + fprintf(stderr, "zpm_exec failure: %s\n", + conf->log->errmsg ? conf->log->errmsg : "unknown"); + conf->errors++; + } + if (conf->log->errmsg) { + fprintf(stderr, "error: %s\n", conf->log->errmsg); + } + if (conf->errors && conf->exitonerror) { + zpm_close(conf->log); + zpm_close(conf->src); + exit(EXIT_FAILURE); + } + /* TODO final report function in conf var */ +} + static void runstage(struct config *conf, char *stage, int (callback)(void *, int, char **, char **)) { int rv; @@ -414,7 +612,7 @@ static void runstage(struct config *conf, char *stage, } else { sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))"); } - sqlite3_str_appendall(s, " as dest from install_status"); + sqlite3_str_appendall(s, " as dest from syncinfo"); if (stage) { sqlite3_str_appendf(s," where op = %Q", stage); @@ -467,6 +665,7 @@ int main(int ac, char **av){ conf.errabort = 1; conf.errors = 0; + conf.conflicts = 0; conf.verbose = 0; conf.dryrun = 0; conf.setuser = 1; @@ -475,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; @@ -501,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; @@ -510,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); @@ -548,16 +751,27 @@ int main(int ac, char **av){ conf.errors = 0; conf.exitonerror = 0; - runstage(&conf, "conflict", report_conflicts); - runstage(&conf, "new", check_existing); - - if (!conf.errors) { - conf.exitonerror = 1; - runstage(&conf, "new", install_files); - runstage(&conf, "update", update_files); - conf.reverse = 1; - runstage(&conf, "remove", remove_files); - conf.reverse = 0; + check_conflicts(&conf, NULL, report_conflicts); + if (conf.conflicts) { + fprintf(stderr, "%d conflicts reported, aborting sync\n", + conf.conflicts); + conf.errors++; + } else { + /* 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; + runstage(&conf, "new", install_files); + runstage(&conf, "update", update_files); + conf.reverse = 1; + runstage(&conf, "remove", remove_files); + conf.reverse = 0; + } } zpm_close(&localdb);