X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/5865fcdd6370cef7d3a4997500a9a28f3114ad7f..c27f25922e9f59e439228a3c3e0c38342899a80f:/rsync.c diff --git a/rsync.c b/rsync.c index 3f79f504..d7f2d3ce 100644 --- a/rsync.c +++ b/rsync.c @@ -113,12 +113,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; + int change_uid, change_gid; extern int am_daemon; if (dry_run) return 0; @@ -145,33 +179,40 @@ int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st, } } - 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 = !am_daemon && 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:st->st_uid, - preserve_gid?file->gid:st->st_gid) != 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; } + 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)) { + int file_mode; + if (preserve_perms) + file_mode = file->mode; + else + file_mode = file->mode & ACCESSPERMS; + 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 @@ -201,7 +242,8 @@ int make_backup(char *fname) slprintf(fnamebak,sizeof(fnamebak),"%s%s",fname,backup_suffix); if (do_rename(fname,fnamebak) != 0) { - if (errno != ENOENT) { + /* cygwin (at least version b19) reports EINVAL */ + if (errno != ENOENT && errno != EINVAL) { rprintf(FERROR,"rename %s %s : %s\n",fname,fnamebak,strerror(errno)); return 0; } @@ -224,7 +266,7 @@ void finish_transfer(char *fname, char *fnametmp, struct file_struct *file) 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 {