Add cpdup feature - allow uid/gid/flags changes to fail if running as user
authorMatthew Dillon <dillon@apollo.backplane.com>
Sat, 25 Apr 2009 18:39:45 +0000 (11:39 -0700)
committerJordan Gordeev <jgordeev@dir.bg>
Sun, 26 Apr 2009 19:18:10 +0000 (22:18 +0300)
If running as a user instead of root uid, gid, and flags changes are allowed
to fail and also, if running as a user, no longer force a copy if they
differ but the mtime and size are the same.  Generate a single warning
instead.

Reorder the call to setutimes to occur after chown/chmod instead of before,
and to occur after a chflags call if IMMUTABLE is not set.

bin/cpdup/cpdup.c
bin/cpdup/cpdup.h
bin/cpdup/hcproto.c

index eb5dc8e..99e02db 100644 (file)
@@ -147,6 +147,8 @@ int ValidateOpt;
 int CurParallel;
 int MaxParallel = -1;
 int HardLinkCount;
+int RunningAsUser;
+int RunningAsRoot;
 const char *UseCpFile;
 const char *UseHLPath;
 const char *MD5CacheFile;
@@ -180,6 +182,9 @@ main(int ac, char **av)
 
     signal(SIGPIPE, SIG_IGN);
 
+    RunningAsUser = (geteuid() != 0);
+    RunningAsRoot = !RunningAsUser;
+
 #if USE_PTHREADS
     for (i = 0; i < HCTHASH_SIZE; ++i) {
        pthread_mutex_init(&SrcHost.hct_mutex[i], NULL);
@@ -522,9 +527,9 @@ checkHLPath(struct stat *st1, const char *spath, const char *dpath)
      */
     if (hc_stat(&DstHost, hpath, &sthl) < 0 ||
        st1->st_size != sthl.st_size ||
-       st1->st_uid != sthl.st_uid ||
-       st1->st_gid != sthl.st_gid ||
-       st1->st_mtime != sthl.st_mtime
+       st1->st_mtime != sthl.st_mtime ||
+       (RunningAsRoot && (st1->st_uid != sthl.st_uid ||
+                          st1->st_gid != sthl.st_gid))
     ) {
        free(hpath);
        return(NULL);
@@ -626,6 +631,7 @@ DoCopy(copy_info_t info, int depth)
     dev_t ddevNo = info->ddevNo;
     struct stat st1;
     struct stat st2;
+    unsigned long st2_flags;
     int r, mres, fres, st2Valid;
     struct hlink *hln;
     List *list = malloc(sizeof(List));
@@ -633,6 +639,7 @@ DoCopy(copy_info_t info, int depth)
 
     InitList(list);
     r = mres = fres = st2Valid = 0;
+    st2_flags = 0;
     size = 0;
     hln = NULL;
 
@@ -642,8 +649,12 @@ DoCopy(copy_info_t info, int depth)
     }
     st2.st_mode = 0;   /* in case lstat fails */
     st2.st_flags = 0;  /* in case lstat fails */
-    if (dpath && hc_lstat(&DstHost, dpath, &st2) == 0)
+    if (dpath && hc_lstat(&DstHost, dpath, &st2) == 0) {
        st2Valid = 1;
+#ifdef _ST_FLAGS_PRESENT_
+       st2_flags = st2.st_flags;
+#endif
+    }
 
     if (S_ISREG(st1.st_mode)) {
        size = st1.st_size;
@@ -737,7 +748,7 @@ relink:
        st2Valid
        && st1.st_mode == st2.st_mode
 #ifdef _ST_FLAGS_PRESENT_
-       && st1.st_flags == st2.st_flags
+       && (RunningAsUser || st1.st_flags == st2.st_flags)
 #endif
     ) {
        if (S_ISLNK(st1.st_mode) || S_ISDIR(st1.st_mode)) {
@@ -762,9 +773,9 @@ relink:
        } else {
            if (ForceOpt == 0 &&
                st1.st_size == st2.st_size &&
-               st1.st_uid == st2.st_uid &&
-               st1.st_gid == st2.st_gid &&
-               st1.st_mtime == st2.st_mtime
+               st1.st_mtime == st2.st_mtime &&
+               (RunningAsUser || (st1.st_uid == st2.st_uid &&
+                                  st1.st_gid == st2.st_gid))
 #ifndef NOMD5
                && (UseMD5Opt == 0 || !S_ISREG(st1.st_mode) ||
                    (mres = md5_check(spath, dpath)) == 0)
@@ -776,20 +787,47 @@ relink:
                && (ValidateOpt == 0 || !S_ISREG(st1.st_mode) ||
                    validate_check(spath, dpath) == 0)
            ) {
+               /*
+                * The files are identical, but if we are not running as
+                * root we might need to adjust ownership/group/flags.
+                */
+               int changedown = 0;
+               int changedflags = 0;
                 if (hln)
                    hltsetdino(hln, st2.st_ino);
+
+               if (RunningAsUser && (st1.st_uid != st2.st_uid ||
+                                     st1.st_gid != st2.st_gid)) {
+                       hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid);
+                       changedown = 1;
+               }
+#ifdef _ST_FLAGS_PRESENT_
+               if (RunningAsUser && st1.st_flags != st2.st_flags) {
+                       hc_chflags(&DstHost, dpath, st1.st_flags);
+                       changedflags = 1;
+               }
+#endif
                if (VerboseOpt >= 3) {
 #ifndef NOMD5
-                   if (UseMD5Opt)
-                       logstd("%-32s md5-nochange\n", (dpath ? dpath : spath));
-                   else
+                   if (UseMD5Opt) {
+                       logstd("%-32s md5-nochange",
+                               (dpath ? dpath : spath));
+                   } else
 #endif
-                   if (UseFSMIDOpt)
-                       logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath));
-                   else if (ValidateOpt)
-                       logstd("%-32s nochange (contents validated)\n", (dpath ? dpath : spath));
-                   else
-                       logstd("%-32s nochange\n", (dpath ? dpath : spath));
+                   if (UseFSMIDOpt) {
+                       logstd("%-32s fsmid-nochange",
+                               (dpath ? dpath : spath));
+                   } else if (ValidateOpt) {
+                       logstd("%-32s nochange (contents validated)",
+                               (dpath ? dpath : spath));
+                   } else {
+                       logstd("%-32s nochange", (dpath ? dpath : spath));
+                   }
+                   if (changedown)
+                       logstd(" (uid/gid differ)");
+                   if (changedflags)
+                       logstd(" (flags differ)");
+                   logstd("\n");
                }
                CountSourceBytes += size;
                CountSourceItems++;
@@ -1152,10 +1190,15 @@ relink:
                    tv[0].tv_sec = st1.st_mtime;
                    tv[1].tv_sec = st1.st_mtime;
 
-                   hc_utimes(&DstHost, path, tv);
                    hc_chown(&DstHost, path, st1.st_uid, st1.st_gid);
                    hc_chmod(&DstHost, path, st1.st_mode);
-                   if (xrename(path, dpath, st2.st_flags) != 0) {
+#ifdef _ST_FLAGS_PRESENT_
+                   if (st1.st_flags & (UF_IMMUTABLE|SF_IMMUTABLE))
+                       hc_utimes(&DstHost, path, tv);
+#else
+                   hc_utimes(&DstHost, path, tv);
+#endif
+                   if (xrename(path, dpath, st2_flags) != 0) {
                        logerr("%-32s rename-after-copy failed: %s\n",
                            (dpath ? dpath : spath), strerror(errno)
                        );
@@ -1168,6 +1211,10 @@ relink:
                            hc_chflags(&DstHost, dpath, st1.st_flags);
 #endif
                    }
+#ifdef _ST_FLAGS_PRESENT_
+                   if ((st1.st_flags & (UF_IMMUTABLE|SF_IMMUTABLE)) == 0)
+                       hc_utimes(&DstHost, dpath, tv);
+#endif
                    CountSourceReadBytes += size;
                    CountWriteBytes += size;
                    CountSourceBytes += size;
@@ -1234,7 +1281,7 @@ skip_copy:
                     * there is no lchmod() or lchflags(), we 
                     * cannot chmod or chflags a softlink.
                     */
-                   if (xrename(path, dpath, st2.st_flags) != 0) {
+                   if (xrename(path, dpath, st2_flags) != 0) {
                        logerr("%-32s rename softlink (%s->%s) failed: %s\n",
                            (dpath ? dpath : spath),
                            path, dpath, strerror(errno));
@@ -1278,7 +1325,7 @@ skip_copy:
                hc_chmod(&DstHost, path, st1.st_mode);
                hc_chown(&DstHost, path, st1.st_uid, st1.st_gid);
                xremove(&DstHost, dpath);
-               if (xrename(path, dpath, st2.st_flags) != 0) {
+               if (xrename(path, dpath, st2_flags) != 0) {
                    logerr("%-32s dev-rename-after-create failed: %s\n",
                        (dpath ? dpath : spath),
                        strerror(errno)
index 9e6a8bc..4017008 100644 (file)
@@ -48,6 +48,8 @@ extern const char *FSMIDCacheFile;
 extern int SummaryOpt;
 extern int CompressOpt;
 extern int CurParallel;
+extern int RunningAsUser;
+extern int RunningAsRoot;
 
 extern int64_t CountSourceBytes;
 extern int64_t CountSourceItems;
index 2f812ac..a9e4207 100644 (file)
@@ -69,6 +69,31 @@ struct HCDesc HCDispatchTable[] = {
     { HC_UTIMES,       rc_utimes },
 };
 
+static int chown_warning;
+static int chflags_warning;
+
+/*
+ * If not running as root generate a silent warning and return no error.
+ *
+ * If running as root return an error.
+ */
+static int
+silentwarning(int *didwarn, const char *ctl, ...)
+{
+    va_list va;
+
+    if (RunningAsRoot)
+       return(-1);
+    if (*didwarn == 0) {
+       *didwarn = 1;
+       fprintf(stderr, "WARNING: Not running as root, ");
+       va_start(va, ctl);
+       vfprintf(stderr, ctl, va);
+       va_end(va);
+    }
+    return(0);
+}
+
 int
 hc_connect(struct HostConf *hc)
 {
@@ -988,15 +1013,22 @@ rc_rmdir(hctransaction_t trans __unused, struct HCHead *head)
 
 /*
  * CHOWN
+ *
+ * Almost silently ignore chowns that fail if we are not root.
  */
 int
 hc_chown(struct HostConf *hc, const char *path, uid_t owner, gid_t group)
 {
     hctransaction_t trans;
     struct HCHead *head;
+    int rc;
 
-    if (hc == NULL || hc->host == NULL)
-       return(chown(path, owner, group));
+    if (hc == NULL || hc->host == NULL) {
+       rc = chown(path, owner, group);
+       if (rc < 0)
+           rc = silentwarning(&chown_warning, "file ownership may differ\n");
+       return(rc);
+    }
 
     trans = hcc_start_command(hc, HC_CHOWN);
     hcc_leaf_string(trans, LC_PATH1, path);
@@ -1016,6 +1048,7 @@ rc_chown(hctransaction_t trans __unused, struct HCHead *head)
     const char *path = NULL;
     uid_t uid = (uid_t)-1;
     gid_t gid = (gid_t)-1;
+    int rc;
 
     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
        switch(item->leafid) {
@@ -1032,7 +1065,10 @@ rc_chown(hctransaction_t trans __unused, struct HCHead *head)
     }
     if (path == NULL)
        return(-1);
-    return(chown(path, uid, gid));
+    rc = chown(path, uid, gid);
+    if (rc < 0)
+       rc = silentwarning(&chown_warning, "file ownership may differ\n");
+    return(rc);
 }
 
 /*
@@ -1043,9 +1079,14 @@ hc_lchown(struct HostConf *hc, const char *path, uid_t owner, gid_t group)
 {
     hctransaction_t trans;
     struct HCHead *head;
+    int rc;
 
-    if (hc == NULL || hc->host == NULL)
-       return(lchown(path, owner, group));
+    if (hc == NULL || hc->host == NULL) {
+       rc = lchown(path, owner, group);
+       if (rc < 0)
+           rc = silentwarning(&chown_warning, "file ownership may differ\n");
+       return(rc);
+    }
 
     trans = hcc_start_command(hc, HC_LCHOWN);
     hcc_leaf_string(trans, LC_PATH1, path);
@@ -1065,6 +1106,7 @@ rc_lchown(hctransaction_t trans __unused, struct HCHead *head)
     const char *path = NULL;
     uid_t uid = (uid_t)-1;
     gid_t gid = (gid_t)-1;
+    int rc;
 
     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
        switch(item->leafid) {
@@ -1081,7 +1123,10 @@ rc_lchown(hctransaction_t trans __unused, struct HCHead *head)
     }
     if (path == NULL)
        return(-1);
-    return(lchown(path, uid, gid));
+    rc = lchown(path, uid, gid);
+    if (rc < 0)
+       rc = silentwarning(&chown_warning, "file ownership may differ\n");
+    return(rc);
 }
 
 /*
@@ -1230,9 +1275,20 @@ hc_chflags(struct HostConf *hc, const char *path, u_long flags)
 {
     hctransaction_t trans;
     struct HCHead *head;
+    int rc;
 
-    if (hc == NULL || hc->host == NULL)
-       return(chflags(path, flags));
+    if (hc == NULL || hc->host == NULL) {
+       rc = chflags(path, flags);
+       if (rc < 0) {
+           if (RunningAsUser) {
+               flags &= UF_SETTABLE;
+               rc = chflags(path, flags);
+           }
+           if (rc < 0)
+               rc = silentwarning(&chflags_warning, "file flags may differ\n");
+       }
+       return (rc);
+    }
 
     trans = hcc_start_command(hc, HC_CHFLAGS);
     hcc_leaf_string(trans, LC_PATH1, path);
@@ -1250,6 +1306,7 @@ rc_chflags(hctransaction_t trans __unused, struct HCHead *head)
     struct HCLeaf *item;
     const char *path = NULL;
     u_long flags = 0;
+    int rc;
 
     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
        switch(item->leafid) {
@@ -1263,7 +1320,16 @@ rc_chflags(hctransaction_t trans __unused, struct HCHead *head)
     }
     if (path == NULL)
        return(-2);
-    return(chflags(path, flags));
+    rc = chflags(path, flags);
+    if (rc < 0) {
+       if (RunningAsUser) {
+           flags &= UF_SETTABLE;
+           rc = chflags(path, flags);
+       }
+       if (rc < 0)
+           rc = silentwarning(&chflags_warning, "file flags may differ\n");
+    }
+    return(rc);
 }
 
 #endif