From: Matthew Dillon Date: Sat, 25 Apr 2009 18:39:45 +0000 (-0700) Subject: Add cpdup feature - allow uid/gid/flags changes to fail if running as user X-Git-Url: https://gitweb.dragonflybsd.org/~syl/dragonfly.git/commitdiff_plain/c065129d9b559c4dda62925b43d5d08c7c33abd5 Add cpdup feature - allow uid/gid/flags changes to fail if running as user 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. --- diff --git a/bin/cpdup/cpdup.c b/bin/cpdup/cpdup.c index eb5dc8ed3b..99e02db7b9 100644 --- a/bin/cpdup/cpdup.c +++ b/bin/cpdup/cpdup.c @@ -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) diff --git a/bin/cpdup/cpdup.h b/bin/cpdup/cpdup.h index 9e6a8bcbaf..4017008c99 100644 --- a/bin/cpdup/cpdup.h +++ b/bin/cpdup/cpdup.h @@ -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; diff --git a/bin/cpdup/hcproto.c b/bin/cpdup/hcproto.c index 2f812acb2a..a9e4207c5d 100644 --- a/bin/cpdup/hcproto.c +++ b/bin/cpdup/hcproto.c @@ -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