+ /* TODO config to dishonor setuid/setgid */
+ n->dest = COL("dest");
+ if (!n->dest) {
+ seterror(conf, "no file dest");
+ return 0;
+ }
+
+ if (strlen(n->dest) == 0) {
+ seterror(conf, "zero length dest not allowed");
+ return 0;
+ }
+
+ val = COL("mode");
+
+ if (!val) {
+ seterror(conf, "can't determine mode");
+ return 0;
+ }
+
+ n->mode = strtoul(val, NULL, 8);
+
+ val = COL("configuration");
+ if (!val) {
+ seterror(conf, "can't determine config status");
+ return 0;
+ }
+ lval = strtol(val, NULL, 10);
+
+ n->configuration = ((lval & 1) != 0);
+ n->oldwasconf = ((lval & 2) != 0);
+
+ val = COL("filetype");
+ if (!val || strlen(val) == 0) {
+ seterror(conf, "can't determine file type");
+ 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");
+
+ 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;
+ }
+
+ 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 (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();
+ }
+
+ 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
+/* files are different types */
+#define D_TYPE 0x2
+/* metadata is different */
+#define D_MD 0x4
+/* content or link target is different */
+#define D_HASH 0x8
+/* file to be installed is a directory */
+#define D_ISDIR 0x10
+/* path on disk is a directory */
+#define D_EISDIR 0x20
+/* usernames different */
+#define D_UID 0x40
+/* group names different */
+#define D_GID 0x80
+/* file mode is different */
+#define D_MODE 0x100
+/* mtimes are different */
+#define D_MTIME 0x200
+/* the hash of the file we are supposedly replacing is different than
+ * the the hash of the file on disk
+ */
+#define D_OHASH 0x400
+/* an error occurred trying to compare the file (other than it doesn't exist */
+#define D_ERROR 0x1000
+/* there was a stat error */
+#define D_STATERROR 0x2000
+/* there was an error calling readlink */
+#define D_RLERROR 0x4000
+
+/* 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(struct nitem *n, struct stat *st) {
+ int etype = 0, stat_type;
+ char ehash[ZPM_HASH_STRLEN+1];
+ unsigned int diff = 0;
+ char link[1024];
+ ssize_t lsize;
+
+ switch (n->ftype) {
+ case 'd': etype = S_IFDIR; diff |= D_ISDIR ; break;
+ case 'r': etype = S_IFREG; break;
+ case 'l': etype = S_IFLNK; break;
+ default: etype = 0; break;
+ }
+
+ errno = 0;
+ /* new file, so check type, hash, etc */
+ if (lstat(n->dest, st) == 0) {
+ stat_type = st->st_mode & S_IFMT;
+ if (stat_type != etype) {
+ diff |= D_TYPE;
+ }
+ if (stat_type == S_IFDIR) {
+ diff |= D_EISDIR;
+ }
+
+ if (n->hash && etype == S_IFREG && stat_type == S_IFREG) {
+ zpm_hash(n->dest, ehash, 0);
+ if (strcmp(n->hash, ehash) != 0) {
+ diff |= D_HASH;
+ }
+ if (n->ohash && strcmp(n->ohash, ehash) != 0) {
+ diff |= D_OHASH;
+ }
+ }
+ if (n->hash && etype == S_IFLNK && stat_type == S_IFLNK) {
+ lsize = readlink(n->dest, link, sizeof link);
+
+ if (lsize == -1 || lsize == sizeof link) {
+ diff |= D_RLERROR;
+ diff |= D_ERROR;
+ } else {
+ link[lsize] = 0;
+ if (strcmp(n->target, link) != 0) {
+ diff |= D_HASH;
+ }
+ }
+ }
+ if (n->uid != st->st_uid) {
+ diff |= D_UID;
+ diff |= D_MD;
+ }
+ if (n->gid != st->st_gid) {
+ diff |= D_GID;
+ diff |= D_MD;
+ }
+ if (n->mode != (st->st_mode & 07777)) {
+ diff |= D_MODE;
+ diff |= D_MD;
+ }