From: Nathan Wagner Date: Wed, 5 Dec 2018 10:57:47 +0000 (+0000) Subject: improve config file handling X-Git-Tag: v0.3.6~9 X-Git-Url: https://pd.if.org/git/?p=zpackage;a=commitdiff_plain;h=c6358b8aba981d20b80d233e4d2edb79c802502b improve config file handling --- diff --git a/t/update.t b/t/update.t index 2606f70..e97835d 100755 --- a/t/update.t +++ b/t/update.t @@ -95,12 +95,21 @@ require zpm install -R. -f itest-2.0-1.zpm itest-2.0-1 conf=$(cat etc/conf) okstreq "$conf" bar new conf is bar +zpm note -f var/lib/zpm/local.db -l | diagstdin + echo quux > etc/conf +qhash=$(printf '%.8s' $(zpm hash etc/conf)) require zpm install -R. -f itest-3.0-1.zpm itest-3.0-1 conf=$(cat etc/conf) -okstreq "$conf" quux kept conf as quux +okstreq "$conf" quux kept conf $qhash as quux + +zpm note -f var/lib/zpm/local.db -l | diagstdin + +ZPMDB=$PWD/var/lib/zpm/local.db +export ZPMDB +require zpm uninstall -R. itest zpm note -f var/lib/zpm/local.db -l | diagstdin cd .. diff --git a/zpm-syncfs.c b/zpm-syncfs.c index e3d9dbb..beff560 100644 --- a/zpm-syncfs.c +++ b/zpm-syncfs.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,15 @@ static void usage() { printf("usage: zpm $scriptname [-fncC] args ...\n"); } +static void warn(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + static void pdots(int len, int ch, int was, int now, int total) { was = len * was / total; if (now > total) { @@ -431,6 +441,8 @@ static int read_item(struct config *conf, int ncols, char **vals, char **cols, * the the hash of the file on disk */ #define D_OHASH 0x400 +/* file exists, and is a directory, and is empty */ +#define D_ISEMPTY 0x800 /* an error occurred trying to compare the file (other than it doesn't exist */ #define D_ERROR 0x1000 /* there was a stat error */ @@ -438,6 +450,27 @@ static int read_item(struct config *conf, int ncols, char **vals, char **cols, /* there was an error calling readlink */ #define D_RLERROR 0x4000 +static int dir_is_empty(char *path) { + DIR *dir; + struct dirent *dp; + int empty; + + dir = opendir(path); + if (!dir) { + return -1; + } + + dp = readdir(dir); + if (dp) { + empty = 0; + } else { + empty = 1; + } + closedir(dir); + + return empty; +} + /* 1 = file doesn't exist, 2 = file is a directory, target isn't */ /* 4 == ftype different */ /* 8 = hash different when both are regular files */ @@ -464,6 +497,9 @@ static unsigned int file_compare(struct nitem *n, struct stat *st) { } if (stat_type == S_IFDIR) { diff |= D_EISDIR; + if (dir_is_empty(n->dest)) { + diff |= D_ISEMPTY; + } } if (n->hash && etype == S_IFREG && stat_type == S_IFREG) { @@ -590,10 +626,16 @@ static int check_existing(void *f, int ncols, char **vals, char **cols) { } unsigned int diffs = file_compare(&nitem, &st); + int sametype = (!(diffs & D_TYPE)); + if (diffs >= D_ERROR) { return seterror(conf, "can't check %s", nitem.dest); } + if (sametype && nitem.configuration) { + return 0; + } + int action = acceptable(conf, diffs, nitem.op); if (!action) { if (conf->accept) { @@ -660,7 +702,6 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) { } /* TODO check that expected filetype matches actual filetype */ - errno = 0; if (unlinkat(AT_FDCWD, dest, flags) == -1) { @@ -681,7 +722,6 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) { #define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__) - static int remove_dir(struct config *conf, char *path) { int rv; @@ -775,7 +815,7 @@ static int install(struct config *conf, struct nitem *item, unsigned int flags) int failure = conf->errabort; int success = 0; - if (flags & 16) { + if (flags & INS_RTF) { failure = 0; success = 1; } @@ -854,6 +894,24 @@ static int install(struct config *conf, struct nitem *item, unsigned int flags) return success; } +static int save_config_file(struct config *conf, struct nitem *n, char *msgfmt) { + char hash[ZPM_HASH_STRLEN+1]; + + if (!msgfmt) { + msgfmt = "saved config file %.8s"; + } + + if (zpm_import(conf->log, n->dest, 0, hash)) { + zpm_note_add(conf->log, n->pkglist, n->path, hash, msgfmt, hash); + } else { + warn("unable to import existing config file %s", n->dest); + conf->errors++; + return 0; + } + return 1; +} + +#if 0 /* * figure out what the difference is for a config file, only called * for an update of a configuration file @@ -861,67 +919,33 @@ static int install(struct config *conf, struct nitem *item, unsigned int flags) * return 1 if the new file should not be installed * return 0 if the new file should be installed */ -static int adjust_for_config(struct config *conf, struct nitem *n, unsigned int - diffs) { +static int adjust_for_config(struct nitem *n, unsigned int diffs) { if (!n->oldwasconf) { return 0; } + + int sametype = (!(diffs & D_TYPE)); + int isdir = (diffs & D_ISDIR); + int eisdir = (diffs & D_EISDIR); /* TODO what if old was a directory? */ if (!n->configuration) { /* replacing conf with non-conf */ /* absorb file, mark todo */ - char hash[ZPM_HASH_STRLEN+1]; - if (!conf->dryrun) { - if (conf->verbose) { - fprintf(stderr, "importing old conf file\n"); - } - if (zpm_import(conf->log, n->dest, 0, hash)) { - zpm_note_add(conf->log, n->pkglist, n->dest, hash, - "replaced config file with non-config. zpm-cat %.8s", hash); - } else { - fprintf(stderr, "unable to import existing config file %s\n", n->dest); - return -1; - } - } else { - fprintf(stderr, "dry-run: would replace config file %s with non-config file\n", n->dest); - } return 0; } - int sametype = (!(diffs & D_TYPE)); - int isdir = (diffs & D_ISDIR); - int eisdir = (diffs & D_EISDIR); - /* both old and new are config files */ if (isdir && sametype) { /* both config directories, can only be changing * metadata, so no adjustment needed */ - if (conf->verbose) { - fprintf(stderr, "both config dirs, ok to update\n"); - } return 0; } if (isdir) { - char hash[ZPM_HASH_STRLEN+1]; - - /* replacing old file with new directory */ - /* absorb, make note */ - if (!conf->dryrun) { - if (zpm_import(conf->log, n->dest, 0, hash)) { - zpm_note_add(conf->log, n->pkglist, n->dest, hash, - "replaced config file with config directory. zpm-cat %.8s", hash); - } else { - fprintf(stderr, "unable to import existing config file %s\n", n->dest); - return -1; - } - } else { - fprintf(stderr, "dry-run: would replace config file %s with config directory\n", n->dest); - } - return 0; + return 0; } if (eisdir) { @@ -936,37 +960,185 @@ static int adjust_for_config(struct config *conf, struct nitem *n, unsigned int /* replacing old file with new file */ /* new is same as on disk */ if (!(diffs & D_HASH)) { - if (conf->verbose) { - fprintf(stderr, "new config file is already on disk, probably shouldn't happen\n"); - } return 0; } /* new is different than on disk, but on disk is same as old */ if (!(diffs & D_OHASH)) { - if (conf->verbose) { - fprintf(stderr, "old config file not changed from default, replacing with new default\n"); - } - /* ok to do the update, since same as default */ - fprintf(stderr, "updating default config %s\n", n->dest); return 0; } - /* new is different than on disk, and disk different than old */ - /* log */ - if (conf->verbose) { - fprintf(stderr, "new default config file is different than on disk, and old default was changed, should keep on-disk config\n"); + return 1; + +} +#endif + +static int config_handler(void *f, int ncols, char **vals, char **cols) { + struct config *conf = f; + struct nitem nitem; + struct stat existing; + char *save = 0; + char *note = 0; + char *notehash = 0; + int update = 0; + + if (!read_item(conf, ncols, vals, cols, &nitem)) { + fprintf(stderr, "can't read item\n"); + conf->errors++; + return conf->errabort; } + + unsigned int diffs = file_compare(&nitem, &existing); + if (diffs >= D_ERROR) { + return seterror(conf, "can't check %s", nitem.dest); + } + + int exist = (!(diffs & D_NOEXIST)); + int sametype = (!(diffs & D_TYPE)); + //int mdsame = (!(diffs & D_MD)); + int hashsame = (!(diffs & D_HASH)); + int oldhashsame = (!(diffs & D_OHASH)); + int isdir = (diffs & D_ISDIR); + int eisdir = (diffs & D_EISDIR); + update = (nitem.op == OP_UPDATE); + + notehash = nitem.hash; + + /* if the file doesn't exist in the system, nothing to do */ + /* could possibly note if we expected it, but the regular handling + * should do that + */ + if (!exist) { + return 0; + } + + if (nitem.op == OP_UPDATE && !nitem.oldwasconf) { + /* possibly save anyway */ + return 0; + } + + /* so, old was conf, and something exists in the filesystem */ + + if (!sametype) { + warn("won't %s %s%s, %s exists", + nitem.op == OP_NEW ? "install" : nitem.opstr, + nitem.path, + isdir ? "/" : "", + eisdir ? "directory" : "file" + ); + conf->errors++; + return conf->errabort; + } + + /* all below are same type of file */ + /* what about sametype, but old was different type */ + + if (isdir) { + return 0; + } + + /* save or note cases */ + + if (nitem.op == OP_REMOVE) { + save ="saved removed config file %.8s"; + } else + + if (nitem.op == OP_UPDATE) { + if (!nitem.configuration) { + /* replacing config with non-config */ + save = "replacing configuration file %.8s with non-configuration file"; + } else if (oldhashsame) { + /* config file hasn't changed from old default, + * so go ahead and install the new one + */ + save = "replaced old default config (%.8s) with new one."; + notehash = nitem.ohash; + } else { + note = "kept existing config file. new default version is %.8s"; + save = 0; + } + } else + + if (nitem.op == OP_NEW && !hashsame) { + note = "config file already existed. would have installed %.8s"; + save = 0; + } + + /* + * save files, add notes + */ if (!conf->dryrun) { - zpm_note_add(conf->log, n->pkglist, n->dest, n->hash, - "default config file update. zpm-cat %.8s", n->hash); - /* TODO check for note error */ + if (save) { + warn("saving config file: %s (root %s)", nitem.path, conf->rootdir ? conf->rootdir : "/"); + save_config_file(conf, &nitem, save); + } + if (note) { + zpm_note_add(conf->log, nitem.pkglist, nitem.path, nitem.hash, + note, nitem.hash); + } } else { - fprintf(stderr, "dry-run: default config file %s update\n", - n->dest); + if (save) { + fprintf(stderr, "dry run: %s %s: ", nitem.pkglist, + nitem.path); + warn(save, notehash); + } + if (note) { + fprintf(stderr, "dry run: %s %s: ", nitem.pkglist, + nitem.path); + warn(note, notehash); + } + } - return 1; + return 0; +} + +static void handle_config_files(struct config *conf) { + 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 syncinfo"); + + sqlite3_str_appendall(s," where configuration > 0"); + + if (conf->reverse) { + sqlite3_str_appendall(s," order by length(path) desc, path desc"); + } + + sql = sqlite3_str_value(s); + + rv = zpm_exec(conf->log, sql, config_handler, 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->errors && conf->exitonerror) { + zpm_close(conf->log); + zpm_close(conf->src); + exit(EXIT_FAILURE); + } } static int install_files(void *f, int ncols, char **vals, char **cols) { @@ -1037,6 +1209,7 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { int sametype = (!(diffs & D_TYPE)); int mdsame = (!(diffs & D_MD)); int hashsame = (!(diffs & D_HASH)); + int ohashsame = (!(diffs & D_OHASH)); int isdir = (diffs & D_ISDIR); int eisdir = (diffs & D_EISDIR); int accept = conf->accept; @@ -1044,6 +1217,27 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { int installing = (nitem.op == OP_NEW); update = (nitem.op == OP_UPDATE); + /* if a config file doesn't exist on disk, go ahead and do + * whatever you were going to do, this logic here just + * needs to determine if we should skip what we were going to do + * + * if the old item was a configuration item and the new one isn't, it + * will have been saved earlier, so we can just go ahead. so we only + * test for an existing file, where the item is a configuration file + */ + if (nitem.configuration && exist) { + if (!sametype && !conf->overwrite) { + return seterror(conf, "configuration file exists with different type: %s", nitem.dest); + } + + if (conf->accept) { + return 0; + } + + if (isdir && !mdsame) return 0; + if (!isdir && !ohashsame) return 0; + } + if (update) { if (!exist) { /* warn, it should exist */ @@ -1051,14 +1245,6 @@ static int install_files(void *f, int ncols, char **vals, char **cols) { return install(conf, &nitem, 3); } - switch (adjust_for_config(conf, &nitem, diffs)) { - case -1: return conf->errabort; break; - case 1: - fprintf(stderr, "skipping changed default config file: %s\n", nitem.dest); - return 0; break; - default: break; - } - /* file exists in filesystem */ if (sametype) { if (mdsame && hashsame) { @@ -1488,6 +1674,11 @@ int main(int ac, char **av) { fprintf(stderr, "beginning %ssync\n", conf.dryrun ? "dryrun " : ""); } + + if (!conf.errors) { + handle_config_files(&conf); + } + /* have to do the removes first otherwise * old files may conflict with update file * type changes