Delay the renaming of all the temp files until the end of the transfer. --- orig/options.c 2005-01-25 03:26:51 +++ options.c 2005-01-25 09:21:08 @@ -101,6 +101,7 @@ int modify_window = 0; int blocking_io = -1; int checksum_seed = 0; int inplace = 0; +int delay_renames = 0; long block_size = 0; /* "long" because popt can't set an int32. */ @@ -288,6 +289,7 @@ void usage(enum logcode F) rprintf(F," --max-size=SIZE don't transfer any file larger than SIZE\n"); rprintf(F," --partial keep partially transferred files\n"); rprintf(F," --partial-dir=DIR put a partially transferred file into DIR\n"); + rprintf(F," --delay-renames rename all transferred files into place at end\n"); rprintf(F," --numeric-ids don't map uid/gid values by user/group name\n"); rprintf(F," --timeout=TIME set I/O timeout in seconds\n"); rprintf(F," -I, --ignore-times turn off mod time & file size quick check\n"); @@ -407,6 +409,7 @@ static struct poptOption long_options[] {"progress", 0, POPT_ARG_NONE, &do_progress, 0, 0, 0 }, {"partial", 0, POPT_ARG_NONE, &keep_partial, 0, 0, 0 }, {"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 }, + {"delay-renames", 0, POPT_ARG_NONE, &delay_renames, 0, 0, 0 }, {"ignore-errors", 0, POPT_ARG_NONE, &ignore_errors, 0, 0, 0 }, {"blocking-io", 0, POPT_ARG_VAL, &blocking_io, 1, 0, 0 }, {"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 }, @@ -995,11 +998,15 @@ int parse_arguments(int *argc, const cha bwlimit_writemax = 512; } + if (delay_renames && !partial_dir) + partial_dir = ".~tmp~"; + if (inplace) { #if HAVE_FTRUNCATE if (partial_dir) { snprintf(err_buf, sizeof err_buf, - "--inplace cannot be used with --partial-dir\n"); + "--inplace cannot be used with --%s\n", + delay_renames ? "delay-renames" : "partial-dir"); return 0; } keep_partial = 0; @@ -1236,6 +1243,8 @@ void server_options(char **args,int *arg if (partial_dir && am_sender) { args[ac++] = "--partial-dir"; args[ac++] = partial_dir; + if (delay_renames) + args[ac++] = "--delay-renames"; } else if (keep_partial) args[ac++] = "--partial"; --- orig/receiver.c 2005-01-24 01:43:10 +++ receiver.c 2005-01-10 10:16:54 @@ -48,6 +48,7 @@ extern int orig_umask; extern int keep_partial; extern int checksum_seed; extern int inplace; +extern int delay_renames; extern struct filter_list_struct server_filter_list; @@ -272,6 +273,7 @@ int recv_files(int f_in, struct file_lis char fnametmp[MAXPATHLEN]; char *fnamecmp, *partialptr; char fnamecmpbuf[MAXPATHLEN]; + uchar *delayed_bits = NULL; struct file_struct *file; struct stats initial_stats; int save_make_backups = make_backups; @@ -285,6 +287,13 @@ int recv_files(int f_in, struct file_lis flist->hlink_pool = NULL; } + if (delay_renames) { + int sz = (flist->count + 7) / 8; + if (!(delayed_bits = new_array(char, sz))) + out_of_memory("recv_files"); + memset(delayed_bits, 0, sz); + } + while (1) { cleanup_disable(); @@ -499,7 +508,7 @@ int recv_files(int f_in, struct file_lis exit_cleanup(RERR_FILEIO); } - if (recv_ok || inplace) { + if ((recv_ok && !delay_renames) || inplace) { finish_transfer(fname, fnametmp, file, recv_ok, 1); if (partialptr != fname && fnamecmp == partialptr) { do_unlink(partialptr); @@ -509,6 +518,8 @@ int recv_files(int f_in, struct file_lis && handle_partial_dir(partialptr, PDIR_CREATE)) { finish_transfer(partialptr, fnametmp, file, recv_ok, !partial_dir); + if (delay_renames && recv_ok) + delayed_bits[i/8] |= 1 << (i % 8); } else { partialptr = NULL; do_unlink(fnametmp); @@ -548,6 +559,33 @@ int recv_files(int f_in, struct file_lis } make_backups = save_make_backups; + if (delay_renames) { + for (i = 0; i < flist->count; i++) { + struct file_struct *file = flist->files[i]; + if (!file->basename + || !(delayed_bits[i/8] & (1 << (i % 8)))) + continue; + fname = local_name ? local_name : f_name(file); + partialptr = partial_dir_fname(fname); + if (partialptr) { + if (make_backups && !make_backup(fname)) + continue; + if (verbose > 2) { + rprintf(FINFO, "renaming %s to %s\n", + partialptr, fname); + } + if (do_rename(partialptr, fname) < 0) { + rsyserr(FERROR, errno, + "rename failed for %s (from %s)", + fname, partialptr); + } else { + handle_partial_dir(partialptr, + PDIR_DELETE); + } + } + } + } + if (delete_after && recurse && !local_name && flist->count > 0) delete_files(flist); --- orig/rsync.yo 2005-01-25 03:26:51 +++ rsync.yo 2005-01-25 09:30:44 @@ -353,6 +353,7 @@ verb( --max-size=SIZE don't transfer any file larger than SIZE --partial keep partially transferred files --partial-dir=DIR put a partially transferred file into DIR + --delay-renames rename transferred files into place at end --numeric-ids don't map uid/gid values by user/group name --timeout=TIME set I/O timeout in seconds -I, --ignore-times turn off mod time & file size quick check @@ -549,9 +550,9 @@ or appended data, and also on systems th bound. The option implies --partial (since an interrupted transfer does not delete -the file), but conflicts with --partial-dir. Prior to rsync 2.6.4 ---inplace was also incompatible with --compare-dest, --copy-dest, and ---link-dest. +the file), but conflicts with --partial-dir and --delay-renames. +Prior to rsync 2.6.4 --inplace was also incompatible with --compare-dest, +--copy-dest, and --link-dest. WARNING: The file's data will be in an inconsistent state during the transfer (and possibly afterward if the transfer gets interrupted), so you @@ -1028,13 +1029,26 @@ is a security risk. E.g. AVOID "/tmp". You can also set the partial-dir value the RSYNC_PARTIAL_DIR environment variable. Setting this in the environment does not force --partial to be -enabled, but rather it effects where partial files go when --partial (or --P) is used. For instance, instead of specifying --partial-dir=.rsync-tmp +enabled, but rather it effects where partial files go when --partial is +specified. For instance, instead of using --partial-dir=.rsync-tmp along with --progress, you could set RSYNC_PARTIAL_DIR=.rsync-tmp in your environment and then just use the -P option to turn on the use of the -.rsync-tmp dir for partial transfers. The only time the --partial option -does not look for this environment value is when --inplace was also -specified (since --inplace conflicts with --partial-dir). +.rsync-tmp dir for partial transfers. The only times the --partial option +does not look for this environment value is (1) when --inplace was also +specified (since --inplace conflicts with --partial-dir), and (2) when +--delay-renames was also specified (see below). + +dit(bf(--delay-renames)) This option puts the temporary file from each +updated file into the file's partial-dir (see above) until the end of the +transfer, at which time all the files are renamed into place in rapid +succession. This attempts to make the updating of the files a little more +atomic. If you don't specify the --partial-dir option, this option will +cause it to default to ".~tmp~" (RSYNC_PARTIAL_DIR is not consulted for +this value). Conflicts with --inplace. + +See also the "atomic-rsync" perl script in the "support" subdir for an +update algorithm that is even more atomic (it uses --link-dest and a +parallel hierarchy of files). dit(bf(--progress)) This option tells rsync to print information showing the progress of the transfer. This gives a bored user