X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/2f03f956f425f03335005ba743f19b6ed3454248..66203a982b3d249bafda9b9272c4c103c19e4a9b:/rsync.c diff --git a/rsync.c b/rsync.c index 13c5f21b..039e1cd5 100644 --- a/rsync.c +++ b/rsync.c @@ -30,7 +30,6 @@ extern int preserve_uid; extern int preserve_gid; extern int preserve_perms; extern int make_backups; -extern char *backup_suffix; /* @@ -57,7 +56,7 @@ int delete_file(char *fname) int ret; extern int recurse; - if (do_unlink(fname) == 0 || errno == ENOENT) return 0; + if (robust_unlink(fname) == 0 || errno == ENOENT) return 0; #if SUPPORT_LINKS ret = do_lstat(fname, &st); @@ -94,7 +93,7 @@ int delete_file(char *fname) if (strcmp(dname,".")==0 || strcmp(dname,"..")==0) continue; - slprintf(buf, sizeof(buf)-1, "%s/%s", fname, dname); + slprintf(buf, sizeof(buf), "%s/%s", fname, dname); if (verbose > 0) rprintf(FINFO,"deleting %s\n", buf); if (delete_file(buf) != 0) { @@ -113,13 +112,46 @@ int delete_file(char *fname) return 0; } +static int is_in_group(gid_t gid) +{ +#ifdef GETGROUPS_T + static gid_t last_in = (gid_t) -2, last_out; + static int ngroups = -2; + static GETGROUPS_T *gidset; + int n; + + if (gid == last_in) + return last_out; + if (ngroups < -1) { + /* treat failure (-1) as if not member of any group */ + ngroups = getgroups(0, 0); + if (ngroups > 0) { + gidset = (GETGROUPS_T *) malloc(ngroups * sizeof(GETGROUPS_T)); + ngroups = getgroups(ngroups, gidset); + } + } + + last_in = gid; + last_out = 0; + for (n = 0; n < ngroups; n++) { + if (gidset[n] == gid) { + last_out = 1; + break; + } + } + return last_out; + +#else + return 0; +#endif +} int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st, int report) { int updated = 0; STRUCT_STAT st2; - extern int am_daemon; + int change_uid, change_gid; if (dry_run) return 0; @@ -133,41 +165,53 @@ int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st, if (preserve_times && !S_ISLNK(st->st_mode) && st->st_mtime != file->modtime) { - updated = 1; - if (set_modtime(fname,file->modtime) != 0) { + /* don't complain about not setting times on directories + because some filesystems can't do it */ + if (set_modtime(fname,file->modtime) != 0 && + !S_ISDIR(st->st_mode)) { rprintf(FERROR,"failed to set times on %s : %s\n", fname,strerror(errno)); return 0; + } else { + updated = 1; } } - if ((am_root || !am_daemon) && - ((am_root && preserve_uid && st->st_uid != file->uid) || - (preserve_gid && st->st_gid != file->gid))) { + change_uid = am_root && preserve_uid && st->st_uid != file->uid; + change_gid = preserve_gid && file->gid != (gid_t) -1 && \ + st->st_gid != file->gid; + if (change_gid && !am_root) { + /* enforce bsd-style group semantics: non-root can only + change to groups that the user is a member of */ + change_gid = is_in_group(file->gid); + } + if (change_uid || change_gid) { if (do_lchown(fname, - (am_root&&preserve_uid)?file->uid:-1, - preserve_gid?file->gid:-1) != 0) { - if (preserve_uid && st->st_uid != file->uid) - updated = 1; - if (verbose>1 || preserve_uid) { - rprintf(FERROR,"chown %s : %s\n", - fname,strerror(errno)); - return 0; - } - } else { - updated = 1; + change_uid?file->uid:st->st_uid, + change_gid?file->gid:st->st_gid) != 0) { + /* shouldn't have attempted to change uid or gid + unless have the privilege */ + rprintf(FERROR,"chown %s : %s\n", fname,strerror(errno)); + return 0; } + /* a lchown had been done - we have to re-stat if the + destination had the setuid or setgid bits set due + to the side effect of the chown call */ + if (st->st_mode & (S_ISUID | S_ISGID)) { + link_stat(fname, st); + } + updated = 1; } #ifdef HAVE_CHMOD - if (preserve_perms && !S_ISLNK(st->st_mode) && - (st->st_mode != file->mode || - (updated && (file->mode & ~ACCESSPERMS)))) { - updated = 1; - if (do_chmod(fname,file->mode) != 0) { - rprintf(FERROR,"failed to set permissions on %s : %s\n", - fname,strerror(errno)); - return 0; + if (!S_ISLNK(st->st_mode)) { + if (st->st_mode != file->mode) { + updated = 1; + if (do_chmod(fname,file->mode) != 0) { + rprintf(FERROR,"failed to set permissions on %s : %s\n", + fname,strerror(errno)); + return 0; + } } } #endif @@ -184,7 +228,7 @@ int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st, void sig_int(void) { - exit_cleanup(1); + exit_cleanup(RERR_SIGNAL); } @@ -192,40 +236,26 @@ void sig_int(void) and ownership */ void finish_transfer(char *fname, char *fnametmp, struct file_struct *file) { - if (make_backups) { - char fnamebak[MAXPATHLEN]; - if (strlen(fname) + strlen(backup_suffix) > (MAXPATHLEN-1)) { - rprintf(FERROR,"backup filename too long\n"); - return; - } - slprintf(fnamebak,sizeof(fnamebak)-1,"%s%s",fname,backup_suffix); - if (do_rename(fname,fnamebak) != 0 && errno != ENOENT) { - rprintf(FERROR,"rename %s %s : %s\n",fname,fnamebak,strerror(errno)); - return; - } - } + if (make_backups && !make_backup(fname)) + return; /* move tmp file over real file */ - if (do_rename(fnametmp,fname) != 0) { + if (robust_rename(fnametmp,fname) != 0) { if (errno == EXDEV) { /* rename failed on cross-filesystem link. Copy the file instead. */ - if (copy_file(fnametmp,fname, file->mode & ACCESSPERMS)) { + if (copy_file(fnametmp,fname, file->mode & INITACCESSPERMS)) { rprintf(FERROR,"copy %s -> %s : %s\n", fnametmp,fname,strerror(errno)); } else { set_perms(fname,file,NULL,0); } - do_unlink(fnametmp); } else { rprintf(FERROR,"rename %s -> %s : %s\n", fnametmp,fname,strerror(errno)); - do_unlink(fnametmp); } + do_unlink(fnametmp); } else { set_perms(fname,file,NULL,0); } } - - -