X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/a800434a82af8dbd59da7c27b382c9897dd90150..31f440e68b1a087093f1c34be1ad3444f911d7d9:/rsync.c diff --git a/rsync.c b/rsync.c index 72c7ce9c..7ad7a9ad 100644 --- a/rsync.c +++ b/rsync.c @@ -267,65 +267,68 @@ static struct sum_struct *receive_sums(int f) static 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 updated = 0; + STRUCT_STAT st2; + extern int am_daemon; - if (dry_run) return 0; + if (dry_run) return 0; - if (!st) { - if (link_stat(fname,&st2) != 0) { - rprintf(FERROR,"stat %s : %s\n",fname,strerror(errno)); - return 0; - } - st = &st2; - } + if (!st) { + if (link_stat(fname,&st2) != 0) { + rprintf(FERROR,"stat %s : %s\n",fname,strerror(errno)); + return 0; + } + st = &st2; + } - if (preserve_times && !S_ISLNK(st->st_mode) && - st->st_mtime != file->modtime) { - updated = 1; - if (set_modtime(fname,file->modtime) != 0) { - rprintf(FERROR,"failed to set times on %s : %s\n", - fname,strerror(errno)); - return 0; - } - } + if (preserve_times && !S_ISLNK(st->st_mode) && + st->st_mtime != file->modtime) { + updated = 1; + if (set_modtime(fname,file->modtime) != 0) { + rprintf(FERROR,"failed to set times on %s : %s\n", + fname,strerror(errno)); + return 0; + } + } + + if ((am_root || !am_daemon) && + ((am_root && preserve_uid && st->st_uid != file->uid) || + (preserve_gid && st->st_gid != file->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; + } + } #ifdef HAVE_CHMOD - if (preserve_perms && !S_ISLNK(st->st_mode) && - 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; - } - } + 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; + } + } #endif - - if ((am_root || !am_daemon) && - ((am_root && preserve_uid && st->st_uid != file->uid) || - (preserve_gid && st->st_gid != file->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 updated; - } - updated = 1; - } - if (verbose > 1 && report) { - if (updated) - rprintf(FINFO,"%s\n",fname); - else - rprintf(FINFO,"%s is uptodate\n",fname); - } - return updated; + if (verbose > 1 && report) { + if (updated) + rprintf(FINFO,"%s\n",fname); + else + rprintf(FINFO,"%s is uptodate\n",fname); + } + return updated; } @@ -356,7 +359,11 @@ static int skip_file(char *fname, /* use a larger block size for really big files */ int adapt_block_size(struct file_struct *file, int bsize) { - int ret = file->length / (10000); /* rough heuristic */ + int ret; + + if (bsize != BLOCK_SIZE) return bsize; + + ret = file->length / (10000); /* rough heuristic */ ret = ret & ~15; /* multiple of 16 */ if (ret < bsize) ret = bsize; if (ret > CHUNK_SIZE/2) ret = CHUNK_SIZE/2; @@ -724,11 +731,19 @@ static void delete_files(struct file_list *flist) } static char *cleanup_fname; +static char *cleanup_new_fname; +static struct file_struct *cleanup_file; +static void finish_transfer(char *fname, char *fnametmp, struct file_struct *file); void exit_cleanup(int code) { + extern int keep_partial; + + if (cleanup_fname && keep_partial) { + finish_transfer(cleanup_new_fname, cleanup_fname, cleanup_file); + } io_flush(); - if (cleanup_fname) + if (cleanup_fname && !keep_partial) do_unlink(cleanup_fname); signal(SIGUSR1, SIG_IGN); if (code) { @@ -783,6 +798,46 @@ static int get_tmpname(char *fnametmp, char *fname) return 1; } +/* finish off a file transfer, renaming the file and setting the permissions + and ownership */ +static 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; + } + } + + /* move tmp file over real file */ + if (do_rename(fnametmp,fname) != 0) { + if (errno == EXDEV) { + /* rename failed on cross-filesystem link. + Copy the file instead. */ + if (copy_file(fnametmp,fname, file->mode)) { + 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); + } + } else { + set_perms(fname,file,NULL,0); + } +} + + int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen) { int fd1,fd2; @@ -804,6 +859,9 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen) } while (1) { + cleanup_fname = NULL; + cleanup_new_fname = NULL; + i = read_int(f_in); if (i == -1) { if (phase==0 && remote_version >= 13) { @@ -880,10 +938,18 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen) continue; } - fd2 = do_open(fnametmp,O_WRONLY|O_CREAT|O_EXCL,file->mode); + /* we initially set the perms without the + setuid/setgid bits to ensure that there is no race + condition. They are then correctly updated after + the lchown. Thanks to snabb@epipe.fi for pointing + this out */ + fd2 = do_open(fnametmp,O_WRONLY|O_CREAT|O_EXCL, + file->mode & ACCESSPERMS); + if (fd2 == -1 && relative_paths && errno == ENOENT && create_directory_path(fnametmp) == 0) { - fd2 = do_open(fnametmp,O_WRONLY|O_CREAT|O_EXCL,file->mode); + fd2 = do_open(fnametmp,O_WRONLY|O_CREAT|O_EXCL, + file->mode & ACCESSPERMS); } if (fd2 == -1) { rprintf(FERROR,"open %s : %s\n",fnametmp,strerror(errno)); @@ -894,6 +960,8 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen) } cleanup_fname = fnametmp; + cleanup_new_fname = fname; + cleanup_file = file; if (!am_server && verbose) printf("%s\n",fname); @@ -909,43 +977,12 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen) if (verbose > 2) rprintf(FINFO,"renaming %s to %s\n",fnametmp,fname); - - if (make_backups) { - char fnamebak[MAXPATHLEN]; - if (strlen(fname) + strlen(backup_suffix) > (MAXPATHLEN-1)) { - rprintf(FERROR,"backup filename too long\n"); - continue; - } - 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)); - continue; - } - } - - /* move tmp file over real file */ - if (do_rename(fnametmp,fname) != 0) { - if (errno == EXDEV) { - /* rename failed on cross-filesystem link. - Copy the file instead. */ - if (copy_file(fnametmp,fname, file->mode)) { - 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); - } - } else { - set_perms(fname,file,NULL,0); - } + finish_transfer(fname, fnametmp, file); + cleanup_fname = NULL; - + cleanup_new_fname = NULL; + cleanup_file = NULL; if (!recv_ok) { if (csum_length == SUM_LENGTH) {