diff -urN --exclude=patches rsync-2.6.2/Makefile.in rsync-2.6.3/Makefile.in --- rsync-2.6.2/Makefile.in 2004-02-10 09:06:11.000000000 -0800 +++ rsync-2.6.3/Makefile.in 2004-08-12 11:59:03.000000000 -0700 @@ -44,11 +44,11 @@ TLS_OBJ = tls.o syscall.o lib/permstring.o # Programs we must have to run the test cases -CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) \ +CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \ trimslash$(EXEEXT) t_unsafe$(EXEEXT) wildtest$(EXEEXT) # Objects for CHECK_PROGS to clean -CHECK_OBJS=getgroups.o t_stub.o t_unsafe.o trimslash.o wildtest.o +CHECK_OBJS=getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.o wildtest.o # note that the -I. is needed to handle config.h when using VPATH .c.o: @@ -58,18 +58,16 @@ all: rsync$(EXEEXT) -man: rsync.1 rsyncd.conf.5 - install: all -mkdir -p ${DESTDIR}${bindir} - ${INSTALLCMD} ${STRIP} -m 755 rsync$(EXEEXT) ${DESTDIR}${bindir} + ${INSTALLCMD} ${INSTALL_STRIP} -m 755 rsync$(EXEEXT) ${DESTDIR}${bindir} -mkdir -p ${DESTDIR}${mandir}/man1 -mkdir -p ${DESTDIR}${mandir}/man5 ${INSTALLMAN} -m 644 $(srcdir)/rsync.1 ${DESTDIR}${mandir}/man1 ${INSTALLMAN} -m 644 $(srcdir)/rsyncd.conf.5 ${DESTDIR}${mandir}/man5 install-strip: - $(MAKE) STRIP='-s' install + $(MAKE) INSTALL_STRIP='-s' install rsync$(EXEEXT): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) @@ -82,6 +80,9 @@ getgroups$(EXEEXT): getgroups.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getgroups.o $(LIBS) +getfsdev$(EXEEXT): getfsdev.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS) + TRIMSLASH_OBJ = trimslash.o syscall.o trimslash$(EXEEXT): $(TRIMSLASH_OBJ) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS) @@ -90,17 +91,15 @@ t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS) -# I don't like these rules because CVS can skew the timestamps and -# produce spurious warnings, and also make "make install" fail if the -# source directory can no longer be found. Since we don't rebuild -# automatically they're kind of lame anyhow. - -#Makefile: Makefile.in configure config.status -# echo "WARNING: You need to run ./config.status --recheck" - -# don't actually run autoconf, just issue a warning -#configure: configure.in -# echo "WARNING: you need to rerun autoconf" +gen: $(srcdir)/configure $(srcdir)/config.h.in proto man + +man: $(srcdir)/rsync.1 $(srcdir)/rsyncd.conf.5 + +$(srcdir)/configure: $(srcdir)/configure.in $(srcdir)/aclocal.m4 + cd $(srcdir); autoconf + +$(srcdir)/config.h.in: $(srcdir)/configure.in $(srcdir)/aclocal.m4 + cd $(srcdir); autoheader $(srcdir)/rsync.1: $(srcdir)/rsync.yo yodl2man -o $(srcdir)/rsync.1 $(srcdir)/rsync.yo @@ -109,7 +108,12 @@ yodl2man -o $(srcdir)/rsyncd.conf.5 $(srcdir)/rsyncd.conf.yo proto: - cat $(srcdir)/*.c $(srcdir)/lib/compat.c | awk -f $(srcdir)/mkproto.awk > $(srcdir)/proto.h + cat $(srcdir)/*.c $(srcdir)/lib/compat.c | awk -f $(srcdir)/mkproto.awk >$(srcdir)/proto.h.new + if diff $(srcdir)/proto.h $(srcdir)/proto.h.new >/dev/null; then \ + rm $(srcdir)/proto.h.new; \ + else \ + mv $(srcdir)/proto.h.new $(srcdir)/proto.h; \ + fi clean: cleantests rm -f *~ $(OBJS) $(TLS_OBJ) $(CHECK_PROGS) $(CHECK_OBJS) @@ -146,9 +150,6 @@ # There seems to be no standard way to specify some variables as # exported from a Makefile apart from listing them like this. -# TODO: Tests that depend on built test aide programs like tls need to -# know where the build directory is. - # This depends on building rsync; if we need any helper programs it # should depend on them too. diff -urN --exclude=patches rsync-2.6.2/NEWS rsync-2.6.3/NEWS --- rsync-2.6.2/NEWS 2004-04-30 10:53:51.000000000 -0700 +++ rsync-2.6.3/NEWS 2004-09-30 03:46:43.000000000 -0700 @@ -1,229 +1,252 @@ -NEWS for rsync 2.6.2 (30 Apr 2004) +NEWS for rsync 2.6.3 (30 Sep 2004) Protocol: 28 (unchanged) -Changes since 2.6.1: +Changes since 2.6.2: + + SECURITY FIXES: + + - A bug in the sanitize_path routine (which affects a non-chrooted + rsync daemon) could allow a user to craft a pathname that would get + transformed into an absolute path for certain options (but not for + file-transfer names). If you're running an rsync daemon with chroot + disabled, *please upgrade*, ESPECIALLY if the user privs you run + rsync under is anything above "nobody". + + OUTPUT CHANGES (ATTN: those using a script to parse the verbose output): + + - Please note that the 2-line footer (output when verbose) now uses the + term "sent" instead of "wrote" and "received" instead of "read". If + you are not parsing the numeric values out of this footer, a script + would be better off using the empty line prior to the footer as the + indicator that the verbose output is over. + + - The output from the --stats option was similarly affected to change + "written" to "sent" and "read" to "received". + + - Rsync ensures that a filename that contains a newline gets mentioned + with each newline transformed into a question mark (which prevents a + filename from causing an empty line to be output). + + - The "backed up ..." message that is output when at least 2 --verbose + options are specified is now the same both with and without the + --backup-dir option. BUG FIXES: - - Fixed a major bug in the sorting of the filenames when --relative - is used for some sources (probably just "/", but don't depend on - that). This fix ensures that we ask for the right file-list item - when requesting changes from the sender. - - - Rsync now checks the return value of the close() function to - better report disk-full problems on an NFS file system. - - - Restored the old daemon-server behavior of logging error messages - rather than returning them to the user. (A better long-term fix - will be sought in the future.) - - - An obscure uninitialized-variable bug was fixed in the uid/gid - code. (This bug probably had no ill effects.) - - - Got rid of the configure check for sys/sysctl.h (it wasn't used - and was causing a problem on some systems). Also improved the - broken-largefile-locking test to try to avoid failure due to an - NFS build-dir. + - Fixed a crash bug that might appear when --delete was used and + multiple source directories were specified. + + - Fixed a 32-bit truncation of the file length when generating the + checksums. + + - The --backup code no longer attempts to create some directories + over and over again (generating warnings along the way). + + - Fixed a bug in the reading of the secrets file (by the daemon) and + the password file (by the client): the files no longer need to be + terminated by a newline for their content to be read in. + + - If a file has a read error on the sending side or the reconstructed + data doesn't match the expected checksum (perhaps due to the basis + file changing during the transfer), the receiver will no longer + retain the resulting file unless the --partial option was specified. + (Note: for the read-error detection to work, neither side can be + older than 2.6.3 -- older receivers will always retain the file, and + older senders don't tell the receiver that the file had a read + error.) + + - If a file gets resent in a single transfer and the --backup option + is enabled, rsync no longer performs a duplicate backup (it used to + overwrite the original file in the backup area). + + - Files specified in the daemon's "exclude" or "exclude from" config + items are now excluded from being uploaded (assuming that the module + allows uploading at all) in addition to the old download exclusion. + + - Got rid of a potential hang in the receiver when near the end of a + phase. + + - When using --backup without a --backup-dir, rsync no longer preserves + the modify time on directories. This avoids confusing NFS. + + - When --copy-links (-L) is specified, we now output a separate error + for a symlink that has no referent instead of claiming that a file + "vanished". + + - The --copy-links (-L) option no longer has the side-effect of telling + the receiving side to follow symlinks. See the --keep-dirlinks + option (mentioned below) for a way to specify that behavior. + + - Error messages from the daemon server's option-parsing (such as + refused options) are now successfully transferred back to the client + (the server used to fail to send the message because the socket + wasn't in the right state for the message to get through). + + - Most transfer errors that occur during a daemon transfer are now + returned to the user in addition to being logged (some messages are + intended to be daemon-only and are not affected by this). + + - Fixed a bug in the daemon authentication code when using one of the + batch-processing options. + + - We try to work around some buggy IPv6 implementations that fail to + implement IPV6_V6ONLY. This should fix the "address in use" error + that some daemons get when running on an OS with a buggy IPv6 + implementation. Also, if the new code gets this error, we might + suggest that the user specify --ipv4 or --ipv6 (if we think it will + help). + + - When the remote rsync dies, make a better effort to recover any error + messages it may have sent before dying (the local rsync used to just + die with a socket-write error). + + - When using --delete and a --backup-dir that contains files that are + hard-linked to their destination equivalents, rsync now makes sure + that removed files really get removed (avoids a really weird rename() + behavior). + + - Avoid a bogus run-time complaint about a lack of 64-bit integers when + the int64 type is defined as an off_t and it actually has 64-bits. + + - Added a configure check for open64() without mkstemp64() so that we + can avoid using mkstemp() when such a combination is encountered. + This bypasses a problem writing out large temp files on OSes such as + AIX and HP-UX. + + - Fixed an age-old crash problem with --read-batch on a local copy + (rsync was improperly assuming --whole-file for the local copy). + + - When --dry-run (-n) is used and the destination directory does not + exist, rsync now produces a correct report of files that would be + sent instead of dying with a chdir() error. + + - Fixed a bug that could cause a slow-to-connect rsync daemon to die + with an error instead of waiting for the connection to finish. + + - Fixed an ssh interaction that could cause output to be lost when the + user chose to combine the output of rsync's stdout and stderr (e.g. + using the "2>&1"). + + ENHANCEMENTS: + + - Added the --partial-dir=DIR option that lets you specify where to + (temporarily) put a partially transferred file (instead of over- + writing the destination file). E.g. --partial-dir=.rsync-partial + Also added support for the RSYNC_PARTIAL_DIR environment variable + that, when found, transforms a regular --partial option (such as + the convenient -P option) into one that also specifies a directory. + + - Added --keep-dirlinks (-K), which allows you to symlink a directory + onto another partition on the receiving side and have rsync treat it + as matching a normal directory from the sender. + + - Added the --inplace option that tells rsync to write each destination + file without using a temporary file. The matching of existing data + in the destination file can be severely limited by this, but there + are also cases where this is more efficient (such as appending data). + Use only when needed (see the man page for more details). - - Fixed a compile problem on systems that don't define - AI_NUMERICHOST. + - Added the "write only" option for the daemon's config file. - - Fixed a compile problem in the popt source for compilers that - don't support __attribute__. + - Added long-option names for -4 and -6 (namely --ipv4 and --ipv6) + and documented all these options in the man page. + + - Improved the handling of the --bwlimit option so that it's less + bursty, more accurate, and works properly over a larger range of + values. + + - The rsync daemon-over-ssh code now looks for SSH_CONNECTION and + SSH2_CLIENT in addition to SSH_CLIENT to figure out the IP address. + + - Added the --checksum-seed=N option for advanced users. + + - Batch writing/reading has a brand-new implementation that is simpler, + fixes a few weird problems with the old code (such as no longer + sprinkling the batch files into different dirs or even onto different + systems), and is much less intrusive into the code (making it easier + to maintain for the future). The new code generates just one data + file instead of three, which makes it possible to read the batch on + stdin via a remote shell. Also, the old requirement of forcing the + same fixed checksum-seed for all batch processing has been removed. + + - If an rsync daemon has a module set with "list = no" (which hides its + presence in the list of available modules), a user that fails to + authenticate gets the same "unknown module" error that they would get + if the module were actually unknown (while still logging the real + error to the daemon's log file). This prevents fishing for module + names. + + - The daemon's "refuse options" config item now allows you to match + option names using wildcards and/or the single-letter option names. + + - Each transferred file now gets its permissions and modified-time + updated before the temp-file gets moved into place. Previously, the + finished file would have a very brief window where its permissions + disallowed all group and world access. + + - Added the ability to parse a literal IPv6 address in an "rsync:" URL + (e.g. rsync://[2001:638:500:101::21]:873/module/dir). + + - The daemon's wildcard expanding code can now handle more than 1000 + filenames (it's now limited by memory instead of having a hard-wired + limit). INTERNAL: - - Improved the testsuite's "merge" test to work on OSF1. + - Some cleanup in the exclude code has saved some per-exclude memory + and made the code easier to maintain. - - Two new diffs were added to the patches dir. + - Improved the argv-overflow checking for a remote command that has a + lot of args. - -NEWS for rsync 2.6.1 (26 Apr 2004) -Protocol: 28 (changed) -Changes since 2.6.0: + - Use rsyserr() in the various places that were still calling rprintf() + with strerror() as an arg. - SECURITY FIXES: + - If an rsync daemon is listening on multiple sockets (to handle both + IPv4 and IPv6 to a single port), we now close all the unneeded file + handles after we accept a connection (we used to close just one of + them). - - Paths sent to an rsync daemon are more thoroughly sanitized when - chroot is not used. If you're running a non-read-only rsync - daemon with chroot disabled, *please upgrade*, ESPECIALLY if the - user privs you run rsync under is anything above "nobody". + - Optimized the handling of larger block sizes (rsync used to slow to a + crawl if the block size got too large). - ENHANCEMENTS: + - Optimized away a loop in hash_search(). - - Lower memory use, more optimal transfer of data over the socket, - and lower CPU usage (see the INTERNAL section for details). + - Some improvements to the sanitize_path() and clean_fname() functions + makes them more efficient and produce better results (while still + being compatible with the file-name cleaning that gets done on both + sides when sending the file-list). - - The RSYNC_PROXY environment variable can now contain a - "USER:PASS@" prefix before the "HOST:PORT" information. - (Bardur Arantsson) - - - The --progress output now mentions how far along in the transfer - we are, including both a count of files transferred and a - percentage of the total file-count that we've processed. It also - shows better current-rate-of-transfer and remaining-transfer-time - values. + - Got rid of alloc_sanitize_path() after adding a destination-buffer + arg to sanitize_path() made it possible to put all the former's + functionality into the latter. - - The configure script now accepts --with-rsyncd-conf=PATH to - override the default value of the /etc/rsyncd.conf file. + - The file-list that is output when at least 4 verbose options are + specified reports the uid value on the sender even when rsync is + not running as root (since we might be sending to a root receiver). - - Added a couple extra diffs in the "patches" dir, removed the ones - that got applied, and rebuilt the rest. + BUILD CHANGES: - - Documentation changes now attempt to describe some often mis- - understood features more clearly. + - Added a "gen" target to rebuild most of the generated files, + including configure, config.h.in, the man pages, and proto.h. - BUG FIXES: + - If "make proto" doesn't find some changes in the prototypes, the + proto.h file is left untouched (its time-stamp used to always be + updated). - - When -x (--one-file-system) is combined with -L (--copy-links) or - --copy-unsafe-links, no symlinked files are skipped, even if the - referent file is on a different filesystem. - - - The --link-dest code now works properly for a non-root user when - (1) the UIDs of the source and destination differ and -o was - specified, or (2) when the group of the source can't be used on - the destination and -g was specified. - - - Fixed a bug in the handling of -H (hard-links) that might cause - the expanded PATH/NAME value of the current item to get - overwritten (due to an expanded-name caching bug). - - - We now reset the "new data has been sent" flag at the start of - each file we send. This makes sure that an interrupted transfer - with the --partial option set doesn't keep a shorter temp file - than the current basis file when no new data has been transfered - over the wire for that file. - - - Fixed a byte-order problem in --batch-mode on big-endian machines. - (Jay Fenlason) - - - Fixed configure bug when running "./configure --disable-ipv6". - - - Fixed "make test" bug when build dir is not the source dir. - - - When using --cvs-exclude, the exclude items we get from a - per-directory's .cvsignore file once again only affect that one - directory (not all following directories too). The items are also - now properly word-split and parsed without any +/- prefix parsing. - - - When specifying the USER@HOST: prefix for a file, the USER part - can now contain an '@', if needed (i.e. the last '@' is used to - find the HOST, not the first). - - - Fixed some bugs in the handling of group IDs for non-root users: - (1) It properly handles a group that the sender didn't have a name - for (it would previously skip changing the group on any files in - that group). (2) If --numeric-ids is used, rsync no longer - attempts to set groups that the user doesn't have the permission - to set. - - - Fixed the "refuse options" setting in the rsyncd.conf file. - - - Improved the -x (--one-file-system) flag's handling of any mount- - point directories we encounter. It is both more optimal (in that - it no longer does a useless scan of the contents of the mount- - point dirs) and also fixes a bug where a remapped mount of the - original filesystem could get discovered in a subdir we should be - ignoring. - - - Rsync no longer discards a double-slash at the start of a filename - when trying to open the file. It also no longer constructs names - that start with a double slash (unless the user supplied them). - - - Path-specifying options to a daemon should now work the same with - or without chroot turned on. Previously, such a option (such as - --link-dest) would get its absolute path munged into a relative - one if chroot was not on, making that setting fairly useless. - Rsync now transforms the path into one that is based on the - module's base dir when chroot is not enabled. - - - Fixed compilation problem on Tru64 Unix (having to do with - sockaddr.sa_len and sockaddr.sin_len). - - - Fixed a compatibility problem interacting with older rsync - versions that might send us an empty --suffix value without - telling us that --backup-dir was specified. - - - The "hosts allow" option for a daemon-over-remote-shell process - now has improved support for IPv6 addresses and a fix for systems - that have a length field in their socket structs. + - The variable $STRIP (that is optionally set by the install-strip + target's rule) was changed to $INSTALL_STRIP because some systems + have $STRIP already set in the environment. - - Fixed the ability to request an empty backup --suffix when sending - files to an rsync daemon. + - Fixed a build problem when SUPPORT_HARD_LINKS isn't defined. - INTERNAL: + - When cross-compiling, the gettimeofday() function is now assumed to + be a modern version that takes two-args (since we can't test it). - - Most of the I/O is now buffered, which results in a pretty large - speedup when running under MS Windows. (Craig Barratt) + DEVELOPER RELATED: - - Optimizations to the name-handling/comparing code have made some - significant reductions in user-CPU time for large file sets. + - The scripts in the testsuite dir were cleaned up a bit and a few + new tests added. - - Some cleanup of the variable types make the code more consistent. + - Some new diffs were added to the patches dir, and some accepted + ones were removed. - - Reduced memory requirements of hard link preservation. - (J.W. Schultz) - - - Implemented a new algorithm for hard-link handling that speeds up - the code significantly. (J.W. Schultz and Wayne Davison) - - - The --hard-link option now uses the first existing file in the - group of linked files as the basis for the transfer. This - prevents the sub-optimal transfer of a file's data when a new - hardlink is added on the sending side and it sorts alphabetically - earlier in the list than the files that are already present on the - receiving side. - - - Dropped support for protocol versions less than 20 (2.3.0 released - 15 Mar 1999) and activated warnings for protocols less than 25 - (2.5.0 released 23 Aug 2001). (Wayne Davison and J.W. Schultz, - severally) - - - More optimal data transmission for --hard-links (protocol 28). - - - More optimal data transmission for --checksum (protocol 28). - - - Less memory is used when --checksum is specified. - - - Less memory is used in the file list (a per-file savings). - - - The generator is now better about not modifying the file list - during the transfer in order to avoid a copy-on-write memory - bifurcation (on systems where fork() uses shared memory). - Previously, rsync's shared memory would slowly become unshared, - resulting in real memory usage nearly doubling on the receiving - side by the end of the transfer. Now, as long as permissions - are being preserved, the shared memory should remain that way - for the entire transfer. - - - Changed hardlink info and file_struct + strings to use allocation - pools. This reduces memory use for large file-sets and permits - freeing memory to the OS. (J.W. Schultz) - - - The 2 pipes used between the receiver and generator processes - (which are forked on the same machine) were reduced to 1 pipe and - the protocol improved so that (1) it is now impossible to have the - "redo" pipe fill up and hang rsync, and (2) trailing messages from - the receiver don't get lost on their way through the generator - over to the sender (which mainly affected hard-link messages and - verbose --stats output). - - - Improved the internal uid/gid code to be more portable and a - little more optimized. - - - The device numbers sent when using --devices are now sent as - separate major/minor values with 32-bit accuracy (protocol 28). - Previously, the copied devices were sent as a single 32-bit - number. This will make inter-operation of 64-bit binaries more - compatible with their 32-bit brethren (with both ends of the - connection are using protocol 28). Note that optimizations in the - binary protocol for sending the device numbers often results in - fewer bytes being used than before, even though more precision is - now available. - - - Some cleanup of the exclude/include structures and its code made - things clearer (internally), simpler, and more efficient. - - - The reading & writing of the file-list in batch-mode is now - handled by the same code that sends & receives the list over the - wire. This makes it much easier to maintain. (Note that the - batch code is still considered to be experimental.) diff -urN --exclude=patches rsync-2.6.2/OLDNEWS rsync-2.6.3/OLDNEWS --- rsync-2.6.2/OLDNEWS 2004-03-04 11:35:57.000000000 -0800 +++ rsync-2.6.3/OLDNEWS 2004-09-30 02:36:29.000000000 -0700 @@ -1,3 +1,240 @@ +NEWS for rsync 2.6.2 (30 Apr 2004) +Protocol: 28 (unchanged) +Changes since 2.6.1: + + BUG FIXES: + + - Fixed a major bug in the sorting of the filenames when --relative + is used for some sources (just sources such as "/" and "/*" were + affected). This fix ensures that we ask for the right file-list + item when requesting changes from the sender. + + - Rsync now checks the return value of the close() function to + better report disk-full problems on an NFS file system. + + - Restored the old daemon-server behavior of logging error messages + rather than returning them to the user. (A better long-term fix + will be sought in the future.) + + - An obscure uninitialized-variable bug was fixed in the uid/gid + code. (This bug probably had no ill effects.) + + BUILD CHANGES: + + - Got rid of the configure check for sys/sysctl.h (it wasn't used + and was causing a problem on some systems). Also improved the + broken-largefile-locking test to try to avoid failure due to an + NFS build-dir. + + - Fixed a compile problem on systems that don't define + AI_NUMERICHOST. + + - Fixed a compile problem in the popt source for compilers that + don't support __attribute__. + + DEVELOPER RELATED: + + - Improved the testsuite's "merge" test to work on OSF1. + + - Two new diffs were added to the patches dir. + + +NEWS for rsync 2.6.1 (26 Apr 2004) +Protocol: 28 (changed) +Changes since 2.6.0: + + SECURITY FIXES: + + - Paths sent to an rsync daemon are more thoroughly sanitized when + chroot is not used. If you're running a non-read-only rsync + daemon with chroot disabled, *please upgrade*, ESPECIALLY if the + user privs you run rsync under is anything above "nobody". + + ENHANCEMENTS: + + - Lower memory use, more optimal transfer of data over the socket, + and lower CPU usage (see the INTERNAL section for details). + + - The RSYNC_PROXY environment variable can now contain a + "USER:PASS@" prefix before the "HOST:PORT" information. + (Bardur Arantsson) + + - The --progress output now mentions how far along in the transfer + we are, including both a count of files transferred and a + percentage of the total file-count that we've processed. It also + shows better current-rate-of-transfer and remaining-transfer-time + values. + + - Documentation changes now attempt to describe some often mis- + understood features more clearly. + + BUG FIXES: + + - When -x (--one-file-system) is combined with -L (--copy-links) or + --copy-unsafe-links, no symlinked files are skipped, even if the + referent file is on a different filesystem. + + - The --link-dest code now works properly for a non-root user when + (1) the UIDs of the source and destination differ and -o was + specified, or (2) when the group of the source can't be used on + the destination and -g was specified. + + - Fixed a bug in the handling of -H (hard-links) that might cause + the expanded PATH/NAME value of the current item to get + overwritten (due to an expanded-name caching bug). + + - We now reset the "new data has been sent" flag at the start of + each file we send. This makes sure that an interrupted transfer + with the --partial option set doesn't keep a shorter temp file + than the current basis file when no new data has been transfered + over the wire for that file. + + - Fixed a byte-order problem in --batch-mode on big-endian machines. + (Jay Fenlason) + + - When using --cvs-exclude, the exclude items we get from a + per-directory's .cvsignore file once again only affect that one + directory (not all following directories too). The items are also + now properly word-split and parsed without any +/- prefix parsing. + + - When specifying the USER@HOST: prefix for a file, the USER part + can now contain an '@', if needed (i.e. the last '@' is used to + find the HOST, not the first). + + - Fixed some bugs in the handling of group IDs for non-root users: + (1) It properly handles a group that the sender didn't have a name + for (it would previously skip changing the group on any files in + that group). (2) If --numeric-ids is used, rsync no longer + attempts to set groups that the user doesn't have the permission + to set. + + - Fixed the "refuse options" setting in the rsyncd.conf file. + + - Improved the -x (--one-file-system) flag's handling of any mount- + point directories we encounter. It is both more optimal (in that + it no longer does a useless scan of the contents of the mount- + point dirs) and also fixes a bug where a remapped mount of the + original filesystem could get discovered in a subdir we should be + ignoring. + + - Rsync no longer discards a double-slash at the start of a filename + when trying to open the file. It also no longer constructs names + that start with a double slash (unless the user supplied them). + + - Path-specifying options to a daemon should now work the same with + or without chroot turned on. Previously, such a option (such as + --link-dest) would get its absolute path munged into a relative + one if chroot was not on, making that setting fairly useless. + Rsync now transforms the path into one that is based on the + module's base dir when chroot is not enabled. + + - Fixed a compatibility problem interacting with older rsync + versions that might send us an empty --suffix value without + telling us that --backup-dir was specified. + + - The "hosts allow" option for a daemon-over-remote-shell process + now has improved support for IPv6 addresses and a fix for systems + that have a length field in their socket structs. + + - Fixed the ability to request an empty backup --suffix when sending + files to an rsync daemon. + + INTERNAL: + + - Most of the I/O is now buffered, which results in a pretty large + speedup when running under MS Windows. (Craig Barratt) + + - Optimizations to the name-handling/comparing code have made some + significant reductions in user-CPU time for large file sets. + + - Some cleanup of the variable types make the code more consistent. + + - Reduced memory requirements of hard link preservation. + (J.W. Schultz) + + - Implemented a new algorithm for hard-link handling that speeds up + the code significantly. (J.W. Schultz and Wayne Davison) + + - The --hard-link option now uses the first existing file in the + group of linked files as the basis for the transfer. This + prevents the sub-optimal transfer of a file's data when a new + hardlink is added on the sending side and it sorts alphabetically + earlier in the list than the files that are already present on the + receiving side. + + - Dropped support for protocol versions less than 20 (2.3.0 released + 15 Mar 1999) and activated warnings for protocols less than 25 + (2.5.0 released 23 Aug 2001). (Wayne Davison and J.W. Schultz, + severally) + + - More optimal data transmission for --hard-links (protocol 28). + + - More optimal data transmission for --checksum (protocol 28). + + - Less memory is used when --checksum is specified. + + - Less memory is used in the file list (a per-file savings). + + - The generator is now better about not modifying the file list + during the transfer in order to avoid a copy-on-write memory + bifurcation (on systems where fork() uses shared memory). + Previously, rsync's shared memory would slowly become unshared, + resulting in real memory usage nearly doubling on the receiving + side by the end of the transfer. Now, as long as permissions + are being preserved, the shared memory should remain that way + for the entire transfer. + + - Changed hardlink info and file_struct + strings to use allocation + pools. This reduces memory use for large file-sets and permits + freeing memory to the OS. (J.W. Schultz) + + - The 2 pipes used between the receiver and generator processes + (which are forked on the same machine) were reduced to 1 pipe and + the protocol improved so that (1) it is now impossible to have the + "redo" pipe fill up and hang rsync, and (2) trailing messages from + the receiver don't get lost on their way through the generator + over to the sender (which mainly affected hard-link messages and + verbose --stats output). + + - Improved the internal uid/gid code to be more portable and a + little more optimized. + + - The device numbers sent when using --devices are now sent as + separate major/minor values with 32-bit accuracy (protocol 28). + Previously, the copied devices were sent as a single 32-bit + number. This will make inter-operation of 64-bit binaries more + compatible with their 32-bit brethren (with both ends of the + connection are using protocol 28). Note that optimizations in the + binary protocol for sending the device numbers often results in + fewer bytes being used than before, even though more precision is + now available. + + - Some cleanup of the exclude/include structures and its code made + things clearer (internally), simpler, and more efficient. + + - The reading & writing of the file-list in batch-mode is now + handled by the same code that sends & receives the list over the + wire. This makes it much easier to maintain. (Note that the + batch code is still considered to be experimental.) + + BUILD CHANGES: + + - The configure script now accepts --with-rsyncd-conf=PATH to + override the default value of the /etc/rsyncd.conf file. + + - Fixed configure bug when running "./configure --disable-ipv6". + + - Fixed compilation problem on Tru64 Unix (having to do with + sockaddr.sa_len and sockaddr.sin_len). + + DEVELOPER RELATED: + + - Fixed "make test" bug when build dir is not the source dir. + + - Added a couple extra diffs in the "patches" dir, removed the ones + that got applied, and rebuilt the rest. + + NEWS for rsync 2.6.0 (1 Jan 2004) Protocol: 27 (changed) Changes since 2.5.7: @@ -553,6 +790,9 @@ Partial Protocol History RELEASE DATE VER. DATE OF COMMIT PROTOCOL + 30 Sep 2004 2.6.3 28 + 30 Apr 2004 2.6.2 28 + 26 Apr 2004 2.6.1 08 Jan 2004 28 01 Jan 2004 2.6.0 10 Apr 2003 27 (MAX=40) 04 Dec 2003 2.5.7 26 26 Jan 2003 2.5.6 26 diff -urN --exclude=patches rsync-2.6.2/README rsync-2.6.3/README --- rsync-2.6.2/README 2002-05-13 11:34:37.000000000 -0700 +++ rsync-2.6.3/README 2004-06-06 13:36:56.000000000 -0700 @@ -29,9 +29,9 @@ SETUP ----- -Rsync normally uses rsh or ssh for communication. It does not need to +Rsync normally uses ssh or rsh for communication. It does not need to be setuid and requires no special privileges for installation. You -must, however, have a working rsh or ssh system. Using ssh is +must, however, have a working ssh or rsh system. Using ssh is recommended for its security features. Alternatively, rsync can run in `daemon' mode, listening on a socket. @@ -77,7 +77,7 @@ If you have web access then please look at - http://rsync.samba.org + http://rsync.samba.org/ That page contains links to the current bug list, and information on how to report a bug well. You might also like to try searching the @@ -86,9 +86,8 @@ http://mail-archive.com/rsync@lists.samba.org/ -Please send bug reports to - - rsync@lists.samba.org +To send a bug report, follow the instructions on the bug-tracking +page of the web site. CVS TREE diff -urN --exclude=patches rsync-2.6.2/TODO rsync-2.6.3/TODO --- rsync-2.6.2/TODO 2004-04-29 14:12:46.000000000 -0700 +++ rsync-2.6.3/TODO 2004-06-06 13:41:01.000000000 -0700 @@ -21,13 +21,12 @@ FAT support Allow forcing arbitrary permissions 2002/03/12 --diff david.e.sewell 2002/03/15 -Add daemon --no-detach and --no-fork options +Add daemon --no-fork option Create more granular verbosity jw 2003/05/15 DOCUMENTATION -------------------------------------------------------- Update README Keep list of open issues and todos on the web site -Update web site from CVS Perhaps redo manual as SGML LOGGING -------------------------------------------------------------- @@ -326,7 +325,7 @@ -- -- -Add daemon --no-detach and --no-fork options +Add daemon --no-fork option Very useful for debugging. Also good when running under a daemon-monitoring process that tries to restart the service when the @@ -362,11 +361,6 @@ -- -- -Update web site from CVS - - -- -- - - Perhaps redo manual as SGML The man page is getting rather large, and there is more information diff -urN --exclude=patches rsync-2.6.2/access.c rsync-2.6.3/access.c --- rsync-2.6.2/access.c 2003-07-29 23:12:27.000000000 -0700 +++ rsync-2.6.3/access.c 2004-09-24 09:50:07.000000000 -0700 @@ -88,9 +88,13 @@ /* Fail quietly if tok is a hostname (not an address) */ if (strspn(tok, ".0123456789") != len #ifdef INET6 - && !strchr(tok, ':') + && strchr(tok, ':') == NULL #endif - ) return 0; + ) { + if (p) + *p = '/'; + return 0; + } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; @@ -99,17 +103,18 @@ hints.ai_flags = AI_NUMERICHOST; #endif - gai = getaddrinfo(addr, NULL, &hints, &resa); - if (gai) return 0; + if (getaddrinfo(addr, NULL, &hints, &resa) != 0) { + if (p) + *p = '/'; + return 0; + } gai = getaddrinfo(tok, NULL, &hints, &rest); if (p) *p++ = '/'; - if (gai) { - rprintf(FERROR, - "error matching address %s: %s\n", - tok, - gai_strerror(gai)); + if (gai != 0) { + rprintf(FLOG, "error matching address %s: %s\n", + tok, gai_strerror(gai)); freeaddrinfo(resa); return 0; } @@ -152,7 +157,7 @@ } #endif default: - rprintf(FERROR,"unknown family %u\n", rest->ai_family); + rprintf(FLOG, "unknown family %u\n", rest->ai_family); ret = 0; goto out; } @@ -169,14 +174,14 @@ #ifdef HAVE_STRTOL bits = strtol(p, &ep, 10); if (!*p || *ep) { - rprintf(FERROR,"malformed mask in %s\n", tok); + rprintf(FLOG, "malformed mask in %s\n", tok); ret = 0; goto out; } #else for (pp = (unsigned char *)p; *pp; pp++) { if (!isascii(*pp) || !isdigit(*pp)) { - rprintf(FERROR,"malformed mask in %s\n", tok); + rprintf(FLOG, "malformed mask in %s\n", tok); ret = 0; goto out; } @@ -188,7 +193,7 @@ goto out; } if (bits < 0 || bits > (addrlen << 3)) { - rprintf(FERROR,"malformed mask in %s\n", tok); + rprintf(FLOG, "malformed mask in %s\n", tok); ret = 0; goto out; } diff -urN --exclude=patches rsync-2.6.2/authenticate.c rsync-2.6.3/authenticate.c --- rsync-2.6.2/authenticate.c 2004-04-01 10:05:40.000000000 -0800 +++ rsync-2.6.3/authenticate.c 2004-09-24 10:04:05.000000000 -0700 @@ -1,17 +1,17 @@ /* -*- c-file-style: "linux"; -*- - - Copyright (C) 1998-2000 by Andrew Tridgell - + + Copyright (C) 1998-2000 by Andrew Tridgell + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -20,6 +20,9 @@ /* support rsync authentication */ #include "rsync.h" +extern char *password_file; +extern int am_root; + /*************************************************************************** encode a buffer using base64 - simple and slow algorithm. null terminates the result. @@ -54,7 +57,7 @@ char input[32]; struct timeval tv; - memset(input, 0, sizeof(input)); + memset(input, 0, sizeof input); strlcpy((char *)input, addr, 17); sys_gettimeofday(&tv); @@ -62,121 +65,131 @@ SIVAL(input, 20, tv.tv_usec); SIVAL(input, 24, getpid()); - sum_init(); - sum_update(input, sizeof(input)); + sum_init(0); + sum_update(input, sizeof input); sum_end(challenge); } -/* return the secret for a user from the sercret file. maximum length - is len. null terminate it */ +/* Return the secret for a user from the secret file, null terminated. + * Maximum length is len (not counting the null). */ static int get_secret(int module, char *user, char *secret, int len) { char *fname = lp_secrets_file(module); - int fd, found=0; - char line[MAXPATHLEN]; - char *p, *pass=NULL; STRUCT_STAT st; - int ok = 1; - extern int am_root; + int fd, ok = 1; + char ch, *p; - if (!fname || !*fname) return 0; + if (!fname || !*fname) + return 0; - fd = open(fname,O_RDONLY); - if (fd == -1) return 0; + if ((fd = open(fname, O_RDONLY)) < 0) + return 0; if (do_stat(fname, &st) == -1) { - rsyserr(FERROR, errno, "stat(%s)", fname); + rsyserr(FLOG, errno, "stat(%s)", fname); ok = 0; } else if (lp_strict_modes(module)) { if ((st.st_mode & 06) != 0) { - rprintf(FERROR,"secrets file must not be other-accessible (see strict modes option)\n"); + rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n"); ok = 0; } else if (am_root && (st.st_uid != 0)) { - rprintf(FERROR,"secrets file must be owned by root when running as root (see strict modes)\n"); + rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n"); ok = 0; } } if (!ok) { - rprintf(FERROR,"continuing without secrets file\n"); + rprintf(FLOG, "continuing without secrets file\n"); close(fd); return 0; } - while (!found) { - int i = 0; - memset(line, 0, sizeof line); - while ((size_t) i < (sizeof(line)-1)) { - if (read(fd, &line[i], 1) != 1) { - memset(line, 0, sizeof(line)); - close(fd); - return 0; - } - if (line[i] == '\r') continue; - if (line[i] == '\n') break; - i++; + if (*user == '#') { + /* Reject attempt to match a comment. */ + close(fd); + return 0; + } + + /* Try to find a line that starts with the user name and a ':'. */ + p = user; + while (1) { + if (read(fd, &ch, 1) != 1) { + close(fd); + return 0; + } + if (ch == '\n') + p = user; + else if (p) { + if (*p == ch) + p++; + else if (!*p && ch == ':') + break; + else + p = NULL; } - line[i] = 0; - if (line[0] == '#') continue; - p = strchr(line,':'); - if (!p) continue; - *p = 0; - if (strcmp(user, line)) continue; - pass = p+1; - found = 1; } + /* Slurp the secret into the "secret" buffer. */ + p = secret; + while (len > 0) { + if (read(fd, p, 1) != 1 || *p == '\n') + break; + if (*p == '\r') + continue; + p++; + len--; + } + *p = '\0'; close(fd); - if (!found) return 0; - strlcpy(secret, pass, len); return 1; } static char *getpassf(char *filename) { - char buffer[100]; - int fd=0; STRUCT_STAT st; - int ok = 1; - extern int am_root; - char *envpw=getenv("RSYNC_PASSWORD"); + char buffer[512], *p; + int fd, n, ok = 1; + char *envpw = getenv("RSYNC_PASSWORD"); - if (!filename) return NULL; + if (!filename) + return NULL; - if ( (fd=open(filename,O_RDONLY)) == -1) { + if ((fd = open(filename,O_RDONLY)) < 0) { rsyserr(FERROR, errno, "could not open password file \"%s\"",filename); - if (envpw) rprintf(FERROR,"falling back to RSYNC_PASSWORD environment variable.\n"); + if (envpw) + rprintf(FERROR, "falling back to RSYNC_PASSWORD environment variable.\n"); return NULL; } - + if (do_stat(filename, &st) == -1) { rsyserr(FERROR, errno, "stat(%s)", filename); ok = 0; } else if ((st.st_mode & 06) != 0) { rprintf(FERROR,"password file must not be other-accessible\n"); ok = 0; - } else if (am_root && (st.st_uid != 0)) { + } else if (am_root && st.st_uid != 0) { rprintf(FERROR,"password file must be owned by root when running as root\n"); ok = 0; } if (!ok) { rprintf(FERROR,"continuing without password file\n"); - if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n"); + if (envpw) + rprintf(FERROR, "using RSYNC_PASSWORD environment variable.\n"); close(fd); return NULL; } - if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n"); + if (envpw) + rprintf(FERROR, "RSYNC_PASSWORD environment variable ignored\n"); - buffer[sizeof(buffer)-1]='\0'; - if (read(fd,buffer,sizeof(buffer)-1) > 0) - { - char *p = strtok(buffer,"\n\r"); - close(fd); - if (p) p = strdup(p); - return p; - } + n = read(fd, buffer, sizeof buffer - 1); + close(fd); + if (n > 0) { + buffer[n] = '\0'; + if ((p = strtok(buffer, "\n\r")) != NULL) + return strdup(p); + } return NULL; } @@ -186,7 +199,7 @@ { char buf[16]; - sum_init(); + sum_init(0); sum_update(in, strlen(in)); sum_update(challenge, strlen(challenge)); sum_end(buf); @@ -194,15 +207,12 @@ base64_encode(buf, 16, out); } -/* possible negotiate authentication with the client. Use "leader" to - start off the auth if necessary - - return NULL if authentication failed - - return "" if anonymous access - - otherwise return username -*/ +/* Possibly negotiate authentication with the client. Use "leader" to + * start off the auth if necessary. + * + * Return NULL if authentication failed. Return "" if anonymous access. + * Otherwise return username. + */ char *auth_server(int f_in, int f_out, int module, char *addr, char *leader) { char *users = lp_auth_users(module); @@ -216,46 +226,46 @@ char *tok; /* if no auth list then allow anyone in! */ - if (!users || !*users) return ""; + if (!users || !*users) + return ""; gen_challenge(addr, challenge); - + base64_encode(challenge, 16, b64_challenge); io_printf(f_out, "%s%s\n", leader, b64_challenge); - if (!read_line(f_in, line, sizeof(line)-1)) { + if (!read_line(f_in, line, sizeof line - 1)) return NULL; - } - memset(user, 0, sizeof(user)); - memset(pass, 0, sizeof(pass)); + memset(user, 0, sizeof user); + memset(pass, 0, sizeof pass); - if (sscanf(line,"%99s %29s", user, pass) != 2) { + if (sscanf(line,"%99s %29s", user, pass) != 2) return NULL; - } - + users = strdup(users); - if (!users) return NULL; + if (!users) + return NULL; for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) { - if (wildmatch(tok, user)) break; + if (wildmatch(tok, user)) + break; } free(users); - if (!tok) { + if (!tok) return NULL; - } - - memset(secret, 0, sizeof(secret)); - if (!get_secret(module, user, secret, sizeof(secret)-1)) { - memset(secret, 0, sizeof(secret)); + + memset(secret, 0, sizeof secret); + if (!get_secret(module, user, secret, sizeof secret - 1)) { + memset(secret, 0, sizeof secret); return NULL; } generate_hash(secret, b64_challenge, pass2); - memset(secret, 0, sizeof(secret)); - + memset(secret, 0, sizeof secret); + if (strcmp(pass, pass2) == 0) return user; @@ -267,12 +277,12 @@ { char *pass; char pass2[30]; - extern char *password_file; if (!user || !*user) user = "nobody"; - if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) { + if (!(pass = getpassf(password_file)) + && !(pass = getenv("RSYNC_PASSWORD"))) { /* XXX: cyeoh says that getpass is deprecated, because * it may return a truncated password on some systems, * and it is not in the LSB. @@ -285,9 +295,8 @@ pass = getpass("Password: "); } - if (!pass || !*pass) { + if (!pass) pass = ""; - } generate_hash(pass, challenge, pass2); io_printf(fd, "%s %s\n", user, pass2); diff -urN --exclude=patches rsync-2.6.2/backup.c rsync-2.6.3/backup.c --- rsync-2.6.2/backup.c 2004-03-13 12:18:03.000000000 -0800 +++ rsync-2.6.3/backup.c 2004-09-20 12:46:45.000000000 -0700 @@ -33,22 +33,38 @@ extern int preserve_links; extern int preserve_hard_links; extern int orig_umask; +extern int safe_symlinks; + +/* make a complete pathname for backup file */ +char *get_backup_name(char *fname) +{ + if (backup_dir) { + if (stringjoin(backup_dir_buf + backup_dir_len, backup_dir_remainder, + fname, backup_suffix, NULL) < backup_dir_remainder) + return backup_dir_buf; + } else { + if (stringjoin(backup_dir_buf, MAXPATHLEN, + fname, backup_suffix, NULL) < MAXPATHLEN) + return backup_dir_buf; + } + + rprintf(FERROR, "backup filename too long\n"); + return NULL; +} /* simple backup creates a backup with a suffix in the same directory */ static int make_simple_backup(char *fname) { - char fnamebak[MAXPATHLEN]; + char *fnamebak = get_backup_name(fname); - if (stringjoin(fnamebak, sizeof fnamebak, fname, backup_suffix, NULL) - >= sizeof fnamebak) { - rprintf(FERROR, "backup filename too long\n"); + if (!fnamebak) return 0; - } if (do_rename(fname, fnamebak) != 0) { /* cygwin (at least version b19) reports EINVAL */ if (errno != ENOENT && errno != EINVAL) { - rsyserr(FERROR, errno, "rename %s to backup %s", fname, fnamebak); + rsyserr(FERROR, errno, + "rename %s to backup %s", fname, fnamebak); return 0; } } else if (verbose > 1) { @@ -83,9 +99,9 @@ if (do_mkdir(fullpath, 0777 & ~orig_umask) == 0) break; if (errno != ENOENT) { - rprintf(FERROR, - "make_bak_dir mkdir %s failed: %s\n", - full_fname(fullpath), strerror(errno)); + rsyserr(FERROR, errno, + "make_bak_dir mkdir %s failed", + full_fname(fullpath)); goto failure; } } @@ -96,12 +112,11 @@ if (p >= rel) { /* Try to transfer the directory settings of the * actual dir that the files are coming from. */ - if (do_lstat(rel, &st) != 0) { - rprintf(FERROR, - "make_bak_dir stat %s failed: %s\n", - full_fname(rel), strerror(errno)); + if (do_stat(rel, &st) < 0) { + rsyserr(FERROR, errno, + "make_bak_dir stat %s failed", + full_fname(rel)); } else { - set_modtime(fullpath, st.st_mtime); do_lchown(fullpath, st.st_uid, st.st_gid); do_chmod(fullpath, st.st_mode); } @@ -111,9 +126,8 @@ if (p == end) break; if (do_mkdir(fullpath, 0777 & ~orig_umask) < 0) { - rprintf(FERROR, - "make_bak_dir mkdir %s failed: %s\n", - full_fname(fullpath), strerror(errno)); + rsyserr(FERROR, errno, "make_bak_dir mkdir %s failed", + full_fname(fullpath)); goto failure; } } @@ -143,36 +157,34 @@ { STRUCT_STAT st; struct file_struct *file; + char *buf; int kept = 0; int ret_code; /* return if no file to keep */ #if SUPPORT_LINKS - if (do_lstat(fname, &st)) return 1; + ret_code = do_lstat(fname, &st); #else - if (do_stat(fname, &st)) return 1; + ret_code = do_stat(fname, &st); #endif + if (ret_code < 0) + return 1; - file = make_file(fname, NULL, NO_EXCLUDES); + if (!(file = make_file(fname, NULL, NO_EXCLUDES))) + return 1; /* the file could have disappeared */ - /* the file could have disappeared */ - if (!file) return 1; - - /* make a complete pathname for backup file */ - if (stringjoin(backup_dir_buf + backup_dir_len, backup_dir_remainder, - fname, backup_suffix, NULL) >= backup_dir_remainder) { - rprintf(FERROR, "keep_backup filename too long\n"); + if (!(buf = get_backup_name(fname))) return 0; - } #ifdef HAVE_MKNOD /* Check to see if this is a device file, or link */ if (IS_DEVICE(file->mode)) { if (am_root && preserve_devices) { - make_bak_dir(backup_dir_buf); - if (do_mknod(backup_dir_buf, file->mode, file->u.rdev) != 0) { - rprintf(FERROR, "mknod %s failed: %s\n", - full_fname(backup_dir_buf), strerror(errno)); + if (do_mknod(buf, file->mode, file->u.rdev) < 0 + && (errno != ENOENT || make_bak_dir(buf) < 0 + || do_mknod(buf, file->mode, file->u.rdev) < 0)) { + rsyserr(FERROR, errno, "mknod %s failed", + full_fname(buf)); } else if (verbose > 2) { rprintf(FINFO, "make_backup: DEVICE %s successful.\n", @@ -186,10 +198,14 @@ if (!kept && S_ISDIR(file->mode)) { /* make an empty directory */ - make_bak_dir(backup_dir_buf); - do_mkdir(backup_dir_buf, file->mode); - ret_code = do_rmdir(fname); + if (do_mkdir(buf, file->mode) < 0 + && (errno != ENOENT || make_bak_dir(buf) < 0 + || do_mkdir(buf, file->mode) < 0)) { + rsyserr(FINFO, errno, "mkdir %s failed", + full_fname(buf)); + } + ret_code = do_rmdir(fname); if (verbose > 2) { rprintf(FINFO, "make_backup: RMDIR %s returns %i\n", full_fname(fname), ret_code); @@ -199,18 +215,18 @@ #if SUPPORT_LINKS if (!kept && preserve_links && S_ISLNK(file->mode)) { - extern int safe_symlinks; - if (safe_symlinks && unsafe_symlink(file->u.link, backup_dir_buf)) { + if (safe_symlinks && unsafe_symlink(file->u.link, buf)) { if (verbose) { rprintf(FINFO, "ignoring unsafe symlink %s -> %s\n", - full_fname(backup_dir_buf), file->u.link); + full_fname(buf), file->u.link); } kept = 1; } - make_bak_dir(backup_dir_buf); - if (do_symlink(file->u.link, backup_dir_buf) != 0) { - rprintf(FERROR, "link %s -> %s : %s\n", - full_fname(backup_dir_buf), file->u.link, strerror(errno)); + if (do_symlink(file->u.link, buf) < 0 + && (errno != ENOENT || make_bak_dir(buf) < 0 + || do_symlink(file->u.link, buf) < 0)) { + rsyserr(FERROR, errno, "link %s -> \"%s\"", + full_fname(buf), file->u.link); } do_unlink(fname); kept = 1; @@ -220,20 +236,25 @@ if (!kept && !S_ISREG(file->mode)) { rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname); + return 1; } /* move to keep tree if a file */ if (!kept) { - if (robust_move(fname, backup_dir_buf) != 0) { - rprintf(FERROR, "keep_backup failed: %s -> \"%s\": %s\n", - full_fname(fname), backup_dir_buf, strerror(errno)); + if (robust_move(fname, buf) != 0) { + rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"", + full_fname(fname), buf); + } else if (st.st_nlink > 1) { + /* If someone has hard-linked the file into the backup + * dir, rename() might return success but do nothing! */ + robust_unlink(fname); /* Just in case... */ } } - set_perms(backup_dir_buf, file, NULL, 0); + set_perms(buf, file, NULL, 0); free(file); if (verbose > 1) - rprintf(FINFO, "keep_backup %s -> %s\n", fname, backup_dir_buf); + rprintf(FINFO, "backed up %s to %s\n", fname, buf); return 1; } diff -urN --exclude=patches rsync-2.6.2/batch.c rsync-2.6.3/batch.c --- rsync-2.6.2/batch.c 2004-03-05 23:45:52.000000000 -0800 +++ rsync-2.6.3/batch.c 2004-08-01 21:50:33.000000000 -0700 @@ -8,329 +8,171 @@ #include "rsync.h" #include -extern char *batch_prefix; -extern int csum_length; -extern int protocol_version; -extern struct stats stats; - -struct file_list *batch_flist; - -static char rsync_flist_file[] = ".rsync_flist"; -static char rsync_csums_file[] = ".rsync_csums"; -static char rsync_delta_file[] = ".rsync_delta"; -static char rsync_argvs_file[] = ".rsync_argvs"; +extern char *batch_name; +extern int eol_nulls; +extern int recurse; +extern int preserve_links; +extern int preserve_hard_links; +extern int preserve_devices; +extern int preserve_uid; +extern int preserve_gid; +extern int always_checksum; -static int f_csums = -1; -static int f_delta = -1; +extern struct exclude_list_struct exclude_list; -void write_batch_flist_info(int flist_count, struct file_struct **files) -{ - char filename[MAXPATHLEN]; - int i, f, save_pv; - int64 save_written; - - stringjoin(filename, sizeof filename, - batch_prefix, rsync_flist_file, NULL); +static int *flag_ptr[] = { + &recurse, + &preserve_uid, + &preserve_gid, + &preserve_links, + &preserve_devices, + &preserve_hard_links, + &always_checksum, + NULL +}; - f = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); - if (f < 0) { - rprintf(FERROR, "Batch file %s open error: %s\n", - filename, strerror(errno)); - exit_cleanup(1); - } +static char *flag_name[] = { + "--recurse (-r)", + "--owner (-o)", + "--group (-g)", + "--links (-l)", + "--devices (-D)", + "--hard-links (-H)", + "--checksum (-c)", + NULL +}; - save_written = stats.total_written; - save_pv = protocol_version; - protocol_version = PROTOCOL_VERSION; - write_int(f, protocol_version); - write_int(f, flist_count); - - for (i = 0; i < flist_count; i++) { - send_file_entry(files[i], f, - files[i]->flags & FLAG_TOP_DIR ? XMIT_TOP_DIR : 0); - } - send_file_entry(NULL, f, 0); - - protocol_version = save_pv; - stats.total_written = save_written; - - close(f); -} - - -void write_batch_argvs_file(int argc, char *argv[]) +void write_stream_flags(int fd) { - int f; - int i; - char buff[256]; /* XXX */ - char buff2[MAXPATHLEN + 6]; - char filename[MAXPATHLEN]; - - stringjoin(filename, sizeof filename, - batch_prefix, rsync_argvs_file, NULL); - - f = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IEXEC); - if (f < 0) { - rprintf(FERROR, "Batch file %s open error: %s\n", - filename, strerror(errno)); - exit_cleanup(1); - } - buff[0] = '\0'; + int i, flags; - /* Write argvs info to batch file */ - - for (i = 0; i < argc; ++i) { - if (i == argc - 2) /* Skip source directory on cmdline */ - continue; - /* - * FIXME: - * I think directly manipulating argv[] is probably bogus - */ - if (!strncmp(argv[i], "--write-batch", - strlen("--write-batch"))) { - /* Safer to change it here than script */ - /* - * Change to --read-batch=prefix - * to get ready for remote - */ - strlcat(buff, "--read-batch=", sizeof buff); - strlcat(buff, batch_prefix, sizeof buff); - } else - if (i == argc - 1) { - snprintf(buff2, sizeof buff2, "${1:-%s}", argv[i]); - strlcat(buff, buff2, sizeof buff); - } - else { - strlcat(buff, argv[i], sizeof buff); - } - - if (i < (argc - 1)) { - strlcat(buff, " ", sizeof buff); - } - } - strlcat(buff, "\n", sizeof buff); - if (!write(f, buff, strlen(buff))) { - rprintf(FERROR, "Batch file %s write error: %s\n", - filename, strerror(errno)); - close(f); - exit_cleanup(1); + /* Start the batch file with a bitmap of data-stream-affecting + * flags. */ + for (i = 0, flags = 0; flag_ptr[i]; i++) { + if (*flag_ptr[i]) + flags |= 1 << i; } - close(f); + write_int(fd, flags); } -struct file_list *create_flist_from_batch(void) +void read_stream_flags(int fd) { - char filename[MAXPATHLEN]; - unsigned short flags; - int i, f, save_pv; - int64 save_read; - - stringjoin(filename, sizeof filename, - batch_prefix, rsync_flist_file, NULL); - - f = do_open(filename, O_RDONLY, 0); - if (f < 0) { - rprintf(FERROR, "Batch file %s open error: %s\n", - filename, strerror(errno)); - exit_cleanup(1); - } - - batch_flist = flist_new(WITH_HLINK, "create_flist_from_batch"); - - save_read = stats.total_read; - save_pv = protocol_version; - protocol_version = read_int(f); - - batch_flist->count = read_int(f); - flist_expand(batch_flist); - - for (i = 0; (flags = read_byte(f)) != 0; i++) { - if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS)) - flags |= read_byte(f) << 8; - receive_file_entry(&batch_flist->files[i], flags, batch_flist, f); - } - receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */ + int i, flags; - protocol_version = save_pv; - stats.total_read = save_read; - - return batch_flist; -} - -void write_batch_csums_file(void *buff, int bytes_to_write) -{ - if (write(f_csums, buff, bytes_to_write) < 0) { - rprintf(FERROR, "Batch file write error: %s\n", - strerror(errno)); - close(f_csums); - exit_cleanup(1); + for (i = 0, flags = read_int(fd); flag_ptr[i]; i++) { + int set = flags & (1 << i) ? 1 : 0; + if (*flag_ptr[i] != set) { + if (verbose) { + rprintf(FINFO, + "%sing the %s option to match the batchfile.\n", + set ? "Sett" : "Clear", flag_name[i]); + } + *flag_ptr[i] = set; + } } } -void close_batch_csums_file(void) +static void write_arg(int fd, char *arg) { - close(f_csums); - f_csums = -1; -} + char *x, *s; - -/** - * Write csum info to batch file - * - * @todo This will break if s->count is ever larger than maxint. The - * batch code should probably be changed to consistently use the - * variable-length integer routines, which is probably a compatible - * change. - **/ -void write_batch_csum_info(int *flist_entry, struct sum_struct *s) -{ - size_t i; - int int_count; - char filename[MAXPATHLEN]; - - if (f_csums < 0) { - stringjoin(filename, sizeof filename, - batch_prefix, rsync_csums_file, NULL); - - f_csums = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR); - if (f_csums < 0) { - rprintf(FERROR, "Batch file %s open error: %s\n", - filename, strerror(errno)); - close(f_csums); - exit_cleanup(1); - } + if (*arg == '-' && (x = strchr(arg, '=')) != NULL) { + write(fd, arg, x - arg + 1); + arg += x - arg + 1; } - write_batch_csums_file(flist_entry, sizeof (int)); - int_count = s ? (int) s->count : 0; - write_batch_csums_file(&int_count, sizeof int_count); - - if (s) { - for (i = 0; i < s->count; i++) { - write_batch_csums_file(&s->sums[i].sum1, - sizeof (uint32)); - write_batch_csums_file(s->sums[i].sum2, csum_length); + if (strpbrk(arg, " \"'&;|[]()$#!*?^\\") != NULL) { + write(fd, "'", 1); + for (s = arg; (x = strchr(s, '\'')) != NULL; s = x + 1) { + write(fd, s, x - s + 1); + write(fd, "'", 1); } + write(fd, s, strlen(s)); + write(fd, "'", 1); + return; } -} - -int read_batch_csums_file(char *buff, int len) -{ - int bytes_read; - if ((bytes_read = read(f_csums, buff, len)) < 0) { - rprintf(FERROR, "Batch file read error: %s\n", strerror(errno)); - close(f_csums); - exit_cleanup(1); - } - return bytes_read; + write(fd, arg, strlen(arg)); } -void read_batch_csum_info(int flist_entry, struct sum_struct *s, - int *checksums_match) +static void write_excludes(int fd) { - int i; - int file_flist_entry; - int file_chunk_ct; - uint32 file_sum1; - char file_sum2[SUM_LENGTH]; - char filename[MAXPATHLEN]; - - if (f_csums < 0) { - stringjoin(filename, sizeof filename, - batch_prefix, rsync_csums_file, NULL); - - f_csums = do_open(filename, O_RDONLY, 0); - if (f_csums < 0) { - rprintf(FERROR, "Batch file %s open error: %s\n", - filename, strerror(errno)); - close(f_csums); - exit_cleanup(1); - } - } - - read_batch_csums_file((char *) &file_flist_entry, sizeof (int)); - if (file_flist_entry != flist_entry) { - rprintf(FINFO, "file_flist_entry (%d) != flist_entry (%d)\n", - file_flist_entry, flist_entry); - close(f_csums); - exit_cleanup(1); + struct exclude_struct *ent; - } else { - read_batch_csums_file((char *) &file_chunk_ct, sizeof (int)); - *checksums_match = 1; - for (i = 0; i < file_chunk_ct; i++) { - read_batch_csums_file((char *) &file_sum1, - sizeof (uint32)); - read_batch_csums_file(file_sum2, csum_length); - - if ((s->sums[i].sum1 != file_sum1) - || memcmp(s->sums[i].sum2, file_sum2, csum_length)) - *checksums_match = 0; - } /* end for */ + write_sbuf(fd, " <<'#E#'\n"); + for (ent = exclude_list.head; ent; ent = ent->next) { + char *p = ent->pattern; + if (ent->match_flags & MATCHFLG_INCLUDE) + write_buf(fd, "+ ", 2); + else if (((*p == '-' || *p == '+') && p[1] == ' ') + || *p == '#' || *p == ';') + write_buf(fd, "- ", 2); + write_sbuf(fd, p); + if (ent->match_flags & MATCHFLG_DIRECTORY) + write_byte(fd, '/'); + write_byte(fd, eol_nulls ? 0 : '\n'); } + if (eol_nulls) + write_sbuf(fd, ";\n"); + write_sbuf(fd, "#E#"); } -void write_batch_delta_file(char *buff, int bytes_to_write) +/* This routine tries to write out an equivalent --read-batch command + * given the user's --write-batch args. However, it doesn't really + * understand most of the options, so it uses some overly simple + * heuristics to munge the command line into something that will + * (hopefully) work. */ +void write_batch_shell_file(int argc, char *argv[], int file_arg_cnt) { - char filename[MAXPATHLEN]; + int fd, i; + char *p, filename[MAXPATHLEN]; - if (f_delta < 0) { - stringjoin(filename, sizeof filename, - batch_prefix, rsync_delta_file, NULL); - - f_delta = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR); - if (f_delta < 0) { - rprintf(FERROR, "Batch file %s open error: %s\n", - filename, strerror(errno)); - exit_cleanup(1); - } - } - - if (write(f_delta, buff, bytes_to_write) < 0) { - rprintf(FERROR, "Batch file %s write error: %s\n", - filename, strerror(errno)); - close(f_delta); + stringjoin(filename, sizeof filename, + batch_name, ".sh", NULL); + fd = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IEXEC); + if (fd < 0) { + rsyserr(FERROR, errno, "Batch file %s open error", filename); exit_cleanup(1); } -} -void close_batch_delta_file(void) -{ - close(f_delta); - f_delta = -1; -} - -int read_batch_delta_file(char *buff, int len) -{ - int bytes_read; - char filename[MAXPATHLEN]; - - if (f_delta < 0) { - stringjoin(filename, sizeof filename, - batch_prefix, rsync_delta_file, NULL); - - f_delta = do_open(filename, O_RDONLY, 0); - if (f_delta < 0) { - rprintf(FERROR, "Batch file %s open error: %s\n", - filename, strerror(errno)); - close(f_delta); - exit_cleanup(1); + /* Write argvs info to BATCH.sh file */ + write_arg(fd, argv[0]); + if (exclude_list.head) + write_sbuf(fd, " --exclude-from=-"); + for (i = 1; i < argc - file_arg_cnt; i++) { + p = argv[i]; + if (strncmp(p, "--files-from", 12) == 0 + || strncmp(p, "--include", 9) == 0 + || strncmp(p, "--exclude", 9) == 0) { + if (strchr(p, '=') == NULL) + i++; + continue; } + write(fd, " ", 1); + if (strncmp(p, "--write-batch", 13) == 0) { + write(fd, "--read-batch", 12); + if (p[13] == '=') { + write(fd, "=", 1); + write_arg(fd, p + 14); + } + } else + write_arg(fd, p); } - - bytes_read = read(f_delta, buff, len); - if (bytes_read < 0) { - rprintf(FERROR, "Batch file %s read error: %s\n", - filename, strerror(errno)); - close(f_delta); + if ((p = find_colon(argv[argc - 1])) != NULL) { + if (*++p == ':') + p++; + } else + p = argv[argc - 1]; + write(fd, " ${1:-", 6); + write_arg(fd, p); + write_byte(fd, '}'); + if (exclude_list.head) + write_excludes(fd); + if (write(fd, "\n", 1) != 1 || close(fd) < 0) { + rsyserr(FERROR, errno, "Batch file %s write error", filename); exit_cleanup(1); } - - return bytes_read; } void show_flist(int index, struct file_struct **fptr) diff -urN --exclude=patches rsync-2.6.2/checksum.c rsync-2.6.3/checksum.c --- rsync-2.6.2/checksum.c 2004-02-09 10:32:52.000000000 -0800 +++ rsync-2.6.3/checksum.c 2004-08-03 08:37:54.000000000 -0700 @@ -1,17 +1,17 @@ -/* +/* Copyright (C) Andrew Tridgell 1996 Copyright (C) Paul Mackerras 1996 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -38,9 +38,9 @@ s1 = s2 = 0; for (i = 0; i < (len-4); i+=4) { - s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] + + s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] + 10*CHAR_OFFSET; - s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET); + s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET); } for (; i < len; i++) { s1 += (buf[i]+CHAR_OFFSET); s2 += s1; @@ -62,15 +62,15 @@ len1 = len; if (!buf1) out_of_memory("get_checksum2"); } - + mdfour_begin(&m); - + memcpy(buf1,buf,len); if (checksum_seed) { SIVAL(buf1,len,checksum_seed); len += 4; } - + for(i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) { mdfour_update(&m, (uchar *)(buf1+i), CSUM_CHUNK); } @@ -83,7 +83,7 @@ if (len - i > 0 || protocol_version >= 27) { mdfour_update(&m, (uchar *)(buf1+i), (len-i)); } - + mdfour_result(&m, (uchar *)sum); } @@ -94,35 +94,29 @@ struct map_struct *buf; int fd; OFF_T len = size; - char tmpchunk[CSUM_CHUNK]; struct mdfour m; - + memset(sum,0,MD4_SUM_LENGTH); - + fd = do_open(fname, O_RDONLY, 0); - if (fd == -1) return; - - buf = map_file(fd,size); - + if (fd == -1) + return; + + buf = map_file(fd, size, MAX_MAP_SIZE, CSUM_CHUNK); + mdfour_begin(&m); for(i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) { - memcpy(tmpchunk, map_ptr(buf,i,CSUM_CHUNK), CSUM_CHUNK); - mdfour_update(&m, (uchar *)tmpchunk, CSUM_CHUNK); + mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK), + CSUM_CHUNK); } - /* - * Prior to version 27 an incorrect MD4 checksum was computed + /* Prior to version 27 an incorrect MD4 checksum was computed * by failing to call mdfour_tail() for block sizes that * are multiples of 64. This is fixed by calling mdfour_update() - * even when there are no more bytes. - */ - if (len - i > 0) { - memcpy(tmpchunk, map_ptr(buf,i,len-i), len-i); - } - if (len - i > 0 || protocol_version >= 27) { - mdfour_update(&m, (uchar *)tmpchunk, (len-i)); - } + * even when there are no more bytes. */ + if (len - i > 0 || protocol_version >= 27) + mdfour_update(&m, (uchar *)map_ptr(buf, i, len-i), len-i); mdfour_result(&m, (uchar *)sum); @@ -135,13 +129,13 @@ static char sumrbuf[CSUM_CHUNK]; static struct mdfour md; -void sum_init(void) +void sum_init(int seed) { char s[4]; mdfour_begin(&md); - sumresidue=0; - SIVAL(s,0,checksum_seed); - sum_update(s,4); + sumresidue = 0; + SIVAL(s, 0, seed); + sum_update(s, 4); } /** @@ -178,7 +172,7 @@ sumresidue = len-i; memcpy(sumrbuf,p+i,sumresidue); } else { - sumresidue = 0; + sumresidue = 0; } } diff -urN --exclude=patches rsync-2.6.2/cleanup.c rsync-2.6.3/cleanup.c --- rsync-2.6.2/cleanup.c 2004-01-27 00:14:33.000000000 -0800 +++ rsync-2.6.3/cleanup.c 2004-07-29 09:06:38.000000000 -0700 @@ -1,19 +1,19 @@ /* -*- c-file-style: "linux" -*- - + Copyright (C) 1996-2000 by Andrew Tridgell Copyright (C) Paul Mackerras 1996 Copyright (C) 2002 by Martin Pool - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -21,6 +21,10 @@ #include "rsync.h" +extern int io_error; +extern int keep_partial; +extern int log_got_error; + /** * Close all open sockets and files, allowing a (somewhat) graceful * shutdown() of socket connections. This eliminates the abortive @@ -38,9 +42,8 @@ for (fd = max_fd; fd >= 0; fd--) { ret = fstat(fd,&st); if (fstat(fd,&st) == 0) { - if (is_a_socket(fd)) { + if (is_a_socket(fd)) ret = shutdown(fd, 2); - } ret = close(fd); } } @@ -65,15 +68,13 @@ * --partial is selected. We need to ensure that the partial file is * kept if any real data has been transferred. **/ -int cleanup_got_literal=0; +int cleanup_got_literal = 0; static char *cleanup_fname; static char *cleanup_new_fname; static struct file_struct *cleanup_file; -static int cleanup_fd1, cleanup_fd2; -static struct map_struct *cleanup_buf; +static int cleanup_fd_r, cleanup_fd_w; static pid_t cleanup_pid = 0; -extern int io_error; pid_t cleanup_child_pid = -1; @@ -85,8 +86,6 @@ void _exit_cleanup(int code, const char *file, int line) { int ocode = code; - extern int keep_partial; - extern int log_got_error; static int inside_cleanup = 0; if (inside_cleanup > 10) { @@ -98,37 +97,39 @@ signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); - if (verbose > 3) - rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): entered\n", + if (verbose > 3) { + rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): entered\n", code, file, line); + } if (cleanup_child_pid != -1) { int status; if (waitpid(cleanup_child_pid, &status, WNOHANG) == cleanup_child_pid) { status = WEXITSTATUS(status); - if (status > code) code = status; + if (status > code) + code = status; } } - if (cleanup_got_literal && cleanup_fname && keep_partial) { + if (cleanup_got_literal && cleanup_fname && keep_partial + && handle_partial_dir(cleanup_new_fname, PDIR_CREATE)) { char *fname = cleanup_fname; cleanup_fname = NULL; - if (cleanup_buf) unmap_file(cleanup_buf); - if (cleanup_fd1 != -1) close(cleanup_fd1); - if (cleanup_fd2 != -1) close(cleanup_fd2); - finish_transfer(cleanup_new_fname, fname, cleanup_file); + if (cleanup_fd_r != -1) + close(cleanup_fd_r); + if (cleanup_fd_w != -1) + close(cleanup_fd_w); + finish_transfer(cleanup_new_fname, fname, cleanup_file, 0); } io_flush(FULL_FLUSH); if (cleanup_fname) do_unlink(cleanup_fname); - if (code) { + if (code) kill_all(SIGUSR1); - } if (cleanup_pid && cleanup_pid == getpid()) { char *pidf = lp_pid_file(); - if (pidf && *pidf) { + if (pidf && *pidf) unlink(lp_pid_file()); - } } if (code == 0) { @@ -138,11 +139,13 @@ code = RERR_VANISHED; } - if (code) log_exit(code, file, line); + if (code) + log_exit(code, file, line); - if (verbose > 2) - rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): about to call exit(%d)\n", + if (verbose > 2) { + rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): about to call exit(%d)\n", ocode, file, line, code); + } close_all(); exit(code); @@ -156,14 +159,13 @@ void cleanup_set(char *fnametmp, char *fname, struct file_struct *file, - struct map_struct *buf, int fd1, int fd2) + int fd_r, int fd_w) { cleanup_fname = fnametmp; cleanup_new_fname = fname; cleanup_file = file; - cleanup_buf = buf; - cleanup_fd1 = fd1; - cleanup_fd2 = fd2; + cleanup_fd_r = fd_r; + cleanup_fd_w = fd_w; } void cleanup_set_pid(pid_t pid) diff -urN --exclude=patches rsync-2.6.2/clientname.c rsync-2.6.3/clientname.c --- rsync-2.6.2/clientname.c 2004-04-30 09:10:45.000000000 -0700 +++ rsync-2.6.3/clientname.c 2004-09-24 09:39:41.000000000 -0700 @@ -43,12 +43,11 @@ **/ char *client_addr(int fd) { - struct sockaddr_storage ss; - socklen_t length = sizeof ss; - char *ssh_client, *p; - int len; static char addr_buf[100]; static int initialised; + struct sockaddr_storage ss; + socklen_t length = sizeof ss; + char *ssh_info, *p; if (initialised) return addr_buf; @@ -57,14 +56,13 @@ if (am_server) { /* daemon over --rsh mode */ strcpy(addr_buf, "0.0.0.0"); - if ((ssh_client = getenv("SSH_CLIENT")) != NULL) { - /* truncate SSH_CLIENT to just IP address */ - if ((p = strchr(ssh_client, ' ')) != NULL) { - len = MIN((unsigned int) (p - ssh_client), - sizeof addr_buf - 1); - strncpy(addr_buf, ssh_client, len); - *(addr_buf + len) = '\0'; - } + if ((ssh_info = getenv("SSH_CONNECTION")) != NULL + || (ssh_info = getenv("SSH_CLIENT")) != NULL + || (ssh_info = getenv("SSH2_CLIENT")) != NULL) { + strlcpy(addr_buf, ssh_info, sizeof addr_buf); + /* Truncate the value to just the IP address. */ + if ((p = strchr(addr_buf, ' ')) != NULL) + *p = '\0'; } } else { client_sockaddr(fd, &ss, &length); @@ -124,7 +122,7 @@ hint.ai_socktype = SOCK_STREAM; if ((err = getaddrinfo(addr, NULL, &hint, &answer)) != 0) { - rprintf(FERROR, RSYNC_NAME ": malformed address %s: %s\n", + rprintf(FLOG, "malformed address %s: %s\n", addr, gai_strerror(err)); return name_buf; } @@ -147,8 +145,8 @@ client_sockaddr(fd, &ss, &ss_len); } - if (!lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf, - port_buf, sizeof port_buf)) + if (lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf, + port_buf, sizeof port_buf) == 0) check_name(fd, &ss, name_buf); return name_buf; @@ -170,8 +168,7 @@ if (getpeername(fd, (struct sockaddr *) ss, ss_len)) { /* FIXME: Can we really not continue? */ - rprintf(FERROR, RSYNC_NAME ": getpeername on fd%d failed: %s\n", - fd, strerror(errno)); + rsyserr(FLOG, errno, "getpeername on fd%d failed", fd); exit_cleanup(RERR_SOCKETIO); } @@ -226,9 +223,8 @@ NI_NAMEREQD | NI_NUMERICSERV); if (name_err != 0) { strcpy(name_buf, default_name); - rprintf(FERROR, RSYNC_NAME ": name lookup failed for %s: %s\n", - client_addr(fd), - gai_strerror(name_err)); + rprintf(FLOG, "name lookup failed for %s: %s\n", + client_addr(fd), gai_strerror(name_err)); return name_err; } @@ -249,8 +245,7 @@ const char fn[] = "compare_addrinfo_sockaddr"; if (ai->ai_family != ss_family) { - rprintf(FERROR, - "%s: response family %d != %d\n", + rprintf(FLOG, "%s: response family %d != %d\n", fn, ai->ai_family, ss_family); return 1; } @@ -274,8 +269,7 @@ sin2 = (const struct sockaddr_in6 *) ai->ai_addr; if (ai->ai_addrlen < sizeof (struct sockaddr_in6)) { - rprintf(FERROR, - "%s: too short sockaddr_in6; length=%d\n", + rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n", fn, ai->ai_addrlen); return 1; } @@ -321,8 +315,7 @@ hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(name_buf, NULL, &hints, &res0); if (error) { - rprintf(FERROR, - RSYNC_NAME ": forward name lookup for %s failed: %s\n", + rprintf(FLOG, "forward name lookup for %s failed: %s\n", name_buf, gai_strerror(error)); strcpy(name_buf, default_name); return error; @@ -339,23 +332,17 @@ if (!res0) { /* We hit the end of the list without finding an * address that was the same as ss. */ - rprintf(FERROR, RSYNC_NAME - ": no known address for \"%s\": " - "spoofed address?\n", - name_buf); + rprintf(FLOG, "no known address for \"%s\": " + "spoofed address?\n", name_buf); strcpy(name_buf, default_name); } else if (res == NULL) { /* We hit the end of the list without finding an * address that was the same as ss. */ - rprintf(FERROR, RSYNC_NAME - ": %s is not a known address for \"%s\": " - "spoofed address?\n", - client_addr(fd), - name_buf); + rprintf(FLOG, "%s is not a known address for \"%s\": " + "spoofed address?\n", client_addr(fd), name_buf); strcpy(name_buf, default_name); } freeaddrinfo(res0); return 0; } - diff -urN --exclude=patches rsync-2.6.2/clientserver.c rsync-2.6.3/clientserver.c --- rsync-2.6.2/clientserver.c 2004-04-14 16:33:34.000000000 -0700 +++ rsync-2.6.3/clientserver.c 2004-07-31 12:55:42.000000000 -0700 @@ -50,6 +50,7 @@ extern struct exclude_list_struct server_exclude_list; extern char *exclude_path_prefix; extern char *config_file; +extern char *files_from; char *auth_user; @@ -70,21 +71,21 @@ int start_socket_client(char *host, char *path, int argc, char *argv[]) { int fd, ret; - char *p, *user=NULL; + char *p, *user = NULL; - /* this is redundant with code in start_inband_exchange(), but - * this short-circuits a problem before we open a socket, and - * the extra check won't hurt */ + /* This is redundant with code in start_inband_exchange(), but this + * short-circuits a problem in the client before we open a socket, + * and the extra check won't hurt. */ if (*path == '/') { - rprintf(FERROR,"ERROR: The remote path must start with a module name not a /\n"); + rprintf(FERROR, + "ERROR: The remote path must start with a module name not a /\n"); return -1; } - p = strchr(host, '@'); - if (p) { + if ((p = strchr(host, '@')) != NULL) { user = host; host = p+1; - *p = 0; + *p = '\0'; } fd = open_socket_out_wrapped(host, rsync_port, bind_address, @@ -97,7 +98,8 @@ return ret < 0? ret : client_run(fd, fd, -1, argc, argv); } -int start_inband_exchange(char *user, char *path, int f_in, int f_out, int argc) +int start_inband_exchange(char *user, char *path, int f_in, int f_out, + int argc) { int i; char *sargs[MAX_ARGS]; @@ -109,12 +111,15 @@ list_only = 1; if (*path == '/') { - rprintf(FERROR, "ERROR: The remote path must start with a module name\n"); + rprintf(FERROR, + "ERROR: The remote path must start with a module name\n"); return -1; } - if (!user) user = getenv("USER"); - if (!user) user = getenv("LOGNAME"); + if (!user) + user = getenv("USER"); + if (!user) + user = getenv("LOGNAME"); /* set daemon_over_rsh to false since we need to build the * true set of args passed through the rsh/ssh connection; @@ -131,7 +136,7 @@ io_printf(f_out, "@RSYNCD: %d\n", protocol_version); - if (!read_line(f_in, line, sizeof(line)-1)) { + if (!read_line(f_in, line, sizeof line - 1)) { rprintf(FERROR, "rsync: did not see server greeting\n"); return -1; } @@ -155,7 +160,7 @@ kludge_around_eof = list_only && (protocol_version < 25); while (1) { - if (!read_line(f_in, line, sizeof(line)-1)) { + if (!read_line(f_in, line, sizeof line - 1)) { rprintf(FERROR, "rsync: didn't get server startup line\n"); return -1; } @@ -165,7 +170,8 @@ continue; } - if (strcmp(line,"@RSYNCD: OK") == 0) break; + if (strcmp(line,"@RSYNCD: OK") == 0) + break; if (strcmp(line,"@RSYNCD: EXIT") == 0) { /* This is sent by recent versions of the @@ -176,7 +182,7 @@ } if (strncmp(line, "@ERROR", 6) == 0) { - rprintf(FERROR,"%s\n", line); + rprintf(FERROR, "%s\n", line); /* This is always fatal; the server will now * close the socket. */ return RERR_STARTCLIENT; @@ -193,7 +199,7 @@ if (protocol_version < 23) { if (protocol_version == 22 || !am_sender) - io_start_multiplex_in(f_in); + io_start_multiplex_in(); } return 0; @@ -203,8 +209,9 @@ static int rsync_module(int f_in, int f_out, int i) { - int argc=0; - char *argv[MAX_ARGS]; + int argc = 0; + int maxargs; + char **argv; char **argp; char line[MAXPATHLEN]; uid_t uid = (uid_t)-2; /* canonically "nobody" */ @@ -214,31 +221,36 @@ char *host = client_name(f_in); char *name = lp_name(i); int use_chroot = lp_use_chroot(i); - int start_glob=0; + int start_glob = 0; int ret; - char *request=NULL; + char *request = NULL; if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) { - rprintf(FERROR,"rsync denied on module %s from %s (%s)\n", + rprintf(FLOG, "rsync denied on module %s from %s (%s)\n", name, host, addr); - io_printf(f_out, "@ERROR: access denied to %s from %s (%s)\n", - name, host, addr); + if (!lp_list(i)) + io_printf(f_out, "@ERROR: Unknown module '%s'\n", name); + else { + io_printf(f_out, + "@ERROR: access denied to %s from %s (%s)\n", + name, host, addr); + } return -1; } if (am_daemon && am_server) { - rprintf(FINFO, "rsync allowed access on module %s from %s (%s)\n", + rprintf(FLOG, "rsync allowed access on module %s from %s (%s)\n", name, host, addr); } if (!claim_connection(lp_lock_file(i), lp_max_connections(i))) { if (errno) { - rprintf(FERROR,"failed to open lock file %s : %s\n", - lp_lock_file(i), strerror(errno)); - io_printf(f_out, "@ERROR: failed to open lock file %s : %s\n", - lp_lock_file(i), strerror(errno)); + rsyserr(FLOG, errno, "failed to open lock file %s", + lp_lock_file(i)); + io_printf(f_out, "@ERROR: failed to open lock file %s\n", + lp_lock_file(i)); } else { - rprintf(FERROR,"max connections (%d) reached\n", + rprintf(FLOG, "max connections (%d) reached\n", lp_max_connections(i)); io_printf(f_out, "@ERROR: max connections (%d) reached - try again later\n", lp_max_connections(i)); @@ -246,11 +258,10 @@ return -1; } - auth_user = auth_server(f_in, f_out, i, addr, "@RSYNCD: AUTHREQD "); if (!auth_user) { - rprintf(FERROR,"auth failed on module %s from %s (%s)\n", + rprintf(FLOG, "auth failed on module %s from %s (%s)\n", name, host, addr); io_printf(f_out, "@ERROR: auth failed on module %s\n", name); return -1; @@ -263,8 +274,8 @@ if (am_root) { p = lp_uid(i); if (!name_to_uid(p, &uid)) { - if (!isdigit(* (unsigned char *) p)) { - rprintf(FERROR,"Invalid uid %s\n", p); + if (!isdigit(*(unsigned char *)p)) { + rprintf(FLOG, "Invalid uid %s\n", p); io_printf(f_out, "@ERROR: invalid uid %s\n", p); return -1; } @@ -273,8 +284,8 @@ p = lp_gid(i); if (!name_to_gid(p, &gid)) { - if (!isdigit(* (unsigned char *) p)) { - rprintf(FERROR,"Invalid gid %s\n", p); + if (!isdigit(*(unsigned char *)p)) { + rprintf(FLOG, "Invalid gid %s\n", p); io_printf(f_out, "@ERROR: invalid gid %s\n", p); return -1; } @@ -326,20 +337,20 @@ * in which case we fail. */ if (chroot(lp_path(i))) { - rsyserr(FERROR, errno, "chroot %s failed", lp_path(i)); + rsyserr(FLOG, errno, "chroot %s failed", lp_path(i)); io_printf(f_out, "@ERROR: chroot failed\n"); return -1; } if (!push_dir("/")) { - rsyserr(FERROR, errno, "chdir %s failed\n", lp_path(i)); + rsyserr(FLOG, errno, "chdir %s failed\n", lp_path(i)); io_printf(f_out, "@ERROR: chdir failed\n"); return -1; } } else { if (!push_dir(lp_path(i))) { - rsyserr(FERROR, errno, "chdir %s failed\n", lp_path(i)); + rsyserr(FLOG, errno, "chdir %s failed\n", lp_path(i)); io_printf(f_out, "@ERROR: chdir failed\n"); return -1; } @@ -358,7 +369,7 @@ * all their supplementary groups. */ if (setgid(gid)) { - rsyserr(FERROR, errno, "setgid %d failed", (int) gid); + rsyserr(FLOG, errno, "setgid %d failed", (int)gid); io_printf(f_out, "@ERROR: setgid failed\n"); return -1; } @@ -366,14 +377,14 @@ /* Get rid of any supplementary groups this process * might have inheristed. */ if (setgroups(1, &gid)) { - rsyserr(FERROR, errno, "setgroups failed"); + rsyserr(FLOG, errno, "setgroups failed"); io_printf(f_out, "@ERROR: setgroups failed\n"); return -1; } #endif if (setuid(uid)) { - rsyserr(FERROR, errno, "setuid %d failed", (int) uid); + rsyserr(FLOG, errno, "setuid %d failed", (int)uid); io_printf(f_out, "@ERROR: setuid failed\n"); return -1; } @@ -383,39 +394,39 @@ io_printf(f_out, "@RSYNCD: OK\n"); + maxargs = MAX_ARGS; + if (!(argv = new_array(char *, maxargs))) + out_of_memory("rsync_module"); argv[argc++] = "rsyncd"; while (1) { - if (!read_line(f_in, line, sizeof(line)-1)) { + if (!read_line(f_in, line, sizeof line - 1)) return -1; - } - if (!*line) break; + if (!*line) + break; p = line; - argv[argc] = strdup(p); - if (!argv[argc]) { - return -1; + if (argc == maxargs) { + maxargs += MAX_ARGS; + if (!(argv = realloc_array(argv, char *, maxargs))) + out_of_memory("rsync_module"); } + if (!(argv[argc] = strdup(p))) + out_of_memory("rsync_module"); if (start_glob) { if (start_glob == 1) { request = strdup(p); start_glob++; } - glob_expand(name, argv, &argc, MAX_ARGS); - } else { + glob_expand(name, &argv, &argc, &maxargs); + } else argc++; - } - if (strcmp(line,".") == 0) { + if (strcmp(line, ".") == 0) start_glob = 1; - } - - if (argc == MAX_ARGS) { - return -1; - } } argp = argv; @@ -426,12 +437,12 @@ if (request) { if (*auth_user) { - rprintf(FINFO,"rsync %s %s from %s@%s (%s)\n", - am_sender?"on":"to", + rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n", + am_sender ? "on" : "to", request, auth_user, host, addr); } else { - rprintf(FINFO,"rsync %s %s from %s (%s)\n", - am_sender?"on":"to", + rprintf(FLOG, "rsync %s %s from %s (%s)\n", + am_sender ? "on" : "to", request, host, addr); } free(request); @@ -443,27 +454,27 @@ verbose = lp_max_verbosity(); #endif - if (protocol_version < 23) { - if (protocol_version == 22 || am_sender) - io_start_multiplex_out(f_out); + if (protocol_version < 23 + && (protocol_version == 22 || am_sender)) + io_start_multiplex_out(); + else if (!ret) { + /* We have to get I/O multiplexing started so that we can + * get the error back to the client. This means getting + * the protocol setup finished first in later versions. */ + setup_protocol(f_out, f_in); + if (files_from && !am_sender && strcmp(files_from, "-") != 0) + write_byte(f_out, 0); + io_start_multiplex_out(); } - /* For later protocol versions, we don't start multiplexing - * until we've configured nonblocking in start_server. That - * means we're in a sticky situation now: there's no way to - * convey errors to the client. */ - - /* FIXME: Hold off on reporting option processing errors until - * we've set up nonblocking and multiplexed IO and can get the - * message back to them. */ if (!ret) { option_error(); + msleep(400); exit_cleanup(RERR_UNSUPPORTED); } - if (lp_timeout(i)) { + if (lp_timeout(i)) io_timeout = lp_timeout(i); - } start_server(f_in, f_out, argc, argp); @@ -477,9 +488,10 @@ int n = lp_numservices(); int i; - for (i=0;i= 25) io_printf(fd,"@RSYNCD: EXIT\n"); @@ -492,11 +504,12 @@ { char line[200]; char *motd; - int i = -1; + int i; - if (!lp_load(config_file, 0)) { + io_set_sock_fds(f_in, f_out); + + if (!lp_load(config_file, 0)) exit_cleanup(RERR_SYNTAX); - } log_init(); @@ -512,19 +525,19 @@ if (motd && *motd) { FILE *f = fopen(motd,"r"); while (f && !feof(f)) { - int len = fread(line, 1, sizeof(line)-1, f); + int len = fread(line, 1, sizeof line - 1, f); if (len > 0) { line[len] = 0; io_printf(f_out, "%s", line); } } - if (f) fclose(f); + if (f) + fclose(f); io_printf(f_out, "\n"); } - if (!read_line(f_in, line, sizeof(line)-1)) { + if (!read_line(f_in, line, sizeof line - 1)) return -1; - } if (sscanf(line,"@RSYNCD: %d", &remote_protocol) != 1) { io_printf(f_out, "@ERROR: protocol startup error\n"); @@ -533,28 +546,28 @@ if (protocol_version > remote_protocol) protocol_version = remote_protocol; - while (i == -1) { - line[0] = 0; - if (!read_line(f_in, line, sizeof(line)-1)) { - return -1; - } + line[0] = 0; + if (!read_line(f_in, line, sizeof line - 1)) + return -1; - if (!*line || strcmp(line,"#list")==0) { - send_listing(f_out); - return -1; - } + if (!*line || strcmp(line, "#list") == 0) { + send_listing(f_out); + return -1; + } - if (*line == '#') { - /* it's some sort of command that I don't understand */ - io_printf(f_out, "@ERROR: Unknown command '%s'\n", line); - return -1; - } + if (*line == '#') { + /* it's some sort of command that I don't understand */ + io_printf(f_out, "@ERROR: Unknown command '%s'\n", line); + return -1; + } - i = lp_number(line); - if (i == -1) { - io_printf(f_out, "@ERROR: Unknown module '%s'\n", line); - return -1; - } + if ((i = lp_number(line)) < 0) { + char *addr = client_addr(f_in); + char *host = client_name(f_in); + rprintf(FLOG, "unknown module '%s' tried from %s (%s)\n", + line, host, addr); + io_printf(f_out, "@ERROR: Unknown module '%s'\n", line); + return -1; } return rsync_module(f_in, f_out, i); @@ -571,7 +584,7 @@ /* we are running via inetd - close off stdout and * stderr so that library functions (and getopt) don't * try to use them. Redirect them to /dev/null */ - for (i=1;i<3;i++) { + for (i = 1; i < 3; i++) { close(i); open("/dev/null", O_RDWR); } @@ -582,13 +595,12 @@ if (!no_detach) become_daemon(); - if (!lp_load(config_file, 1)) { + if (!lp_load(config_file, 1)) exit_cleanup(RERR_SYNTAX); - } log_init(); - rprintf(FINFO, "rsyncd version %s starting, listening on port %d\n", + rprintf(FLOG, "rsyncd version %s starting, listening on port %d\n", RSYNC_VERSION, rsync_port); /* TODO: If listening on a particular address, then show that * address too. In fact, why not just do inet_ntop on the diff -urN --exclude=patches rsync-2.6.2/compat.c rsync-2.6.3/compat.c --- rsync-2.6.2/compat.c 2004-02-09 10:32:53.000000000 -0800 +++ rsync-2.6.3/compat.c 2004-07-21 16:59:22.000000000 -0700 @@ -27,36 +27,27 @@ int remote_protocol = 0; +extern int verbose; extern int am_server; - -extern int preserve_links; -extern int preserve_perms; -extern int preserve_devices; -extern int preserve_uid; -extern int preserve_gid; -extern int preserve_times; -extern int always_checksum; +extern int am_sender; +extern int read_batch; extern int checksum_seed; - extern int protocol_version; -extern int verbose; - -extern int read_batch; -extern int write_batch; void setup_protocol(int f_out,int f_in) { if (remote_protocol == 0) { - if (am_server) { - remote_protocol = read_int(f_in); - write_int(f_out, protocol_version); - } else { + if (!read_batch) write_int(f_out, protocol_version); - remote_protocol = read_int(f_in); - } + remote_protocol = read_int(f_in); if (protocol_version > remote_protocol) protocol_version = remote_protocol; } + if (read_batch && remote_protocol > protocol_version) { + rprintf(FERROR, "The protocol version in the batch file is too new (%d > %d).\n", + remote_protocol, protocol_version); + exit_cleanup(RERR_PROTOCOL); + } if (verbose > 3) { rprintf(FINFO, "(%s) Protocol versions: remote=%d, negotiated=%d\n", diff -urN --exclude=patches rsync-2.6.2/config.h.in rsync-2.6.3/config.h.in --- rsync-2.6.2/config.h.in 2004-04-29 12:40:39.000000000 -0700 +++ rsync-2.6.3/config.h.in 2004-08-02 14:54:49.000000000 -0700 @@ -68,6 +68,9 @@ /* Define to 1 if you have the `fstat' function. */ #undef HAVE_FSTAT +/* Define to 1 if you have the `ftruncate' function. */ +#undef HAVE_FTRUNCATE + /* Define if you have the `getaddrinfo' function. */ #undef HAVE_GETADDRINFO @@ -155,6 +158,9 @@ /* Define to 1 if you have the `mknod' function. */ #undef HAVE_MKNOD +/* Define to 1 if you have the `mkstemp64' function. */ +#undef HAVE_MKSTEMP64 + /* Define to 1 if you have the `mtrace' function. */ #undef HAVE_MTRACE @@ -167,6 +173,9 @@ /* */ #undef HAVE_OFF64_T +/* Define to 1 if you have the `open64' function. */ +#undef HAVE_OPEN64 + /* Define to 1 if you have the `readlink' function. */ #undef HAVE_READLINK diff -urN --exclude=patches rsync-2.6.2/configure rsync-2.6.3/configure --- rsync-2.6.2/configure 2004-04-30 11:02:43.000000000 -0700 +++ rsync-2.6.3/configure 2004-09-30 09:35:56.000000000 -0700 @@ -1314,7 +1314,7 @@ -RSYNC_VERSION=2.6.2 +RSYNC_VERSION=2.6.3 { echo "$as_me:$LINENO: Configuring rsync $RSYNC_VERSION" >&5 echo "$as_me: Configuring rsync $RSYNC_VERSION" >&6;} @@ -9331,10 +9331,14 @@ + + + for ac_func in waitpid wait4 getcwd strdup strerror chown chmod mknod mkfifo \ - fchmod fstat strchr readlink link utime utimes strftime mtrace \ + fchmod fstat strchr readlink link utime utimes strftime mtrace ftruncate \ memmove lchown vsnprintf snprintf asprintf setsid glob strpbrk \ - strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid + strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \ + open64 mkstemp64 do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 @@ -9959,7 +9963,7 @@ fi echo "$as_me:$LINENO: result: $rsync_cv_HAVE_GETTIMEOFDAY_TZ" >&5 echo "${ECHO_T}$rsync_cv_HAVE_GETTIMEOFDAY_TZ" >&6 -if test x"$rsync_cv_HAVE_GETTIMEOFDAY_TZ" = x"yes"; then +if test x"$rsync_cv_HAVE_GETTIMEOFDAY_TZ" != x"no"; then cat >>confdefs.h <<\_ACEOF #define HAVE_GETTIMEOFDAY_TZ 1 diff -urN --exclude=patches rsync-2.6.2/configure.in rsync-2.6.3/configure.in --- rsync-2.6.2/configure.in 2004-04-30 11:02:43.000000000 -0700 +++ rsync-2.6.3/configure.in 2004-09-30 09:35:56.000000000 -0700 @@ -5,7 +5,7 @@ AC_CONFIG_HEADER(config.h) AC_PREREQ(2.52) -RSYNC_VERSION=2.6.2 +RSYNC_VERSION=2.6.3 AC_SUBST(RSYNC_VERSION) AC_MSG_NOTICE([Configuring rsync $RSYNC_VERSION]) @@ -442,9 +442,10 @@ AC_FUNC_UTIME_NULL AC_FUNC_ALLOCA AC_CHECK_FUNCS(waitpid wait4 getcwd strdup strerror chown chmod mknod mkfifo \ - fchmod fstat strchr readlink link utime utimes strftime mtrace \ + fchmod fstat strchr readlink link utime utimes strftime mtrace ftruncate \ memmove lchown vsnprintf snprintf asprintf setsid glob strpbrk \ - strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid) + strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \ + open64 mkstemp64) AC_CACHE_CHECK([for working socketpair],rsync_cv_HAVE_SOCKETPAIR,[ AC_TRY_RUN([ @@ -542,7 +543,7 @@ #include main() { struct timeval tv; exit(gettimeofday(&tv, NULL));}], rsync_cv_HAVE_GETTIMEOFDAY_TZ=yes,rsync_cv_HAVE_GETTIMEOFDAY_TZ=no,rsync_cv_HAVE_GETTIMEOFDAY_TZ=cross)]) -if test x"$rsync_cv_HAVE_GETTIMEOFDAY_TZ" = x"yes"; then +if test x"$rsync_cv_HAVE_GETTIMEOFDAY_TZ" != x"no"; then AC_DEFINE(HAVE_GETTIMEOFDAY_TZ, 1, [ ]) fi diff -urN --exclude=patches rsync-2.6.2/exclude.c rsync-2.6.3/exclude.c --- rsync-2.6.2/exclude.c 2004-04-26 18:36:06.000000000 -0700 +++ rsync-2.6.3/exclude.c 2004-09-21 21:11:15.000000000 -0700 @@ -38,24 +38,23 @@ struct exclude_list_struct server_exclude_list = { 0, 0, "server " }; char *exclude_path_prefix = NULL; -/** Build an exclude structure given a exclude pattern */ -static void make_exclude(struct exclude_list_struct *listp, const char *pattern, - int pat_len, int include) +/** Build an exclude structure given an exclude pattern. */ +static void make_exclude(struct exclude_list_struct *listp, const char *pat, + unsigned int pat_len, unsigned int mflags) { struct exclude_struct *ret; const char *cp; - int ex_len; + unsigned int ex_len; ret = new(struct exclude_struct); if (!ret) out_of_memory("make_exclude"); memset(ret, 0, sizeof ret[0]); - ret->include = include; if (exclude_path_prefix) - ret->match_flags |= MATCHFLG_ABS_PATH; - if (exclude_path_prefix && *pattern == '/') + mflags |= MATCHFLG_ABS_PATH; + if (exclude_path_prefix && *pat == '/') ex_len = strlen(exclude_path_prefix); else ex_len = 0; @@ -64,27 +63,29 @@ out_of_memory("make_exclude"); if (ex_len) memcpy(ret->pattern, exclude_path_prefix, ex_len); - strlcpy(ret->pattern + ex_len, pattern, pat_len + 1); + strlcpy(ret->pattern + ex_len, pat, pat_len + 1); pat_len += ex_len; if (strpbrk(ret->pattern, "*[?")) { - ret->match_flags |= MATCHFLG_WILD; + mflags |= MATCHFLG_WILD; if ((cp = strstr(ret->pattern, "**")) != NULL) { - ret->match_flags |= MATCHFLG_WILD2; + mflags |= MATCHFLG_WILD2; /* If the pattern starts with **, note that. */ if (cp == ret->pattern) - ret->match_flags |= MATCHFLG_WILD2_PREFIX; + mflags |= MATCHFLG_WILD2_PREFIX; } } if (pat_len > 1 && ret->pattern[pat_len-1] == '/') { ret->pattern[pat_len-1] = 0; - ret->directory = 1; + mflags |= MATCHFLG_DIRECTORY; } for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++) ret->slash_cnt++; + ret->match_flags = mflags; + if (!listp->tail) listp->head = listp->tail = ret; else { @@ -99,15 +100,10 @@ free(ex); } -void free_exclude_list(struct exclude_list_struct *listp) +void clear_exclude_list(struct exclude_list_struct *listp) { struct exclude_struct *ent, *next; - if (verbose > 2) { - rprintf(FINFO, "[%s] clearing %sexclude list\n", - who_am_i(), listp->debug_type); - } - for (ent = listp->head; ent; ent = next) { next = ent->next; free_exclude(ent); @@ -119,10 +115,13 @@ static int check_one_exclude(char *name, struct exclude_struct *ex, int name_is_dir) { - char *p; + char *p, full_name[MAXPATHLEN]; int match_start = 0; char *pattern = ex->pattern; + if (!*name) + return 0; + /* If the pattern does not have any slashes AND it does not have * a "**" (which could match a slash), then we just match the * name portion of the path. */ @@ -130,16 +129,14 @@ if ((p = strrchr(name,'/')) != NULL) name = p+1; } - else if ((ex->match_flags & MATCHFLG_ABS_PATH) && *name != '/') { - static char full_name[MAXPATHLEN]; - int plus = curr_dir[1] == '\0'? 1 : 0; - pathjoin(full_name, sizeof full_name, curr_dir+plus, name); + else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/' + && curr_dir[1]) { + pathjoin(full_name, sizeof full_name, curr_dir + 1, name); name = full_name; } - if (!name[0]) return 0; - - if (ex->directory && !name_is_dir) return 0; + if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir) + return 0; if (*pattern == '/') { match_start = 1; @@ -151,8 +148,8 @@ if (ex->match_flags & MATCHFLG_WILD) { /* A non-anchored match with an infix slash and no "**" * needs to match the last slash_cnt+1 name elements. */ - if (!match_start && ex->slash_cnt && - !(ex->match_flags & MATCHFLG_WILD2)) { + if (!match_start && ex->slash_cnt + && !(ex->match_flags & MATCHFLG_WILD2)) { int cnt = ex->slash_cnt + 1; for (p = name + strlen(name) - 1; p >= name; p--) { if (*p == '/' && !--cnt) @@ -206,9 +203,11 @@ if (verbose >= 2) { rprintf(FINFO, "[%s] %scluding %s %s because of %spattern %s%s\n", - who_am_i(), ent->include ? "in" : "ex", + who_am_i(), + ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex", name_is_dir ? "directory" : "file", name, type, - ent->pattern, ent->directory ? "/" : ""); + ent->pattern, + ent->match_flags & MATCHFLG_DIRECTORY ? "/" : ""); } } @@ -225,7 +224,7 @@ if (check_one_exclude(name, ent, name_is_dir)) { report_exclude_result(name, ent, name_is_dir, listp->debug_type); - return ent->include ? 1 : -1; + return ent->match_flags & MATCHFLG_INCLUDE ? 1 : -1; } } @@ -236,16 +235,15 @@ /* Get the next include/exclude arg from the string. The token will not * be '\0' terminated, so use the returned length to limit the string. * Also, be sure to add this length to the returned pointer before passing - * it back to ask for the next token. This routine will not parse the +/- - * prefixes or the "!" token when xflags contains XFLG_WORDS_ONLY. The - * *incl_ptr value will be 1 for an include, 0 for an exclude, and -1 for - * the list-clearing "!" token. + * it back to ask for the next token. This routine parses the +/- prefixes + * and the "!" token unless xflags contains XFLG_WORDS_ONLY. The *flag_ptr + * value will also be set to the MATCHFLG_* bits for the current token. */ -static const char *get_exclude_tok(const char *p, int *len_ptr, int *incl_ptr, - int xflags) +static const char *get_exclude_tok(const char *p, unsigned int *len_ptr, + unsigned int *flag_ptr, int xflags) { const unsigned char *s = (const unsigned char *)p; - int len; + unsigned int len, mflags = 0; if (xflags & XFLG_WORD_SPLIT) { /* Skip over any initial whitespace. */ @@ -258,10 +256,13 @@ /* Is this a '+' or '-' followed by a space (not whitespace)? */ if (!(xflags & XFLG_WORDS_ONLY) && (*s == '-' || *s == '+') && s[1] == ' ') { - *incl_ptr = *s == '+'; + if (*s == '+') + mflags |= MATCHFLG_INCLUDE; s += 2; - } else - *incl_ptr = xflags & XFLG_DEF_INCLUDE; + } else if (xflags & XFLG_DEF_INCLUDE) + mflags |= MATCHFLG_INCLUDE; + if (xflags & XFLG_DIRECTORY) + mflags |= MATCHFLG_DIRECTORY; if (xflags & XFLG_WORD_SPLIT) { const unsigned char *cp = s; @@ -273,9 +274,10 @@ len = strlen(s); if (*p == '!' && len == 1 && !(xflags & XFLG_WORDS_ONLY)) - *incl_ptr = -1; + mflags |= MATCHFLG_CLEAR_LIST; *len_ptr = len; + *flag_ptr = mflags; return (const char *)s; } @@ -283,7 +285,7 @@ void add_exclude(struct exclude_list_struct *listp, const char *pattern, int xflags) { - int pat_len, incl; + unsigned int pat_len, mflags; const char *cp; if (!pattern) @@ -292,21 +294,26 @@ cp = pattern; pat_len = 0; while (1) { - cp = get_exclude_tok(cp + pat_len, &pat_len, &incl, xflags); + cp = get_exclude_tok(cp + pat_len, &pat_len, &mflags, xflags); if (!pat_len) break; - /* If we got the special "!" token, clear the list. */ - if (incl < 0) - free_exclude_list(listp); - else { - make_exclude(listp, cp, pat_len, incl); + if (mflags & MATCHFLG_CLEAR_LIST) { if (verbose > 2) { - rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s)\n", - who_am_i(), pat_len, cp, - listp->debug_type, - incl ? "include" : "exclude"); + rprintf(FINFO, + "[%s] clearing %sexclude list\n", + who_am_i(), listp->debug_type); } + clear_exclude_list(listp); + continue; + } + + make_exclude(listp, cp, pat_len, mflags); + + if (verbose > 2) { + rprintf(FINFO, "[%s] add_exclude(%.*s, %s%sclude)\n", + who_am_i(), (int)pat_len, cp, listp->debug_type, + mflags & MATCHFLG_INCLUDE ? "in" : "ex"); } } } @@ -316,8 +323,8 @@ int xflags) { FILE *fp; - char line[MAXPATHLEN]; - char *eob = line + MAXPATHLEN - 1; + char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */ + char *eob = line + sizeof line - 1; int word_split = xflags & XFLG_WORD_SPLIT; if (!fname || !*fname) @@ -340,7 +347,7 @@ while (1) { char *s = line; - int ch; + int ch, overflow = 0; while (1) { if ((ch = getc(fp)) == EOF) { if (ferror(fp) && errno == EINTR) @@ -353,6 +360,12 @@ break; if (s < eob) *s++ = ch; + else + overflow = 1; + } + if (overflow) { + rprintf(FERROR, "discarding over-long exclude: %s...\n", line); + s = line; } *s = '\0'; /* Skip an empty token and (when line parsing) comments. */ @@ -383,12 +396,12 @@ l = strlcpy(p, ent->pattern, sizeof p); if (l == 0 || l >= MAXPATHLEN) continue; - if (ent->directory) { + if (ent->match_flags & MATCHFLG_DIRECTORY) { p[l++] = '/'; p[l] = '\0'; } - if (ent->include) { + if (ent->match_flags & MATCHFLG_INCLUDE) { write_int(f, l + 2); write_buf(f, "+ ", 2); } else if ((*p == '-' || *p == '+') && p[1] == ' ') { @@ -405,7 +418,7 @@ void recv_exclude_list(int f) { - char line[MAXPATHLEN+1]; /* Allows a trailing slash on a max-len dir */ + char line[MAXPATHLEN+3]; /* Room for "x " prefix and trailing slash. */ unsigned int l; while ((l = read_int(f)) != 0) { diff -urN --exclude=patches rsync-2.6.2/fileio.c rsync-2.6.3/fileio.c --- rsync-2.6.2/fileio.c 2004-01-04 19:57:15.000000000 -0800 +++ rsync-2.6.3/fileio.c 2004-08-03 01:05:29.000000000 -0700 @@ -22,9 +22,10 @@ */ #include "rsync.h" +extern int sparse_files; + static char last_byte; static int last_sparse; -extern int sparse_files; int sparse_end(int f) { @@ -91,6 +92,7 @@ return ret; } + /* * write_file does not allow incomplete writes. It loops internally * until len bytes are written or errno is set. @@ -106,9 +108,9 @@ r1 = write_sparse(f, buf, len1); } else { if (!wf_writeBuf) { - wf_writeBufSize = MAX_MAP_SIZE; + wf_writeBufSize = WRITE_SIZE * 8; wf_writeBufCnt = 0; - wf_writeBuf = new_array(char, MAX_MAP_SIZE); + wf_writeBuf = new_array(char, wf_writeBufSize); if (!wf_writeBuf) out_of_memory("write_file"); } @@ -125,7 +127,8 @@ } } if (r1 <= 0) { - if (ret > 0) return ret; + if (ret > 0) + return ret; return r1; } len -= r1; @@ -136,29 +139,30 @@ } - -/* this provides functionality somewhat similar to mmap() but using - read(). It gives sliding window access to a file. mmap() is not - used because of the possibility of another program (such as a - mailer) truncating the file thus giving us a SIGBUS */ -struct map_struct *map_file(int fd,OFF_T len) +/* This provides functionality somewhat similar to mmap() but using read(). + * It gives sliding window access to a file. mmap() is not used because of + * the possibility of another program (such as a mailer) truncating the + * file thus giving us a SIGBUS. */ +struct map_struct *map_file(int fd, OFF_T len, OFF_T map_size, + size_t block_size) { struct map_struct *map; - map = new(struct map_struct); - if (!map) out_of_memory("map_file"); + if (!(map = new(struct map_struct))) + out_of_memory("map_file"); + + if (block_size && (map_size % block_size)) + map_size += block_size - (map_size % block_size); + + memset(map, 0, sizeof map[0]); map->fd = fd; map->file_size = len; - map->p = NULL; - map->p_size = 0; - map->p_offset = 0; - map->p_fd_offset = 0; - map->p_len = 0; - map->status = 0; + map->def_window_size = map_size; return map; } + /* slide the read window in the file */ char *map_ptr(struct map_struct *map,OFF_T offset,int len) { @@ -166,9 +170,8 @@ OFF_T window_start, read_start; int window_size, read_size, read_offset; - if (len == 0) { + if (len == 0) return NULL; - } /* can't go beyond the end of file */ if (len > (map->file_size - offset)) { @@ -181,15 +184,9 @@ return (map->p + (offset - map->p_offset)); } - /* nope, we are going to have to do a read. Work out our desired window */ - if (offset > 2*CHUNK_SIZE) { - window_start = offset - 2*CHUNK_SIZE; - window_start &= ~((OFF_T)(CHUNK_SIZE-1)); /* assumes power of 2 */ - } else { - window_start = 0; - } - window_size = MAX_MAP_SIZE; + window_start = offset; + window_size = map->def_window_size; if (window_start + window_size > map->file_size) { window_size = map->file_size - window_start; } @@ -200,7 +197,8 @@ /* make sure we have allocated enough memory for the window */ if (window_size > map->p_size) { map->p = realloc_array(map->p, char, window_size); - if (!map->p) out_of_memory("map_ptr"); + if (!map->p) + out_of_memory("map_ptr"); map->p_size = window_size; } @@ -259,9 +257,8 @@ map->p = NULL; } ret = map->status; - memset(map, 0, sizeof(*map)); + memset(map, 0, sizeof map[0]); free(map); return ret; } - diff -urN --exclude=patches rsync-2.6.2/flist.c rsync-2.6.3/flist.c --- rsync-2.6.2/flist.c 2004-04-29 12:37:15.000000000 -0700 +++ rsync-2.6.3/flist.c 2004-09-21 02:24:02.000000000 -0700 @@ -34,6 +34,7 @@ extern int am_root; extern int am_server; extern int am_daemon; +extern int am_sender; extern int always_checksum; extern int module_id; extern int ignore_errors; @@ -47,22 +48,22 @@ extern int filesfrom_fd; extern int one_file_system; +extern int keep_dirlinks; extern int preserve_links; extern int preserve_hard_links; extern int preserve_perms; extern int preserve_devices; extern int preserve_uid; extern int preserve_gid; -extern int preserve_times; extern int relative_paths; extern int implied_dirs; extern int copy_links; extern int copy_unsafe_links; extern int protocol_version; extern int sanitize_paths; - -extern int read_batch; -extern int write_batch; +extern int delete_excluded; +extern int orig_umask; +extern int list_only; extern struct exclude_list_struct exclude_list; extern struct exclude_list_struct server_exclude_list; @@ -72,6 +73,7 @@ static char empty_sum[MD4_SUM_LENGTH]; static unsigned int file_struct_len; +static struct file_list *received_flist; static void clean_flist(struct file_list *flist, int strip_root, int no_dups); static void output_flist(struct file_list *flist); @@ -93,7 +95,7 @@ static void start_filelist_progress(char *kind) { rprintf(FINFO, "%s ... ", kind); - if ((verbose > 1) || do_progress) + if (verbose > 1 || do_progress) rprintf(FINFO, "\n"); rflush(FINFO); } @@ -107,7 +109,7 @@ static void maybe_emit_filelist_progress(const struct file_list *flist) { - if (do_progress && show_filelist_p() && ((flist->count % 100) == 0)) + if (do_progress && show_filelist_p() && (flist->count % 100) == 0) emit_filelist_progress(flist); } @@ -132,9 +134,10 @@ { char perms[11]; - if (!f->basename) + if (!f->basename) { /* this can happen if duplicate names were removed */ return; + } permstring(perms, f->mode); @@ -142,14 +145,16 @@ if (preserve_links && S_ISLNK(f->mode)) { rprintf(FINFO, "%s %11.0f %s %s -> %s\n", perms, - (double) f->length, timestring(f->modtime), + (double)f->length, timestring(f->modtime), f_name(f), f->u.link); } else #endif + { rprintf(FINFO, "%s %11.0f %s %s\n", perms, - (double) f->length, timestring(f->modtime), + (double)f->length, timestring(f->modtime), f_name(f)); + } } @@ -167,15 +172,15 @@ * @post @p buffer contains information about the link or the * referrent as appropriate, if they exist. **/ -int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf) +static int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf) { #if SUPPORT_LINKS if (copy_links) return do_stat(path, buffer); - if (do_lstat(path, buffer) == -1) + if (link_stat(path, buffer, 0) < 0) return -1; if (S_ISLNK(buffer->st_mode)) { - int l = readlink((char *) path, linkbuf, MAXPATHLEN - 1); + int l = readlink((char *)path, linkbuf, MAXPATHLEN - 1); if (l == -1) return -1; linkbuf[l] = 0; @@ -193,12 +198,19 @@ #endif } -int link_stat(const char *path, STRUCT_STAT * buffer) +int link_stat(const char *path, STRUCT_STAT *buffer, int follow_dirlinks) { #if SUPPORT_LINKS if (copy_links) return do_stat(path, buffer); - return do_lstat(path, buffer); + if (do_lstat(path, buffer) < 0) + return -1; + if (follow_dirlinks && S_ISLNK(buffer->st_mode)) { + STRUCT_STAT st; + if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) + *buffer = st; + } + return 0; #else return do_stat(path, buffer); #endif @@ -248,7 +260,7 @@ static void set_filesystem(char *fname) { STRUCT_STAT st; - if (link_stat(fname, &st) != 0) + if (do_stat(fname, &st) != 0) return; filesystem_dev = st.st_dev; } @@ -260,14 +272,14 @@ if (S_ISLNK(mode) && (_S_IFLNK != 0120000)) return (mode & ~(_S_IFMT)) | 0120000; #endif - return (int) mode; + return (int)mode; } static mode_t from_wire_mode(int mode) { if ((mode & (_S_IFMT)) == 0120000 && (_S_IFLNK != 0120000)) return (mode & ~(_S_IFMT)) | _S_IFLNK; - return (mode_t) mode; + return (mode_t)mode; } @@ -283,7 +295,7 @@ **/ void flist_expand(struct file_list *flist) { - void *new_ptr; + struct file_struct **new_ptr; if (flist->count < flist->malloced) return; @@ -302,21 +314,17 @@ if (flist->malloced < flist->count) flist->malloced = flist->count; - if (flist->files) { - new_ptr = realloc_array(flist->files, - struct file_struct *, flist->malloced); - } else { - new_ptr = new_array(struct file_struct *, flist->malloced); - } + new_ptr = realloc_array(flist->files, struct file_struct *, + flist->malloced); if (verbose >= 2) { rprintf(FINFO, "[%s] expand file_list to %.0f bytes, did%s move\n", who_am_i(), - (double) sizeof flist->files[0] * flist->malloced, + (double)sizeof flist->files[0] * flist->malloced, (new_ptr == flist->files) ? " not" : ""); } - flist->files = (struct file_struct **) new_ptr; + flist->files = new_ptr; if (!flist->files) out_of_memory("flist_expand"); @@ -333,7 +341,7 @@ static uid_t uid; static gid_t gid; static char lastname[MAXPATHLEN]; - char *fname, fbuf[MAXPATHLEN]; + char fname[MAXPATHLEN]; int l1, l2; if (f == -1) @@ -351,7 +359,7 @@ io_write_phase = "send_file_entry"; - fname = f_name_to(file, fbuf); + f_name_to(file, fname); flags = base_flags; @@ -515,7 +523,7 @@ void receive_file_entry(struct file_struct **fptr, unsigned short flags, - struct file_list *flist, int f) + struct file_list *flist, int f) { static time_t modtime; static mode_t mode; @@ -525,7 +533,7 @@ static uid_t uid; static gid_t gid; static char lastname[MAXPATHLEN], *lastdir; - static int lastdir_len = -1; + static int lastdir_depth, lastdir_len = -1; char thisname[MAXPATHLEN]; unsigned int l1 = 0, l2 = 0; int alloc_len, basename_len, dirname_len, linkname_len, sum_len; @@ -539,6 +547,7 @@ rdev_major = 0; uid = 0, gid = 0; *lastname = '\0'; + lastdir_len = -1; return; } @@ -563,10 +572,10 @@ strlcpy(lastname, thisname, MAXPATHLEN); - clean_fname(thisname); + clean_fname(thisname, 0); if (sanitize_paths) - sanitize_path(thisname, NULL); + sanitize_path(thisname, thisname, "", 0); if ((basename = strrchr(thisname, '/')) != NULL) { dirname_len = ++basename - thisname; /* counts future '\0' */ @@ -649,6 +658,8 @@ memcpy(bp, dirname, dirname_len - 1); bp += dirname_len; bp[-1] = '\0'; + if (sanitize_paths) + lastdir_depth = count_dir_elements(lastdir); } else if (dirname) file->dirname = dirname; @@ -664,7 +675,7 @@ file->u.link = bp; read_sbuf(f, bp, linkname_len - 1); if (sanitize_paths) - sanitize_path(bp, lastdir); + sanitize_path(bp, bp, "", lastdir_depth); bp += linkname_len; } #endif @@ -708,7 +719,6 @@ } if (!preserve_perms) { - extern int orig_umask; /* set an appropriate set of permissions based on original * permissions and umask. This emulates what GNU cp does */ file->mode &= ~orig_umask; @@ -731,8 +741,8 @@ * statting directories if we're not recursing, but this is not a very * important case. Some systems may not have d_type. **/ -struct file_struct *make_file(char *fname, - struct file_list *flist, int exclude_level) +struct file_struct *make_file(char *fname, struct file_list *flist, + int exclude_level) { static char *lastdir; static int lastdir_len = -1; @@ -745,7 +755,7 @@ char *basename, *dirname, *bp; unsigned short flags = 0; - if (!flist) /* lastdir isn't valid if flist is NULL */ + if (!flist || !flist->count) /* Ignore lastdir when invalid. */ lastdir_len = -1; if (strlcpy(thisname, fname, sizeof thisname) @@ -753,33 +763,39 @@ rprintf(FINFO, "skipping overly long name: %s\n", fname); return NULL; } - clean_fname(thisname); + clean_fname(thisname, 0); if (sanitize_paths) - sanitize_path(thisname, NULL); + sanitize_path(thisname, thisname, "", 0); memset(sum, 0, SUM_LENGTH); if (readlink_stat(thisname, &st, linkname) != 0) { int save_errno = errno; - if (errno == ENOENT) { - enum logcode c = am_daemon && protocol_version < 28 - ? FERROR : FINFO; - /* either symlink pointing nowhere or file that - * was removed during rsync run; see if excluded - * before reporting an error */ - if (exclude_level != NO_EXCLUDES - && check_exclude_file(thisname, 0, exclude_level)) { - /* file is excluded anyway, ignore silently */ - return NULL; + /* See if file is excluded before reporting an error. */ + if (exclude_level != NO_EXCLUDES + && check_exclude_file(thisname, 0, exclude_level)) + return NULL; + if (save_errno == ENOENT) { +#if SUPPORT_LINKS + /* Avoid "vanished" error if symlink points nowhere. */ + if (copy_links && do_lstat(thisname, &st) == 0 + && S_ISLNK(st.st_mode)) { + io_error |= IOERR_GENERAL; + rprintf(FERROR, "symlink has no referent: %s\n", + full_fname(thisname)); + } else +#endif + { + enum logcode c = am_daemon && protocol_version < 28 + ? FERROR : FINFO; + io_error |= IOERR_VANISHED; + rprintf(c, "file has vanished: %s\n", + full_fname(thisname)); } - io_error |= IOERR_VANISHED; - rprintf(c, "file has vanished: %s\n", - full_fname(thisname)); - } - else { + } else { io_error |= IOERR_GENERAL; - rprintf(FERROR, "readlink %s failed: %s\n", - full_fname(thisname), strerror(save_errno)); + rsyserr(FERROR, save_errno, "readlink %s failed", + full_fname(thisname)); } return NULL; } @@ -911,6 +927,26 @@ file->basedir = flist_dir; + /* This code is only used by the receiver when it is building + * a list of files for a delete pass. */ + if (keep_dirlinks && linkname_len && flist) { + STRUCT_STAT st2; + int i = flist_find(received_flist, file); + if (i >= 0 && S_ISDIR(received_flist->files[i]->mode) + && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) { + file->modtime = st2.st_mtime; + file->length = st2.st_size; + file->mode = st2.st_mode; + file->uid = st2.st_uid; + file->gid = st2.st_gid; + file->u.link = NULL; + if (file->link_u.idev) { + pool_free(flist->hlink_pool, 0, file->link_u.idev); + file->link_u.idev = NULL; + } + } + } + if (!S_ISDIR(st.st_mode)) stats.total_size += st.st_size; @@ -923,7 +959,6 @@ { struct file_struct *file; char fbuf[MAXPATHLEN]; - extern int delete_excluded; /* f is set to -1 when calculating deletion file list */ file = make_file(fname, flist, @@ -936,9 +971,6 @@ flist_expand(flist); - if (write_batch) - file->flags |= FLAG_TOP_DIR; - if (file->basename[0]) { flist->files[flist->count++] = file; send_file_entry(file, f, base_flags); @@ -949,7 +981,11 @@ struct exclude_list_struct last_list = local_exclude_list; local_exclude_list.head = local_exclude_list.tail = NULL; send_directory(f, flist, f_name_to(file, fbuf)); - free_exclude_list(&local_exclude_list); + if (verbose > 2) { + rprintf(FINFO, "[%s] popping %sexclude list\n", + who_am_i(), local_exclude_list.debug_type); + } + clear_exclude_list(&local_exclude_list); local_exclude_list = last_list; } } @@ -966,8 +1002,7 @@ d = opendir(dir); if (!d) { io_error |= IOERR_GENERAL; - rprintf(FERROR, "opendir %s failed: %s\n", - full_fname(dir), strerror(errno)); + rsyserr(FERROR, errno, "opendir %s failed", full_fname(dir)); return; } @@ -1014,8 +1049,7 @@ } if (errno) { io_error |= IOERR_GENERAL; - rprintf(FERROR, "readdir(%s): (%d) %s\n", - dir, errno, strerror(errno)); + rsyserr(FERROR, errno, "readdir(%s)", dir); } closedir(d); @@ -1023,6 +1057,8 @@ /** + * This function is normally called by the sender, but the receiver also + * uses it to construct its own file list if --delete has been specified. * The delete_files() function in receiver.c sets f to -1 so that we just * construct the file list in memory without sending it over the wire. It * also has the side-effect of ignoring user-excludes if delete_excluded @@ -1047,11 +1083,11 @@ "send_file_list"); if (f != -1) { - io_start_buffering_out(f); + io_start_buffering_out(); if (filesfrom_fd >= 0) { if (argv[0] && !push_dir(argv[0])) { - rprintf(FERROR, "push_dir %s failed: %s\n", - full_fname(argv[0]), strerror(errno)); + rsyserr(FERROR, errno, "push_dir %s failed", + full_fname(argv[0])); exit_cleanup(RERR_FILESELECT); } use_ff_fd = 1; @@ -1065,13 +1101,13 @@ if (use_ff_fd) { if (read_filesfrom_line(filesfrom_fd, fname) == 0) break; - sanitize_path(fname, NULL); + sanitize_path(fname, fname, "", 0); } else { if (argc-- == 0) break; strlcpy(fname, *argv++, MAXPATHLEN); if (sanitize_paths) - sanitize_path(fname, NULL); + sanitize_path(fname, fname, "", 0); } l = strlen(fname); @@ -1085,11 +1121,11 @@ } } - if (link_stat(fname, &st) != 0) { + if (link_stat(fname, &st, keep_dirlinks) != 0) { if (f != -1) { io_error |= IOERR_GENERAL; - rprintf(FERROR, "link_stat %s failed: %s\n", - full_fname(fname), strerror(errno)); + rsyserr(FERROR, errno, "link_stat %s failed", + full_fname(fname)); } continue; } @@ -1158,8 +1194,8 @@ if (!push_dir(dir)) { io_error |= IOERR_GENERAL; - rprintf(FERROR, "push_dir %s failed: %s\n", - full_fname(dir), strerror(errno)); + rsyserr(FERROR, errno, "push_dir %s failed", + full_fname(dir)); continue; } @@ -1181,8 +1217,8 @@ flist_dir = NULL; flist_dir_len = 0; if (!pop_dir(olddir)) { - rprintf(FERROR, "pop_dir %s failed: %s\n", - full_fname(dir), strerror(errno)); + rsyserr(FERROR, errno, "pop_dir %s failed", + full_fname(dir)); exit_cleanup(RERR_FILESELECT); } } @@ -1213,8 +1249,6 @@ io_end_buffering(); stats.flist_size = stats.total_written - start_write; stats.num_files = flist->count; - if (write_batch) - write_batch_flist_info(flist->count, flist->files); } if (verbose > 3) @@ -1232,7 +1266,6 @@ struct file_list *flist; unsigned short flags; int64 start_read; - extern int list_only; if (show_filelist_p()) start_filelist_progress("receiving file list"); @@ -1240,6 +1273,7 @@ start_read = stats.total_read; flist = flist_new(WITH_HLINK, "recv_file_list"); + received_flist = flist; flist->count = 0; flist->malloced = 1000; @@ -1284,13 +1318,11 @@ * protocol version 15 */ recv_uid_list(f, flist); - if (!read_batch) { - /* Recv the io_error flag */ - if (lp_ignore_errors(module_id) || ignore_errors) - read_int(f); - else - io_error |= read_int(f); - } + /* Recv the io_error flag */ + if (lp_ignore_errors(module_id) || ignore_errors) + read_int(f); + else + io_error |= read_int(f); } if (verbose > 3) @@ -1422,7 +1454,7 @@ return; qsort(flist->files, flist->count, - sizeof flist->files[0], (int (*)()) file_compare); + sizeof flist->files[0], (int (*)())file_compare); for (i = no_dups? 0 : flist->count; i < flist->count; i++) { if (flist->files[i]->basename) { @@ -1478,7 +1510,7 @@ for (i = 0; i < flist->count; i++) { file = flist->files[i]; - if (am_root && preserve_uid) + if ((am_root || am_sender) && preserve_uid) sprintf(uidbuf, " uid=%ld", (long)file->uid); else *uidbuf = '\0'; @@ -1488,8 +1520,8 @@ *gidbuf = '\0'; rprintf(FINFO, "[%s] i=%d %s %s %s mode=0%o len=%.0f%s%s\n", who_am_i(), i, NS(file->basedir), NS(file->dirname), - NS(file->basename), (int) file->mode, - (double) file->length, uidbuf, gidbuf); + NS(file->basename), (int)file->mode, + (double)file->length, uidbuf, gidbuf); } } diff -urN --exclude=patches rsync-2.6.2/generator.c rsync-2.6.3/generator.c --- rsync-2.6.2/generator.c 2004-04-15 09:55:23.000000000 -0700 +++ rsync-2.6.3/generator.c 2004-09-20 12:47:59.000000000 -0700 @@ -26,6 +26,7 @@ extern int verbose; extern int dry_run; extern int relative_paths; +extern int keep_dirlinks; extern int preserve_links; extern int am_root; extern int preserve_devices; @@ -35,29 +36,34 @@ extern int preserve_gid; extern int update_only; extern int opt_ignore_existing; +extern int inplace; +extern int make_backups; extern int csum_length; extern int ignore_times; extern int size_only; extern int io_timeout; extern int protocol_version; extern int always_checksum; +extern char *partial_dir; extern char *compare_dest; extern int link_dest; extern int whole_file; extern int local_server; -extern int write_batch; extern int list_only; +extern int read_batch; extern int only_existing; extern int orig_umask; extern int safe_symlinks; +extern unsigned int block_size; + +extern struct exclude_list_struct server_exclude_list; /* choose whether to skip a particular file */ static int skip_file(char *fname, struct file_struct *file, STRUCT_STAT *st) { - if (st->st_size != file->length) { + if (st->st_size != file->length) return 0; - } if (link_dest) { if (preserve_perms && (st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) @@ -75,29 +81,18 @@ of the file time to determine whether to sync */ if (always_checksum && S_ISREG(st->st_mode)) { char sum[MD4_SUM_LENGTH]; - char fnamecmpdest[MAXPATHLEN]; - - if (compare_dest != NULL) { - if (access(fname, 0) != 0) { - pathjoin(fnamecmpdest, sizeof fnamecmpdest, - compare_dest, fname); - fname = fnamecmpdest; - } - } file_checksum(fname,sum,st->st_size); return memcmp(sum, file->u.sum, protocol_version < 21 ? 2 : MD4_SUM_LENGTH) == 0; } - if (size_only) { + if (size_only) return 1; - } - if (ignore_times) { + if (ignore_times) return 0; - } - return (cmp_modtime(st->st_mtime,file->modtime) == 0); + return cmp_modtime(st->st_mtime, file->modtime) == 0; } @@ -118,11 +113,11 @@ write_int(f, sum->remainder); } -/* +/* * set (initialize) the size entries in the per-file sum_struct - * calulating dynamic block ans checksum sizes. + * calculating dynamic block and checksum sizes. * - * This is only called from generate_and_send_sums() but is a seperate + * This is only called from generate_and_send_sums() but is a separate * function to encapsulate the logic. * * The block size is a rounded square root of file length. @@ -138,7 +133,6 @@ static void sum_sizes_sqroot(struct sum_struct *sum, uint64 len) { - extern unsigned int block_size; unsigned int blength; int s2length; uint32 c; @@ -200,46 +194,37 @@ } } -/** - * Perhaps we want to just send an empty checksum set for this file, - * which will force the whole thing to be literally transferred. - * - * When do we do this? If the user's explicitly said they - * want the whole thing, or if { they haven't explicitly - * requested a delta, and it's local but not batch mode.} - * - * Whew. */ -static BOOL disable_deltas_p(void) -{ - if (whole_file > 0) - return True; - if (whole_file == 0 || write_batch) - return False; - return local_server; -} - /* * Generate and send a stream of signatures/checksums that describe a buffer * * Generate approximately one checksum every block_len bytes. */ -static void generate_and_send_sums(struct map_struct *buf, size_t len, int f_out) +static void generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy) { size_t i; + struct map_struct *mapbuf; struct sum_struct sum; OFF_T offset = 0; sum_sizes_sqroot(&sum, len); + if (len > 0) + mapbuf = map_file(fd, len, MAX_MAP_SIZE, sum.blength); + else + mapbuf = NULL; + write_sum_head(f_out, &sum); for (i = 0; i < sum.count; i++) { unsigned int n1 = MIN(len, sum.blength); - char *map = map_ptr(buf, offset, n1); + char *map = map_ptr(mapbuf, offset, n1); uint32 sum1 = get_checksum1(map, n1); char sum2[SUM_LENGTH]; + if (f_copy >= 0) + full_write(f_copy, map, n1); + get_checksum2(map, n1, sum2); if (verbose > 3) { @@ -253,44 +238,67 @@ len -= n1; offset += n1; } + + if (mapbuf) + unmap_file(mapbuf); } -/** +/* * Acts on file number @p i from @p flist, whose name is @p fname. * * First fixes up permissions, then generates checksums for the file. * * @note This comment was added later by mbp who was trying to work it * out. It might be wrong. - **/ -void recv_generator(char *fname, struct file_struct *file, int i, int f_out) + */ +static void recv_generator(char *fname, struct file_struct *file, int i, + int f_out) { - int fd; - STRUCT_STAT st; - struct map_struct *mapbuf; - int statret; - char *fnamecmp; + int fd, f_copy; + STRUCT_STAT st, partial_st; + struct file_struct *back_file; + int statret, stat_errno; + char *fnamecmp, *partialptr, *backupptr; char fnamecmpbuf[MAXPATHLEN]; if (list_only) return; if (verbose > 2) - rprintf(FINFO,"recv_generator(%s,%d)\n",fname,i); + rprintf(FINFO, "recv_generator(%s,%d)\n", safe_fname(fname), i); + + if (server_exclude_list.head + && check_exclude(&server_exclude_list, fname, + S_ISDIR(file->mode)) < 0) { + if (verbose) { + rprintf(FINFO, "skipping server-excluded file \"%s\"\n", + safe_fname(fname)); + } + return; + } - statret = link_stat(fname,&st); + if (dry_run > 1) { + statret = -1; + stat_errno = ENOENT; + } else { + statret = link_stat(fname, &st, + keep_dirlinks && S_ISDIR(file->mode)); + stat_errno = errno; + } - if (only_existing && statret == -1 && errno == ENOENT) { + if (only_existing && statret == -1 && stat_errno == ENOENT) { /* we only want to update existing files */ - if (verbose > 1) rprintf(FINFO, "not creating new file \"%s\"\n",fname); + if (verbose > 1) { + rprintf(FINFO, "not creating new file \"%s\"\n", + safe_fname(fname)); + } return; } - if (statret == 0 && - !preserve_perms && - (S_ISDIR(st.st_mode) == S_ISDIR(file->mode))) { + if (statret == 0 && !preserve_perms + && S_ISDIR(st.st_mode) == S_ISDIR(file->mode)) { /* if the file exists already and we aren't perserving * permissions then act as though the remote end sent * us the file permissions we already have */ @@ -305,28 +313,31 @@ * we need to delete it. If it doesn't exist, then * recursively create it. */ - if (dry_run) return; /* XXXX -- might cause inaccuracies?? -- mbp */ + if (dry_run) + return; /* TODO: causes inaccuracies -- fix */ if (statret == 0 && !S_ISDIR(st.st_mode)) { if (robust_unlink(fname) != 0) { - rprintf(FERROR, - "recv_generator: unlink %s to make room for directory: %s\n", - full_fname(fname), strerror(errno)); + rsyserr(FERROR, errno, + "recv_generator: unlink %s to make room for directory", + full_fname(fname)); return; } statret = -1; } if (statret != 0 && do_mkdir(fname,file->mode) != 0 && errno != EEXIST) { - if (!(relative_paths && errno==ENOENT && - create_directory_path(fname, orig_umask)==0 && - do_mkdir(fname,file->mode)==0)) { - rprintf(FERROR, "recv_generator: mkdir %s failed: %s\n", - full_fname(fname), strerror(errno)); + if (!(relative_paths && errno == ENOENT + && create_directory_path(fname, orig_umask) == 0 + && do_mkdir(fname, file->mode) == 0)) { + rsyserr(FERROR, errno, + "recv_generator: mkdir %s failed", + full_fname(fname)); } } - /* f_out is set to -1 when doing final directory - permission and modification time repair */ - if (set_perms(fname,file,NULL,0) && verbose && (f_out != -1)) - rprintf(FINFO,"%s/\n",fname); + /* f_out is set to -1 when doing final directory-permission + * and modification-time repair. */ + if (set_perms(fname, file, statret ? NULL : &st, 0) + && verbose && f_out != -1) + rprintf(FINFO, "%s/\n", safe_fname(fname)); return; } @@ -350,7 +361,8 @@ * right place -- no further action * required. */ if (strcmp(lnk,file->u.link) == 0) { - set_perms(fname,file,&st,1); + set_perms(fname, file, &st, + PERMS_REPORT); return; } } @@ -360,12 +372,13 @@ delete_file(fname); } if (do_symlink(file->u.link,fname) != 0) { - rprintf(FERROR, "symlink %s -> \"%s\" failed: %s\n", - full_fname(fname), file->u.link, strerror(errno)); + rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed", + full_fname(fname), safe_fname(file->u.link)); } else { set_perms(fname,file,NULL,0); if (verbose) { - rprintf(FINFO,"%s -> %s\n", fname,file->u.link); + rprintf(FINFO, "%s -> %s\n", safe_fname(fname), + safe_fname(file->u.link)); } } #endif @@ -378,19 +391,23 @@ st.st_mode != file->mode || st.st_rdev != file->u.rdev) { delete_file(fname); - if (verbose > 2) + if (verbose > 2) { rprintf(FINFO,"mknod(%s,0%o,0x%x)\n", - fname,(int)file->mode,(int)file->u.rdev); + safe_fname(fname), + (int)file->mode, (int)file->u.rdev); + } if (do_mknod(fname,file->mode,file->u.rdev) != 0) { - rprintf(FERROR, "mknod %s failed: %s\n", - full_fname(fname), strerror(errno)); + rsyserr(FERROR, errno, "mknod %s failed", + full_fname(fname)); } else { set_perms(fname,file,NULL,0); - if (verbose) - rprintf(FINFO,"%s\n",fname); + if (verbose) { + rprintf(FINFO, "%s\n", + safe_fname(fname)); + } } } else { - set_perms(fname,file,&st,1); + set_perms(fname, file, &st, PERMS_REPORT); } return; } @@ -400,7 +417,8 @@ return; if (!S_ISREG(file->mode)) { - rprintf(FINFO, "skipping non-regular file \"%s\"\n",fname); + rprintf(FINFO, "skipping non-regular file \"%s\"\n", + safe_fname(fname)); return; } @@ -408,91 +426,100 @@ if (statret == -1 && compare_dest != NULL) { /* try the file at compare_dest instead */ - int saveerrno = errno; pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, compare_dest, fname); - statret = link_stat(fnamecmpbuf,&st); - if (!S_ISREG(st.st_mode)) - statret = -1; - if (statret == -1) - errno = saveerrno; + if (link_stat(fnamecmpbuf, &st, 0) == 0 + && S_ISREG(st.st_mode)) { #if HAVE_LINK - else if (link_dest && !dry_run) { - if (do_link(fnamecmpbuf, fname) != 0) { - if (verbose > 0) { - rprintf(FINFO,"link %s => %s : %s\n", - fnamecmpbuf, fname, - strerror(errno)); + if (link_dest && !dry_run) { + if (do_link(fnamecmpbuf, fname) < 0) { + if (verbose) { + rsyserr(FINFO, errno, + "link %s => %s", + fnamecmpbuf, + safe_fname(fname)); + } + fnamecmp = fnamecmpbuf; } - } - fnamecmp = fnamecmpbuf; - } + } else #endif - else - fnamecmp = fnamecmpbuf; + fnamecmp = fnamecmpbuf; + statret = 0; + } } - if (statret == -1) { - if (preserve_hard_links && hard_link_check(file, HL_SKIP)) + if (statret == 0 && !S_ISREG(st.st_mode)) { + if (delete_file(fname) != 0) return; - if (errno == ENOENT) { - write_int(f_out,i); - if (!dry_run) write_sum_head(f_out, NULL); - } else if (verbose > 1) { - rprintf(FERROR, - "recv_generator: failed to open %s: %s\n", - full_fname(fname), strerror(errno)); - } - return; + statret = -1; + stat_errno = ENOENT; } - if (!S_ISREG(st.st_mode)) { - if (delete_file(fname) != 0) { - return; - } + if (partial_dir && (partialptr = partial_dir_fname(fname)) + && link_stat(partialptr, &partial_st, 0) == 0 + && S_ISREG(partial_st.st_mode)) { + if (statret == -1) + goto prepare_to_open; + } else + partialptr = NULL; - /* now pretend the file didn't exist */ + if (statret == -1) { if (preserve_hard_links && hard_link_check(file, HL_SKIP)) return; - write_int(f_out,i); - if (!dry_run) write_sum_head(f_out, NULL); + if (stat_errno == ENOENT) { + write_int(f_out,i); + if (!dry_run && !read_batch) + write_sum_head(f_out, NULL); + } else if (verbose > 1) { + rsyserr(FERROR, stat_errno, + "recv_generator: failed to stat %s", + full_fname(fname)); + } return; } if (opt_ignore_existing && fnamecmp == fname) { if (verbose > 1) - rprintf(FINFO,"%s exists\n",fname); + rprintf(FINFO, "%s exists\n", safe_fname(fname)); return; } - if (update_only && cmp_modtime(st.st_mtime,file->modtime)>0 && fnamecmp == fname) { + if (update_only && fnamecmp == fname + && cmp_modtime(st.st_mtime, file->modtime) > 0) { if (verbose > 1) - rprintf(FINFO,"%s is newer\n",fname); + rprintf(FINFO, "%s is newer\n", safe_fname(fname)); return; } - if (skip_file(fname, file, &st)) { + if (skip_file(fnamecmp, file, &st)) { if (fnamecmp == fname) - set_perms(fname,file,&st,1); + set_perms(fname, file, &st, PERMS_REPORT); return; } - if (dry_run) { +prepare_to_open: + if (dry_run || read_batch) { write_int(f_out,i); return; } - if (disable_deltas_p()) { + if (whole_file > 0) { write_int(f_out,i); write_sum_head(f_out, NULL); return; } + if (partialptr) { + st = partial_st; + fnamecmp = partialptr; + } + /* open the file */ fd = do_open(fnamecmp, O_RDONLY, 0); if (fd == -1) { - rprintf(FERROR, "failed to open %s, continuing: %s\n", - full_fname(fnamecmp), strerror(errno)); + rsyserr(FERROR, errno, "failed to open %s, continuing", + full_fname(fnamecmp)); + pretend_missing: /* pretend the file didn't exist */ if (preserve_hard_links && hard_link_check(file, HL_SKIP)) return; @@ -501,31 +528,63 @@ return; } - if (st.st_size > 0) - mapbuf = map_file(fd,st.st_size); - else - mapbuf = NULL; + if (inplace && make_backups) { + if (!(backupptr = get_backup_name(fname))) { + close(fd); + return; + } + if (!(back_file = make_file(fname, NULL, NO_EXCLUDES))) { + close(fd); + goto pretend_missing; + } + if (robust_unlink(backupptr) && errno != ENOENT) { + rsyserr(FERROR, errno, "unlink %s", + full_fname(backupptr)); + free(back_file); + close(fd); + return; + } + if ((f_copy = do_open(backupptr, + O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) { + rsyserr(FERROR, errno, "open %s", + full_fname(backupptr)); + free(back_file); + close(fd); + return; + } + } else { + backupptr = NULL; + back_file = NULL; + f_copy = -1; + } if (verbose > 3) { - rprintf(FINFO,"gen mapped %s of size %.0f\n", fnamecmp, - (double)st.st_size); + rprintf(FINFO, "gen mapped %s of size %.0f\n", + safe_fname(fnamecmp), (double)st.st_size); } if (verbose > 2) rprintf(FINFO, "generating and sending sums for %d\n", i); write_int(f_out,i); - generate_and_send_sums(mapbuf, st.st_size, f_out); + generate_and_send_sums(fd, st.st_size, f_out, f_copy); + + if (f_copy >= 0) { + close(f_copy); + set_perms(backupptr, back_file, NULL, 0); + if (verbose > 1) + rprintf(FINFO, "backed up %s to %s\n", fname, backupptr); + free(back_file); + } close(fd); - if (mapbuf) unmap_file(mapbuf); } -void generate_files(int f, struct file_list *flist, char *local_name) +void generate_files(int f_out, struct file_list *flist, char *local_name) { int i; - int phase=0; + int phase = 0; char fbuf[MAXPATHLEN]; if (verbose > 2) { @@ -535,7 +594,7 @@ if (verbose >= 2) { rprintf(FINFO, - disable_deltas_p() + whole_file > 0 ? "delta-transmission disabled for local transfer or --whole-file\n" : "delta transmission enabled\n"); } @@ -563,31 +622,31 @@ } recv_generator(local_name ? local_name : f_name_to(file, fbuf), - file, i, f); + file, i, f_out); } phase++; csum_length = SUM_LENGTH; - ignore_times=1; + ignore_times = 1; if (verbose > 2) rprintf(FINFO,"generate_files phase=%d\n",phase); - write_int(f,-1); + write_int(f_out, -1); /* files can cycle through the system more than once * to catch initial checksum errors */ while ((i = get_redo_num()) != -1) { struct file_struct *file = flist->files[i]; recv_generator(local_name ? local_name : f_name_to(file, fbuf), - file, i, f); + file, i, f_out); } phase++; if (verbose > 2) rprintf(FINFO,"generate_files phase=%d\n",phase); - write_int(f,-1); + write_int(f_out, -1); if (preserve_hard_links) do_hard_links(); @@ -596,7 +655,8 @@ * modified during the transfer */ for (i = 0; i < flist->count; i++) { struct file_struct *file = flist->files[i]; - if (!file->basename || !S_ISDIR(file->mode)) continue; + if (!file->basename || !S_ISDIR(file->mode)) + continue; recv_generator(local_name ? local_name : f_name(file), file, i, -1); } diff -urN --exclude=patches rsync-2.6.2/getfsdev.c rsync-2.6.3/getfsdev.c --- rsync-2.6.2/getfsdev.c 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.3/getfsdev.c 2004-08-09 13:46:54.000000000 -0700 @@ -0,0 +1,17 @@ +#include "rsync.h" + + int main(int argc, char *argv[]) +{ + STRUCT_STAT st; + + while (--argc > 0) { + if (stat(*++argv, &st) < 0) { + fprintf(stderr, "Unable to stat `%s'\n", *argv); + exit(1); + } + printf("%ld/%ld\n", (long)major(st.st_dev), + (long)minor(st.st_dev)); + } + + return 0; +} diff -urN --exclude=patches rsync-2.6.2/hlink.c rsync-2.6.3/hlink.c --- rsync-2.6.2/hlink.c 2004-03-26 08:46:20.000000000 -0800 +++ rsync-2.6.3/hlink.c 2004-07-12 00:03:28.000000000 -0700 @@ -135,6 +135,7 @@ int hard_link_check(struct file_struct *file, int skip) { +#if SUPPORT_HARD_LINKS if (!hlink_list || !file->link_u.links) return 0; if (skip && !(file->flags & FLAG_HLINK_EOL)) @@ -146,6 +147,7 @@ } return 1; } +#endif return 0; } @@ -154,8 +156,8 @@ { if (do_link(hlink1, hlink2)) { if (verbose) { - rprintf(FINFO, "link %s => %s failed: %s\n", - hlink2, hlink1, strerror(errno)); + rsyserr(FINFO, errno, "link %s => %s failed", + hlink2, hlink1); } } else if (verbose) @@ -183,11 +185,11 @@ for (i = 0; i < hlink_count; i++) { first = file = hlink_list[i]; - if (link_stat(f_name_to(first, hlink1), &st1) != 0) + if (link_stat(f_name_to(first, hlink1), &st1, 0) < 0) continue; while ((file = file->F_NEXT) != first) { hlink2 = f_name(file); - if (link_stat(hlink2, &st2) == 0) { + if (link_stat(hlink2, &st2, 0) == 0) { if (st2.st_dev == st1.st_dev && st2.st_ino == st1.st_ino) continue; @@ -196,10 +198,9 @@ continue; } else if (robust_unlink(hlink2)) { if (verbose > 0) { - rprintf(FINFO, - "unlink %s failed: %s\n", - full_fname(hlink2), - strerror(errno)); + rsyserr(FINFO, errno, + "unlink %s failed", + full_fname(hlink2)); } continue; } diff -urN --exclude=patches rsync-2.6.2/io.c rsync-2.6.3/io.c --- rsync-2.6.2/io.c 2004-01-16 08:31:47.000000000 -0800 +++ rsync-2.6.3/io.c 2004-08-01 19:43:54.000000000 -0700 @@ -1,19 +1,19 @@ /* -*- c-file-style: "linux" -*- - * - * Copyright (C) 1996-2001 by Andrew Tridgell + * + * Copyright (C) 1996-2001 by Andrew Tridgell * Copyright (C) Paul Mackerras 1996 * Copyright (C) 2001, 2002 by Martin Pool - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -22,7 +22,7 @@ /** * @file io.c * - * Socket and pipe IO utilities used in rsync. + * Socket and pipe I/O utilities used in rsync. * * rsync provides its own multiplexing system, which is used to send * stderr and stdout over a single socket. We need this because @@ -39,23 +39,23 @@ /** If no timeout is specified then use a 60 second select timeout */ #define SELECT_TIMEOUT 60 -static int io_multiplexing_out; -static int io_multiplexing_in; -static int multiplex_in_fd = -1; -static int multiplex_out_fd = -1; -static time_t last_io; -static int no_flush; - extern int bwlimit; +extern size_t bwlimit_writemax; extern int verbose; extern int io_timeout; extern int am_server; extern int am_daemon; extern int am_sender; +extern int eol_nulls; +extern int checksum_seed; +extern int protocol_version; +extern char *remote_filesfrom_file; extern struct stats stats; - const char phase_unknown[] = "unknown"; +int select_timeout = SELECT_TIMEOUT; +int batch_fd = -1; +int batch_gen_fd = -1; /** * The connection might be dropped at some point; perhaps because the @@ -63,9 +63,9 @@ * not very helpful. So instead we try to make io_phase_name point to * something useful. * - * For buffered/multiplexed IO these names will be somewhat + * For buffered/multiplexed I/O these names will be somewhat * approximate; perhaps for ease of support we would rather make the - * buffer always flush when a single application-level IO finishes. + * buffer always flush when a single application-level I/O finishes. * * @todo Perhaps we want some simple stack functionality, but there's * no need to overdo it. @@ -80,6 +80,16 @@ int msg_fd_in = -1; int msg_fd_out = -1; +static int io_multiplexing_out; +static int io_multiplexing_in; +static int sock_f_in = -1; +static int sock_f_out = -1; +static time_t last_io; +static int no_flush; + +static int write_batch_monitor_in = -1; +static int write_batch_monitor_out = -1; + static int io_filesfrom_f_in = -1; static int io_filesfrom_f_out = -1; static char io_filesfrom_buf[2048]; @@ -135,18 +145,26 @@ t = time(NULL); - if (last_io && io_timeout && (t-last_io) >= io_timeout) { + if (t - last_io >= io_timeout) { if (!am_server && !am_daemon) { - rprintf(FERROR,"io timeout after %d seconds - exiting\n", + rprintf(FERROR, "io timeout after %d seconds - exiting\n", (int)(t-last_io)); } exit_cleanup(RERR_TIMEOUT); } } +/* Note the fds used for the main socket (which might really be a pipe + * for a local transfer, but we can ignore that). */ +void io_set_sock_fds(int f_in, int f_out) +{ + sock_f_in = f_in; + sock_f_out = f_out; +} + /** Setup the fd used to receive MSG_* messages. Only needed when * we're the generator because the sender and receiver both use the - * multiplexed IO setup. */ + * multiplexed I/O setup. */ void set_msg_fd_in(int fd) { msg_fd_in = fd; @@ -154,7 +172,7 @@ /** Setup the fd used to send our MSG_* messages. Only needed when * we're the receiver because the generator and the sender both use - * the multiplexed IO setup. */ + * the multiplexed I/O setup. */ void set_msg_fd_out(int fd) { msg_fd_out = fd; @@ -191,13 +209,13 @@ * called by the generator. */ static void read_msg_fd(void) { - char buf[200]; + char buf[2048]; size_t n; int fd = msg_fd_in; int tag, len; - /* Temporarily disable msg_fd_in. This is needed because we - * may call a write routine that could try to call us back. */ + /* Temporarily disable msg_fd_in. This is needed to avoid looping back + * to this routine from read_timeout() and writefd_unbuffered(). */ msg_fd_in = -1; read_loop(fd, buf, 4); @@ -244,7 +262,7 @@ /* Try to push messages off the list onto the wire. If we leave with more * to do, return 0. On error, return -1. If everything flushed, return 1. - * This is only called by the receiver. */ + * This is only active in the receiver. */ int msg_list_push(int flush_it_all) { static int written = 0; @@ -266,7 +284,7 @@ return 0; FD_ZERO(&fds); FD_SET(msg_fd_out, &fds); - tv.tv_sec = io_timeout ? io_timeout : SELECT_TIMEOUT; + tv.tv_sec = select_timeout; tv.tv_usec = 0; if (!select(msg_fd_out+1, NULL, &fds, NULL, &tv)) check_timeout(); @@ -329,34 +347,21 @@ * program where that is a problem (start_socket_client), * kludge_around_eof is True and we just exit. */ -static void whine_about_eof(void) +static void whine_about_eof(int fd) { - if (kludge_around_eof) + if (kludge_around_eof && fd == sock_f_in) exit_cleanup(0); - else { - rprintf(FERROR, - "%s: connection unexpectedly closed " - "(%.0f bytes read so far)\n", - RSYNC_NAME, (double)stats.total_read); - exit_cleanup(RERR_STREAMIO); - } -} + rprintf(FERROR, RSYNC_NAME ": connection unexpectedly closed " + "(%.0f bytes received so far) [%s]\n", + (double)stats.total_read, who_am_i()); - -static void die_from_readerr(int err) -{ - /* this prevents us trying to write errors on a dead socket */ - io_multiplexing_close(); - - rprintf(FERROR, "%s: read error: %s\n", - RSYNC_NAME, strerror(err)); exit_cleanup(RERR_STREAMIO); } /** - * Read from a socket with IO timeout. return the number of bytes + * Read from a socket with I/O timeout. return the number of bytes * read. If no bytes can be read then exit, never return a number <= 0. * * TODO: If the remote shell connection fails, then current versions @@ -368,7 +373,7 @@ */ static int read_timeout(int fd, char *buf, size_t len) { - int n, ret=0; + int n, ret = 0; io_flush(NORMAL_FLUSH); @@ -376,15 +381,20 @@ /* until we manage to read *something* */ fd_set r_fds, w_fds; struct timeval tv; - int fd_count = fd+1; + int maxfd = fd; int count; FD_ZERO(&r_fds); + FD_ZERO(&w_fds); FD_SET(fd, &r_fds); if (msg_fd_in >= 0) { FD_SET(msg_fd_in, &r_fds); - if (msg_fd_in >= fd_count) - fd_count = msg_fd_in+1; + if (msg_fd_in > maxfd) + maxfd = msg_fd_in; + } else if (msg_list_head) { + FD_SET(msg_fd_out, &w_fds); + if (msg_fd_out > maxfd) + maxfd = msg_fd_out; } if (io_filesfrom_f_out >= 0) { int new_fd; @@ -397,37 +407,31 @@ new_fd = -1; } } else { - FD_ZERO(&w_fds); FD_SET(io_filesfrom_f_out, &w_fds); new_fd = io_filesfrom_f_out; } - if (new_fd >= fd_count) - fd_count = new_fd+1; + if (new_fd > maxfd) + maxfd = new_fd; } - tv.tv_sec = io_timeout?io_timeout:SELECT_TIMEOUT; + tv.tv_sec = select_timeout; tv.tv_usec = 0; errno = 0; - count = select(fd_count, &r_fds, - io_filesfrom_buflen? &w_fds : NULL, - NULL, &tv); - - if (count == 0) { - msg_list_push(NORMAL_FLUSH); - check_timeout(); - } + count = select(maxfd + 1, &r_fds, &w_fds, NULL, &tv); if (count <= 0) { - if (errno == EBADF) { + if (errno == EBADF) exit_cleanup(RERR_SOCKETIO); - } + check_timeout(); continue; } if (msg_fd_in >= 0 && FD_ISSET(msg_fd_in, &r_fds)) read_msg_fd(); + else if (msg_list_head && FD_ISSET(msg_fd_out, &w_fds)) + msg_list_push(NORMAL_FLUSH); if (io_filesfrom_f_out >= 0) { if (io_filesfrom_buflen) { @@ -457,7 +461,6 @@ io_filesfrom_buflen = io_filesfrom_lastchar? 2 : 1; io_filesfrom_f_in = -1; } else { - extern int eol_nulls; if (!eol_nulls) { char *s = io_filesfrom_buf + l; /* Transform CR and/or LF into '\0' */ @@ -493,26 +496,31 @@ } } - if (!FD_ISSET(fd, &r_fds)) continue; + if (!FD_ISSET(fd, &r_fds)) + continue; n = read(fd, buf, len); - if (n > 0) { - buf += n; - len -= n; - ret += n; - if (io_timeout) - last_io = time(NULL); - continue; - } else if (n == 0) { - whine_about_eof(); - return -1; /* doesn't return */ - } else if (n < 0) { - if (errno == EINTR || errno == EWOULDBLOCK || - errno == EAGAIN) + if (n <= 0) { + if (n == 0) + whine_about_eof(fd); /* Doesn't return. */ + if (errno == EINTR || errno == EWOULDBLOCK + || errno == EAGAIN) continue; - die_from_readerr(errno); + + /* Don't write errors on a dead socket. */ + if (fd == sock_f_in) + close_multiplexing_out(); + rsyserr(FERROR, errno, "read error"); + exit_cleanup(RERR_STREAMIO); } + + buf += n; + len -= n; + ret += n; + + if (io_timeout && fd == sock_f_in) + last_io = time(NULL); } return ret; @@ -526,9 +534,6 @@ { char ch, *s, *eob = fname + MAXPATHLEN - 1; int cnt; - extern int io_timeout; - extern int eol_nulls; - extern char *remote_filesfrom_file; int reading_remotely = remote_filesfrom_file != NULL; int nulls = eol_nulls || reading_remotely; @@ -542,7 +547,7 @@ fd_set fds; FD_ZERO(&fds); FD_SET(fd, &fds); - tv.tv_sec = io_timeout? io_timeout : SELECT_TIMEOUT; + tv.tv_sec = select_timeout; tv.tv_usec = 0; if (!select(fd+1, &fds, NULL, NULL, &tv)) check_timeout(); @@ -569,6 +574,42 @@ } +static char *iobuf_out; +static int iobuf_out_cnt; + +void io_start_buffering_out(void) +{ + if (iobuf_out) + return; + if (!(iobuf_out = new_array(char, IO_BUFFER_SIZE))) + out_of_memory("io_start_buffering_out"); + iobuf_out_cnt = 0; +} + + +static char *iobuf_in; +static size_t iobuf_in_siz; + +void io_start_buffering_in(void) +{ + if (iobuf_in) + return; + iobuf_in_siz = 2 * IO_BUFFER_SIZE; + if (!(iobuf_in = new_array(char, iobuf_in_siz))) + out_of_memory("io_start_buffering_in"); +} + + +void io_end_buffering(void) +{ + io_flush(NORMAL_FLUSH); + if (!io_multiplexing_out) { + free(iobuf_out); + iobuf_out = NULL; + } +} + + /** * Continue trying to read len bytes - don't return until len has been * read. @@ -587,36 +628,29 @@ /** * Read from the file descriptor handling multiplexing - return number * of bytes read. - * - * Never returns <= 0. + * + * Never returns <= 0. */ -static int read_unbuffered(int fd, char *buf, size_t len) +static int readfd_unbuffered(int fd, char *buf, size_t len) { static size_t remaining; + static size_t iobuf_in_ndx; int tag, ret = 0; char line[1024]; - static char *buffer; - static size_t bufferIdx = 0; - static size_t bufferSz; - if (fd != multiplex_in_fd) + if (!iobuf_in || fd != sock_f_in) return read_timeout(fd, buf, len); if (!io_multiplexing_in && remaining == 0) { - if (!buffer) { - bufferSz = 2 * IO_BUFFER_SIZE; - buffer = new_array(char, bufferSz); - if (!buffer) out_of_memory("read_unbuffered"); - } - remaining = read_timeout(fd, buffer, bufferSz); - bufferIdx = 0; + remaining = read_timeout(fd, iobuf_in, iobuf_in_siz); + iobuf_in_ndx = 0; } while (ret == 0) { if (remaining) { len = MIN(len, remaining); - memcpy(buf, buffer + bufferIdx, len); - bufferIdx += len; + memcpy(buf, iobuf_in + iobuf_in_ndx, len); + iobuf_in_ndx += len; remaining -= len; ret = len; break; @@ -630,13 +664,14 @@ switch (tag) { case MSG_DATA: - if (!buffer || remaining > bufferSz) { - buffer = realloc_array(buffer, char, remaining); - if (!buffer) out_of_memory("read_unbuffered"); - bufferSz = remaining; + if (remaining > iobuf_in_siz) { + if (!(iobuf_in = realloc_array(iobuf_in, char, + remaining))) + out_of_memory("readfd_unbuffered"); + iobuf_in_siz = remaining; } - read_loop(fd, buffer, remaining); - bufferIdx = 0; + read_loop(fd, iobuf_in, remaining); + iobuf_in_ndx = 0; break; case MSG_INFO: case MSG_ERROR: @@ -671,14 +706,20 @@ static void readfd(int fd, char *buffer, size_t N) { int ret; - size_t total=0; + size_t total = 0; while (total < N) { - ret = read_unbuffered(fd, buffer + total, N-total); + ret = readfd_unbuffered(fd, buffer + total, N-total); total += ret; } - stats.total_read += total; + if (fd == write_batch_monitor_in) { + if ((size_t)write(batch_fd, buffer, total) != total) + exit_cleanup(RERR_FILEIO); + } + + if (fd == sock_f_in) + stats.total_read += total; } @@ -689,7 +730,8 @@ readfd(f,b,4); ret = IVAL(b,0); - if (ret == (int32)0xffffffff) return -1; + if (ret == (int32)0xffffffff) + return -1; return ret; } @@ -699,17 +741,17 @@ char b[8]; ret = read_int(f); - if ((int32)ret != (int32)0xffffffff) { + if ((int32)ret != (int32)0xffffffff) return ret; - } -#ifdef NO_INT64 - rprintf(FERROR,"Integer overflow - attempted 64 bit offset\n"); - exit_cleanup(RERR_UNSUPPORTED); -#else +#ifdef INT64_IS_OFF_T + if (sizeof (int64) < 8) { + rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n"); + exit_cleanup(RERR_UNSUPPORTED); + } +#endif readfd(f,b,8); ret = IVAL(b,0) | (((int64)IVAL(b,4))<<32); -#endif return ret; } @@ -721,14 +763,14 @@ void read_sbuf(int f,char *buf,size_t len) { - read_buf(f,buf,len); + readfd(f, buf, len); buf[len] = 0; } unsigned char read_byte(int f) { unsigned char c; - read_buf(f, (char *)&c, 1); + readfd(f, (char *)&c, 1); return c; } @@ -741,81 +783,107 @@ * use a bit less bandwidth than specified, because it doesn't make up * for slow periods. But arguably this is a feature. In addition, we * ought to take the time used to write the data into account. + * + * During some phases of big transfers (file FOO is uptodate) this is + * called with a small bytes_written every time. As the kernel has to + * round small waits up to guarantee that we actually wait at least the + * requested number of microseconds, this can become grossly inaccurate. + * We therefore keep track of the bytes we've written over time and only + * sleep when the accumulated delay is at least 1 tenth of a second. **/ static void sleep_for_bwlimit(int bytes_written) { - struct timeval tv; + static struct timeval prior_tv; + static long total_written = 0; + struct timeval tv, start_tv; + long elapsed_usec, sleep_usec; + +#define ONE_SEC 1000000L /* # of microseconds in a second */ if (!bwlimit) return; - assert(bytes_written > 0); - assert(bwlimit > 0); + total_written += bytes_written; + + gettimeofday(&start_tv, NULL); + if (prior_tv.tv_sec) { + elapsed_usec = (start_tv.tv_sec - prior_tv.tv_sec) * ONE_SEC + + (start_tv.tv_usec - prior_tv.tv_usec); + total_written -= elapsed_usec * bwlimit / (ONE_SEC/1024); + if (total_written < 0) + total_written = 0; + } - tv.tv_usec = bytes_written * 1000 / bwlimit; - tv.tv_sec = tv.tv_usec / 1000000; - tv.tv_usec = tv.tv_usec % 1000000; + sleep_usec = total_written * (ONE_SEC/1024) / bwlimit; + if (sleep_usec < ONE_SEC / 10) { + prior_tv = start_tv; + return; + } + tv.tv_sec = sleep_usec / ONE_SEC; + tv.tv_usec = sleep_usec % ONE_SEC; select(0, NULL, NULL, NULL, &tv); + + gettimeofday(&prior_tv, NULL); + elapsed_usec = (prior_tv.tv_sec - start_tv.tv_sec) * ONE_SEC + + (prior_tv.tv_usec - start_tv.tv_usec); + total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_SEC/1024); } -/** - * Write len bytes to the file descriptor @p fd. +/* Write len bytes to the file descriptor fd, looping as necessary to get + * the job done and also (in the generator) reading any data on msg_fd_in + * (to avoid deadlock). * * This function underlies the multiplexing system. The body of the - * application never calls this function directly. - **/ + * application never calls this function directly. */ static void writefd_unbuffered(int fd,char *buf,size_t len) { - size_t total = 0; + size_t n, total = 0; fd_set w_fds, r_fds; - int fd_count, count; + int maxfd, count, ret; struct timeval tv; - msg_list_push(NORMAL_FLUSH); - no_flush++; while (total < len) { FD_ZERO(&w_fds); FD_SET(fd,&w_fds); - fd_count = fd; + maxfd = fd; if (msg_fd_in >= 0) { FD_ZERO(&r_fds); FD_SET(msg_fd_in,&r_fds); - if (msg_fd_in > fd_count) - fd_count = msg_fd_in; + if (msg_fd_in > maxfd) + maxfd = msg_fd_in; } - tv.tv_sec = io_timeout?io_timeout:SELECT_TIMEOUT; + tv.tv_sec = select_timeout; tv.tv_usec = 0; errno = 0; - count = select(fd_count+1, msg_fd_in >= 0 ? &r_fds : NULL, + count = select(maxfd + 1, msg_fd_in >= 0 ? &r_fds : NULL, &w_fds, NULL, &tv); - if (count == 0) { - msg_list_push(NORMAL_FLUSH); - check_timeout(); - } - if (count <= 0) { - if (errno == EBADF) { + if (count < 0 && errno == EBADF) exit_cleanup(RERR_SOCKETIO); - } + check_timeout(); continue; } if (msg_fd_in >= 0 && FD_ISSET(msg_fd_in, &r_fds)) read_msg_fd(); - if (FD_ISSET(fd, &w_fds)) { - int ret; - size_t n = len-total; - ret = write(fd,buf+total,n); + if (!FD_ISSET(fd, &w_fds)) + continue; + n = len - total; + if (bwlimit && n > bwlimit_writemax) + n = bwlimit_writemax; + ret = write(fd, buf + total, n); + + if (ret <= 0) { if (ret < 0) { if (errno == EINTR) continue; @@ -825,23 +893,28 @@ } } - if (ret <= 0) { - /* Don't try to write errors back - * across the stream */ - io_multiplexing_close(); - rprintf(FERROR, RSYNC_NAME - ": writefd_unbuffered failed to write %ld bytes: phase \"%s\": %s\n", - (long) len, io_write_phase, - strerror(errno)); - exit_cleanup(RERR_STREAMIO); + /* Don't try to write errors back across the stream. */ + if (fd == sock_f_out) + close_multiplexing_out(); + rsyserr(FERROR, errno, + "writefd_unbuffered failed to write %ld bytes: phase \"%s\" [%s]", + (long)len, io_write_phase, who_am_i()); + /* If the other side is sending us error messages, try + * to grab any messages they sent before they died. */ + while (fd == sock_f_out && io_multiplexing_in) { + io_timeout = 30; + readfd_unbuffered(sock_f_in, io_filesfrom_buf, + sizeof io_filesfrom_buf); } + exit_cleanup(RERR_STREAMIO); + } - sleep_for_bwlimit(ret); - - total += ret; + total += ret; + if (fd == sock_f_out) { if (io_timeout) last_io = time(NULL); + sleep_for_bwlimit(ret); } } @@ -849,97 +922,76 @@ } -static char *io_buffer; -static int io_buffer_count; - -void io_start_buffering_out(int fd) -{ - if (io_buffer) return; - multiplex_out_fd = fd; - io_buffer = new_array(char, IO_BUFFER_SIZE); - if (!io_buffer) out_of_memory("writefd"); - io_buffer_count = 0; -} - -void io_start_buffering_in(int fd) -{ - multiplex_in_fd = fd; -} - /** * Write an message to a multiplexed stream. If this fails then rsync * exits. **/ -static void mplex_write(int fd, enum msgcode code, char *buf, size_t len) +static void mplex_write(enum msgcode code, char *buf, size_t len) { char buffer[4096]; size_t n = len; SIVAL(buffer, 0, ((MPLEX_BASE + (int)code)<<24) + len); - if (n > (sizeof buffer - 4)) { + if (n > sizeof buffer - 4) n = sizeof buffer - 4; - } memcpy(&buffer[4], buf, n); - writefd_unbuffered(fd, buffer, n+4); + writefd_unbuffered(sock_f_out, buffer, n+4); len -= n; buf += n; - if (len) { - writefd_unbuffered(fd, buf, len); - } + if (len) + writefd_unbuffered(sock_f_out, buf, len); } void io_flush(int flush_it_all) { - int fd = multiplex_out_fd; - msg_list_push(flush_it_all); - if (!io_buffer_count || no_flush) + if (!iobuf_out_cnt || no_flush) return; if (io_multiplexing_out) - mplex_write(fd, MSG_DATA, io_buffer, io_buffer_count); + mplex_write(MSG_DATA, iobuf_out, iobuf_out_cnt); else - writefd_unbuffered(fd, io_buffer, io_buffer_count); - io_buffer_count = 0; + writefd_unbuffered(sock_f_out, iobuf_out, iobuf_out_cnt); + iobuf_out_cnt = 0; } -void io_end_buffering(void) +static void writefd(int fd,char *buf,size_t len) { - io_flush(NORMAL_FLUSH); - if (!io_multiplexing_out) { - free(io_buffer); - io_buffer = NULL; + if (fd == msg_fd_out) { + rprintf(FERROR, "Internal error: wrong write used in receiver.\n"); + exit_cleanup(RERR_PROTOCOL); } -} -static void writefd(int fd,char *buf,size_t len) -{ - stats.total_written += len; + if (fd == sock_f_out) + stats.total_written += len; - msg_list_push(NORMAL_FLUSH); + if (fd == write_batch_monitor_out) { + if ((size_t)write(batch_fd, buf, len) != len) + exit_cleanup(RERR_FILEIO); + } - if (!io_buffer || fd != multiplex_out_fd) { + if (!iobuf_out || fd != sock_f_out) { writefd_unbuffered(fd, buf, len); return; } while (len) { - int n = MIN((int) len, IO_BUFFER_SIZE-io_buffer_count); + int n = MIN((int)len, IO_BUFFER_SIZE - iobuf_out_cnt); if (n > 0) { - memcpy(io_buffer+io_buffer_count, buf, n); + memcpy(iobuf_out+iobuf_out_cnt, buf, n); buf += n; len -= n; - io_buffer_count += n; + iobuf_out_cnt += n; } - if (io_buffer_count == IO_BUFFER_SIZE) + if (iobuf_out_cnt == IO_BUFFER_SIZE) io_flush(NORMAL_FLUSH); } } @@ -974,16 +1026,18 @@ return; } -#ifdef NO_INT64 - rprintf(FERROR,"Integer overflow - attempted 64 bit offset\n"); - exit_cleanup(RERR_UNSUPPORTED); -#else +#ifdef INT64_IS_OFF_T + if (sizeof (int64) < 8) { + rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n"); + exit_cleanup(RERR_UNSUPPORTED); + } +#endif + write_int(f, (int32)0xFFFFFFFF); SIVAL(b,0,(x&0xFFFFFFFF)); SIVAL(b,4,((x>>32)&0xFFFFFFFF)); writefd(f,b,8); -#endif } void write_buf(int f,char *buf,size_t len) @@ -992,24 +1046,24 @@ } /** Write a string to the connection */ -static void write_sbuf(int f,char *buf) +void write_sbuf(int f, char *buf) { - write_buf(f, buf, strlen(buf)); + writefd(f, buf, strlen(buf)); } - void write_byte(int f,unsigned char c) { - write_buf(f,(char *)&c,1); + writefd(f, (char *)&c, 1); } /** - * Read a line of up to @p maxlen characters into @p buf. Does not - * contain a trailing newline or carriage return. + * Read a line of up to @p maxlen characters into @p buf (not counting + * the trailing null). Strips the (required) trailing newline and all + * carriage returns. * - * @return 1 for success; 0 for io error or truncation. + * @return 1 for success; 0 for I/O error or truncation. **/ int read_line(int f, char *buf, size_t maxlen) { @@ -1018,27 +1072,21 @@ read_buf(f, buf, 1); if (buf[0] == 0) return 0; - if (buf[0] == '\n') { - buf[0] = 0; + if (buf[0] == '\n') break; - } if (buf[0] != '\r') { buf++; maxlen--; } } - if (maxlen == 0) { - *buf = 0; - return 0; - } - - return 1; + *buf = '\0'; + return maxlen > 0; } void io_printf(int fd, const char *format, ...) { - va_list ap; + va_list ap; char buf[1024]; int len; @@ -1046,43 +1094,72 @@ len = vsnprintf(buf, sizeof buf, format, ap); va_end(ap); - if (len < 0) exit_cleanup(RERR_STREAMIO); + if (len < 0) + exit_cleanup(RERR_STREAMIO); write_sbuf(fd, buf); } /** Setup for multiplexing a MSG_* stream with the data stream. */ -void io_start_multiplex_out(int fd) +void io_start_multiplex_out(void) { - multiplex_out_fd = fd; io_flush(NORMAL_FLUSH); - io_start_buffering_out(fd); + io_start_buffering_out(); io_multiplexing_out = 1; } /** Setup for multiplexing a MSG_* stream with the data stream. */ -void io_start_multiplex_in(int fd) +void io_start_multiplex_in(void) { - multiplex_in_fd = fd; io_flush(NORMAL_FLUSH); + io_start_buffering_in(); io_multiplexing_in = 1; } /** Write an message to the multiplexed data stream. */ int io_multiplex_write(enum msgcode code, char *buf, size_t len) { - if (!io_multiplexing_out) return 0; + if (!io_multiplexing_out) + return 0; io_flush(NORMAL_FLUSH); stats.total_written += (len+4); - mplex_write(multiplex_out_fd, code, buf, len); + mplex_write(code, buf, len); return 1; } +void close_multiplexing_in(void) +{ + io_multiplexing_in = 0; +} + /** Stop output multiplexing. */ -void io_multiplexing_close(void) +void close_multiplexing_out(void) { io_multiplexing_out = 0; } +void start_write_batch(int fd) +{ + write_stream_flags(batch_fd); + + /* Some communication has already taken place, but we don't + * enable batch writing until here so that we can write a + * canonical record of the communication even though the + * actual communication so far depends on whether a daemon + * is involved. */ + write_int(batch_fd, protocol_version); + write_int(batch_fd, checksum_seed); + + if (am_sender) + write_batch_monitor_out = fd; + else + write_batch_monitor_in = fd; +} + +void stop_write_batch(void) +{ + write_batch_monitor_out = -1; + write_batch_monitor_in = -1; +} diff -urN --exclude=patches rsync-2.6.2/loadparm.c rsync-2.6.3/loadparm.c --- rsync-2.6.2/loadparm.c 2004-02-03 23:31:29.000000000 -0800 +++ rsync-2.6.3/loadparm.c 2004-07-30 13:05:37.000000000 -0700 @@ -119,6 +119,7 @@ char *comment; char *lock_file; BOOL read_only; + BOOL write_only; BOOL list; BOOL use_chroot; BOOL transfer_logging; @@ -151,6 +152,7 @@ NULL, /* comment */ DEFAULT_LOCK_FILE, /* lock file */ True, /* read only */ + False, /* write only */ True, /* list */ True, /* use chroot */ False, /* transfer logging */ @@ -277,6 +279,7 @@ {"lock file", P_STRING, P_LOCAL, &sDefault.lock_file, NULL, 0}, {"path", P_PATH, P_LOCAL, &sDefault.path, NULL, 0}, {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL, 0}, + {"write only", P_BOOL, P_LOCAL, &sDefault.write_only, NULL, 0}, {"list", P_BOOL, P_LOCAL, &sDefault.list, NULL, 0}, {"use chroot", P_BOOL, P_LOCAL, &sDefault.use_chroot, NULL, 0}, {"ignore nonreadable",P_BOOL, P_LOCAL, &sDefault.ignore_nonreadable, NULL, 0}, @@ -356,6 +359,7 @@ FN_LOCAL_STRING(lp_path, path) FN_LOCAL_STRING(lp_lock_file, lock_file) FN_LOCAL_BOOL(lp_read_only, read_only) +FN_LOCAL_BOOL(lp_write_only, write_only) FN_LOCAL_BOOL(lp_list, list) FN_LOCAL_BOOL(lp_use_chroot, use_chroot) FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging) @@ -807,7 +811,7 @@ int iService; for (iService = iNumServices - 1; iService >= 0; iService--) - if (strequal(lp_name(iService), name)) + if (strcmp(lp_name(iService), name) == 0) break; return (iService); diff -urN --exclude=patches rsync-2.6.2/log.c rsync-2.6.3/log.c --- rsync-2.6.2/log.c 2004-04-29 12:34:31.000000000 -0700 +++ rsync-2.6.3/log.c 2004-09-05 14:30:00.000000000 -0700 @@ -41,7 +41,7 @@ static FILE *logfile; struct stats stats; -int log_got_error=0; +int log_got_error = 0; struct { int code; @@ -61,7 +61,7 @@ { RERR_WAITCHILD , "some error returned by waitpid()" }, { RERR_MALLOC , "error allocating core memory buffers" }, { RERR_PARTIAL , "some files could not be transferred" }, - { RERR_VANISHED , "some files vanished before they could be transfered" }, + { RERR_VANISHED , "some files vanished before they could be transferred" }, { RERR_TIMEOUT , "timeout in data send/receive" }, { RERR_CMD_FAILED , "remote shell failed" }, { RERR_CMD_KILLED , "remote shell killed" }, @@ -104,7 +104,8 @@ int options = LOG_PID; time_t t; - if (log_initialised) return; + if (log_initialised) + return; log_initialised = 1; /* this looks pointless, but it is needed in order for the @@ -160,7 +161,7 @@ * it with FINFO, FERROR or FLOG */ void rwrite(enum logcode code, char *buf, int len) { - FILE *f=NULL; + FILE *f = NULL; /* recursion can happen with certain fatal conditions */ if (quiet && code == FINFO) @@ -171,45 +172,39 @@ buf[len] = 0; - if (code == FLOG) { - if (am_daemon) logit(LOG_INFO, buf); + if (am_server && msg_fd_out >= 0) { + /* Pass the message to our sibling. */ + send_msg((enum msgcode)code, buf, len); return; } - if (am_server) { - /* Pass it to non-server side, perhaps through our sibling. */ - if (msg_fd_out >= 0) { - send_msg((enum msgcode)code, buf, len); - return; - } - if (!am_daemon - && io_multiplex_write((enum msgcode)code, buf, len)) - return; - } - - /* otherwise, if in daemon mode and either we are not a server - * (that is, we are not running --daemon over a remote shell) or - * the log has already been initialised, log the message on this - * side because we don't want the client to see most errors for - * security reasons. We do want early messages when running daemon - * mode over a remote shell to go to the remote side; those will - * fall through to the next case. - * Note that this is only for the time before multiplexing is enabled. - */ - if (am_daemon && (!am_server || log_initialised)) { - static int depth; - int priority = LOG_INFO; - if (code == FERROR) priority = LOG_WARNING; - - if (depth) return; - - depth++; + if (am_daemon) { + static int in_block; + char msg[2048]; + int priority = code == FERROR ? LOG_WARNING : LOG_INFO; - log_init(); - logit(priority, buf); + if (in_block) + return; + in_block = 1; + if (!log_initialised) + log_init(); + strlcpy(msg, buf, MIN((int)sizeof msg, len + 1)); + logit(priority, msg); + in_block = 0; - depth--; + if (code == FLOG || !am_server) + return; + } else if (code == FLOG) return; + + if (am_server) { + /* Pass the message to the non-server side. */ + if (io_multiplex_write((enum msgcode)code, buf, len)) + return; + if (am_daemon) { + /* TODO: can we send the error to the user somehow? */ + return; + } } if (code == FERROR) { @@ -217,18 +212,17 @@ f = stderr; } - if (code == FINFO) { - if (am_server) - f = stderr; - else - f = stdout; - } + if (code == FINFO) + f = am_server ? stderr : stdout; - if (!f) exit_cleanup(RERR_MESSAGEIO); + if (!f) + exit_cleanup(RERR_MESSAGEIO); - if (fwrite(buf, len, 1, f) != 1) exit_cleanup(RERR_MESSAGEIO); + if (fwrite(buf, len, 1, f) != 1) + exit_cleanup(RERR_MESSAGEIO); - if (buf[len-1] == '\r' || buf[len-1] == '\n') fflush(f); + if (buf[len-1] == '\r' || buf[len-1] == '\n') + fflush(f); } @@ -237,18 +231,17 @@ void rprintf(enum logcode code, const char *format, ...) { va_list ap; - char buf[1024]; - int len; + char buf[MAXPATHLEN+512]; + size_t len; va_start(ap, format); - /* Note: might return -1 */ len = vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); /* Deal with buffer overruns. Instead of panicking, just - * truncate the resulting string. Note that some vsnprintf()s - * return -1 on truncation, e.g., glibc 2.0.6 and earlier. */ - if ((size_t) len > sizeof(buf)-1 || len < 0) { + * truncate the resulting string. (Note that configure ensures + * that we have a vsnprintf() that doesn't ever return -1.) */ + if (len > sizeof buf - 1) { const char ellipsis[] = "[...]"; /* Reset length, and zero-terminate the end of our buffer */ @@ -286,33 +279,23 @@ void rsyserr(enum logcode code, int errcode, const char *format, ...) { va_list ap; - char buf[1024]; - int len; - size_t sys_len; - char *sysmsg; + char buf[MAXPATHLEN+512]; + size_t len; + + strcpy(buf, RSYNC_NAME ": "); + len = (sizeof RSYNC_NAME ": ") - 1; va_start(ap, format); - /* Note: might return <0 */ - len = vsnprintf(buf, sizeof(buf), format, ap); + len += vsnprintf(buf + len, sizeof buf - len, format, ap); va_end(ap); - /* TODO: Put in RSYNC_NAME at the start. */ - - if ((size_t) len > sizeof(buf)-1) - exit_cleanup(RERR_MESSAGEIO); - - sysmsg = strerror(errcode); - sys_len = strlen(sysmsg); - if ((size_t) len + 3 + sys_len > sizeof(buf) - 1) + if (len < sizeof buf) { + len += snprintf(buf + len, sizeof buf - len, + ": %s (%d)\n", strerror(errcode), errcode); + } + if (len >= sizeof buf) exit_cleanup(RERR_MESSAGEIO); - strcpy(buf + len, ": "); - len += 2; - strcpy(buf + len, sysmsg); - len += sys_len; - strcpy(buf + len, "\n"); - len++; - rwrite(code, buf, len); } @@ -364,8 +347,9 @@ * rather keep going until we reach the nul of the format. * Just to make sure we don't clobber that nul and therefore * accidentally keep going, we zero the buffer now. */ - memset(buf, 0, sizeof buf); - strlcpy(buf, format, sizeof(buf)); + l = strlcpy(buf, format, sizeof buf); + if (l < sizeof buf) + memset(buf + l, 0, sizeof buf - l); for (s = &buf[0]; s && (p = strchr(s,'%')); ) { n = NULL; @@ -389,7 +373,7 @@ pathjoin(buf2, sizeof buf2, file->basedir ? file->basedir : "", f_name(file)); - clean_fname(buf2); + clean_fname(buf2, 0); n = buf2; if (*n == '/') n++; break; diff -urN --exclude=patches rsync-2.6.2/main.c rsync-2.6.3/main.c --- rsync-2.6.2/main.c 2004-02-09 19:54:47.000000000 -0800 +++ rsync-2.6.3/main.c 2004-09-29 10:58:07.000000000 -0700 @@ -43,20 +43,25 @@ extern int log_got_error; extern int module_id; extern int orig_umask; +extern int copy_links; +extern int keep_dirlinks; extern int preserve_hard_links; extern int protocol_version; extern int recurse; extern int relative_paths; extern int rsync_port; +extern int whole_file; extern int read_batch; extern int write_batch; +extern int batch_fd; +extern int batch_gen_fd; extern int filesfrom_fd; extern pid_t cleanup_child_pid; extern char *files_from; extern char *remote_filesfrom_file; extern char *rsync_path; extern char *shell_cmd; -extern struct file_list *batch_flist; +extern char *batch_name; /* there's probably never more than at most 2 outstanding child processes, @@ -84,7 +89,7 @@ io_flush(FULL_FLUSH); } - if ((waited_pid == -1) && (errno == ECHILD)) { + if (waited_pid == -1 && errno == ECHILD) { /* status of requested child no longer available. * check to see if it was processed by the sigchld_handler. */ @@ -105,8 +110,19 @@ *status = WEXITSTATUS(*status); } +/* This function gets called from all 3 processes. We want the client side + * to actually output the text, but the sender is the only process that has + * all the stats we need. So, if we're a client sender, we do the report. + * If we're a server sender, we write the stats on the supplied fd. If + * we're the client receiver we read the stats from the supplied fd and do + * the report. All processes might also generate a set of debug stats, if + * the verbose level is high enough (this is the only thing that the + * generator process and the server receiver ever do here). */ static void report(int f) { + /* Cache two stats because the read/write code can change it. */ + int64 total_read = stats.total_read; + int64 total_written = stats.total_written; time_t t = time(NULL); if (do_stats && verbose > 1) { @@ -120,18 +136,15 @@ if (am_daemon) { log_exit(0, __FILE__, __LINE__); - if (f == -1 || !am_sender) return; + if (f == -1 || !am_sender) + return; } if (am_server) { if (am_sender) { - int64 w; - /* store total_written in a temporary - * because write_longint changes it */ - w = stats.total_written; - write_longint(f,stats.total_read); - write_longint(f,w); - write_longint(f,stats.total_size); + write_longint(f, total_read); + write_longint(f, total_written); + write_longint(f, stats.total_size); } return; } @@ -139,12 +152,17 @@ /* this is the client */ if (!am_sender) { - int64 r; - stats.total_written = read_longint(f); - /* store total_read in a temporary, read_longint changes it */ - r = read_longint(f); + /* Read the first two in opposite order because the meaning of + * read/write swaps when switching from sender to receiver. */ + total_written = read_longint(f); + total_read = read_longint(f); stats.total_size = read_longint(f); - stats.total_read = r; + } else if (write_batch) { + /* The --read-batch process is going to be a client + * receiver, so we need to give it the stats. */ + write_longint(batch_fd, total_read); + write_longint(batch_fd, total_written); + write_longint(batch_fd, stats.total_size); } if (do_stats) { @@ -160,20 +178,20 @@ rprintf(FINFO,"Matched data: %.0f bytes\n", (double)stats.matched_data); rprintf(FINFO,"File list size: %d\n", stats.flist_size); - rprintf(FINFO,"Total bytes written: %.0f\n", - (double)stats.total_written); - rprintf(FINFO,"Total bytes read: %.0f\n", - (double)stats.total_read); + rprintf(FINFO,"Total bytes sent: %.0f\n", + (double)total_written); + rprintf(FINFO,"Total bytes received: %.0f\n", + (double)total_read); } if (verbose || do_stats) { - rprintf(FINFO,"\nwrote %.0f bytes read %.0f bytes %.2f bytes/sec\n", - (double)stats.total_written, - (double)stats.total_read, - (stats.total_written+stats.total_read)/(0.5 + (t-starttime))); - rprintf(FINFO,"total size is %.0f speedup is %.2f\n", + rprintf(FINFO, + "\nsent %.0f bytes received %.0f bytes %.2f bytes/sec\n", + (double)total_written, (double)total_read, + (total_written + total_read)/(0.5 + (t - starttime))); + rprintf(FINFO, "total size is %.0f speedup is %.2f\n", (double)stats.total_size, - (1.0*stats.total_size)/(stats.total_written+stats.total_read)); + (double)stats.total_size / (total_written+total_read)); } fflush(stdout); @@ -217,7 +235,7 @@ int *f_in, int *f_out) { int i, argc = 0; - char *args[100]; + char *args[MAX_ARGS]; pid_t ret; char *tok, *dir = NULL; int dash_l_set = 0; @@ -232,8 +250,14 @@ if (!cmd) goto oom; - for (tok = strtok(cmd, " "); tok; tok = strtok(NULL, " ")) + for (tok = strtok(cmd, " "); tok; tok = strtok(NULL, " ")) { + /* Comparison leaves rooms for server_options(). */ + if (argc >= MAX_ARGS - 100) { + rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n"); + exit_cleanup(RERR_SYNTAX); + } args[argc++] = tok; + } /* check to see if we've already been given '-l user' in * the remote-shell command */ @@ -270,6 +294,11 @@ } server_options(args,&argc); + + if (argc >= MAX_ARGS - 2) { + rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n"); + exit_cleanup(RERR_SYNTAX); + } } args[argc++] = "."; @@ -277,29 +306,36 @@ if (!daemon_over_rsh && path && *path) args[argc++] = path; - if (argc >= (int)(sizeof args / sizeof args[0])) { - rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n"); - exit_cleanup(RERR_MALLOC); /* XXX Need better RERR? */ - } - args[argc] = NULL; if (verbose > 3) { rprintf(FINFO,"cmd="); - for (i=0;i 0) - rprintf(FINFO,"created directory %s\n",name); + } + if (verbose > 0) + rprintf(FINFO, "created directory %s\n", name); + + if (dry_run) { + dry_run++; + return NULL; } if (!push_dir(name)) { - rprintf(FERROR, "push_dir %s failed: %s (2)\n", - full_fname(name), strerror(errno)); + rsyserr(FERROR, errno, "push_dir#2 %s failed", + full_fname(name)); exit_cleanup(RERR_FILESELECT); } @@ -370,9 +409,15 @@ (long)getpid()); } + if (am_daemon && lp_write_only(module_id)) { + rprintf(FERROR, "ERROR: module is write only\n"); + exit_cleanup(RERR_SYNTAX); + return; + } + if (!relative_paths && !push_dir(dir)) { - rprintf(FERROR, "push_dir %s failed: %s (3)\n", - full_fname(dir), strerror(errno)); + rsyserr(FERROR, errno, "push_dir#3 %s failed", + full_fname(dir)); exit_cleanup(RERR_FILESELECT); } argc--; @@ -382,12 +427,12 @@ int l = strlen(dir); if (strcmp(dir,"/") == 0) l = 0; - for (i=0;icount>0) { + if (recurse && delete_mode && !local_name && flist->count > 0) delete_files(flist); - } } if (fd_pair(error_pipe) < 0) { @@ -434,12 +483,13 @@ io_flush(NORMAL_FLUSH); - if ((pid=do_fork()) == 0) { + if ((pid = do_fork()) == 0) { close(error_pipe[0]); - if (f_in != f_out) close(f_out); + if (f_in != f_out) + close(f_out); /* we can't let two processes write to the socket at one time */ - io_multiplexing_close(); + close_multiplexing_out(); /* set place to send errors */ set_msg_fd_out(error_pipe[1]); @@ -458,11 +508,15 @@ } am_generator = 1; + close_multiplexing_in(); + if (write_batch) + stop_write_batch(); close(error_pipe[1]); - if (f_in != f_out) close(f_in); + if (f_in != f_out) + close(f_in); - io_start_buffering_out(f_out); + io_start_buffering_out(); set_msg_fd_in(error_pipe[0]); @@ -496,7 +550,7 @@ argc, (long)getpid()); } - if (am_daemon && lp_read_only(module_id) && !am_sender) { + if (am_daemon && lp_read_only(module_id)) { rprintf(FERROR,"ERROR: module is read only\n"); exit_cleanup(RERR_SYNTAX); return; @@ -508,13 +562,13 @@ argc--; argv++; if (!am_daemon && !push_dir(dir)) { - rprintf(FERROR, "push_dir %s failed: %s (4)\n", - full_fname(dir), strerror(errno)); + rsyserr(FERROR, errno, "push_dir#4 %s failed", + full_fname(dir)); exit_cleanup(RERR_FILESELECT); } } - io_start_buffering_in(f_in); + io_start_buffering_in(); if (delete_mode && !delete_excluded) recv_exclude_list(f_in); @@ -527,10 +581,7 @@ filesfrom_fd = -1; } - if (read_batch) - flist = batch_flist; - else - flist = recv_file_list(f_in); + flist = recv_file_list(f_in); if (!flist) { rprintf(FERROR,"server_recv: recv_file_list error\n"); exit_cleanup(RERR_FILESELECT); @@ -539,7 +590,8 @@ if (argc > 0) { if (strcmp(dir,".")) { argv[0] += strlen(dir); - if (argv[0][0] == '/') argv[0]++; + if (argv[0][0] == '/') + argv[0]++; } local_name = get_local_name(flist,argv[0]); } @@ -558,20 +610,21 @@ void start_server(int f_in, int f_out, int argc, char *argv[]) { - setup_protocol(f_out, f_in); - set_nonblocking(f_in); set_nonblocking(f_out); + io_set_sock_fds(f_in, f_out); + setup_protocol(f_out, f_in); + if (protocol_version >= 23) - io_start_multiplex_out(f_out); + io_start_multiplex_out(); if (am_sender) { - if (!read_batch) { - recv_exclude_list(f_in); - if (cvs_exclude) - add_cvs_excludes(); - } + keep_dirlinks = 0; /* Must be disabled on the sender. */ + + recv_exclude_list(f_in); + if (cvs_exclude) + add_cvs_excludes(); do_server_sender(f_in, f_out, argc, argv); } else { do_server_recv(f_in, f_out, argc, argv); @@ -591,25 +644,41 @@ char *local_name = NULL; cleanup_child_pid = pid; - if (read_batch) - flist = batch_flist; - - set_nonblocking(f_in); - set_nonblocking(f_out); + if (read_batch) { + assert(am_sender == 0); + } else { + set_nonblocking(f_in); + set_nonblocking(f_out); + } + io_set_sock_fds(f_in, f_out); setup_protocol(f_out,f_in); - if (protocol_version >= 23) - io_start_multiplex_in(f_in); + if (protocol_version >= 23 && !read_batch) + io_start_multiplex_in(); + + /* We set our stderr file handle to blocking because ssh might have + * set it to non-blocking. This can be particularly troublesome if + * stderr is a clone of stdout, because ssh would have set our stdout + * to non-blocking at the same time (which can easily cause us to lose + * output from our print statements). This kluge shouldn't cause ssh + * any problems for how we use it. Note also that we delayed setting + * this until after the above protocol setup so that we know for sure + * that ssh is done twiddling its file descriptors. */ + set_blocking(STDERR_FILENO); if (am_sender) { - io_start_buffering_out(f_out); + keep_dirlinks = 0; /* Must be disabled on the sender. */ + io_start_buffering_out(); if (cvs_exclude) add_cvs_excludes(); if (delete_mode && !delete_excluded) send_exclude_list(f_out); if (remote_filesfrom_file) filesfrom_fd = f_in; + + if (write_batch) + start_write_batch(f_out); if (!read_batch) /* don't write to pipe */ flist = send_file_list(f_out,argc,argv); if (verbose > 3) @@ -633,11 +702,10 @@ exit_cleanup(status); } - if (argc == 0) { + if (argc == 0) list_only = 1; - } - if (!write_batch) + if (!read_batch) send_exclude_list(f_out); if (filesfrom_fd >= 0) { @@ -645,6 +713,8 @@ filesfrom_fd = -1; } + if (write_batch) + start_write_batch(f_in); flist = recv_file_list(f_in); if (!flist || flist->count == 0) { rprintf(FINFO, "client: nothing to do: " @@ -708,33 +778,39 @@ return rc; /* rsync:// always uses rsync server over direct socket connection */ - if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0) { + if (strncasecmp(URL_PREFIX, argv[0], strlen(URL_PREFIX)) == 0 + && !read_batch) { char *host, *path; host = argv[0] + strlen(URL_PREFIX); p = strchr(host,'/'); if (p) { - *p = 0; + *p = '\0'; path = p+1; - } else { + } else path = ""; - } - p = strchr(host,':'); + if (*host == '[' && (p = strchr(host, ']')) != NULL) { + host++; + *p++ = '\0'; + if (*p != ':') + p = NULL; + } else + p = strchr(host, ':'); if (p) { rsync_port = atoi(p+1); - *p = 0; + *p = '\0'; } return start_socket_client(host, path, argc-1, argv+1); } - if (!read_batch) { + if (!read_batch) { /* for read_batch, NO source is specified */ p = find_colon(argv[0]); - if (p) { + if (p) { /* source is remote */ if (remote_filesfrom_file && remote_filesfrom_file != files_from + 1 && strncmp(files_from, argv[0], p-argv[0]+1) != 0) { rprintf(FERROR, - "--files-from hostname is not transfer hostname\n"); + "--files-from hostname is not the same as the transfer hostname\n"); exit_cleanup(RERR_SYNTAX); } if (p[1] == ':') { /* double colon */ @@ -747,7 +823,7 @@ daemon_over_rsh = 1; } - if (argc < 1) { + if (argc < 1) { /* destination required */ usage(FERROR); exit_cleanup(RERR_SYNTAX); } @@ -756,9 +832,8 @@ *p = 0; shell_machine = argv[0]; shell_path = p+1; - argc--; argv++; - } else { + } else { /* source is local */ am_sender = 1; /* rsync:// destination uses rsync server over direct socket */ @@ -768,32 +843,37 @@ host = argv[argc-1] + strlen(URL_PREFIX); p = strchr(host,'/'); if (p) { - *p = 0; + *p = '\0'; path = p+1; - } else { + } else path = ""; - } - p = strchr(host,':'); + if (*host == '[' && (p = strchr(host, ']')) != NULL) { + host++; + *p++ = '\0'; + if (*p != ':') + p = NULL; + } else + p = strchr(host, ':'); if (p) { rsync_port = atoi(p+1); - *p = 0; + *p = '\0'; } return start_socket_client(host, path, argc-1, argv); } - p = find_colon(argv[argc-1]); + p = find_colon(argv[argc-1]); /* look in dest arg */ if (p && remote_filesfrom_file && remote_filesfrom_file != files_from + 1 && strncmp(files_from, argv[argc-1], p-argv[argc-1]+1) != 0) { rprintf(FERROR, - "--files-from hostname is not transfer hostname\n"); + "--files-from hostname is not the same as the transfer hostname\n"); exit_cleanup(RERR_SYNTAX); } - if (!p) { + if (!p) { /* no colon found, so src & dest are local */ local_server = 1; if (remote_filesfrom_file) { rprintf(FERROR, - "--files-from is remote but transfer is local\n"); + "--files-from cannot be remote when the transfer is local\n"); exit_cleanup(RERR_SYNTAX); } } else if (p[1] == ':') { /* double colon */ @@ -819,12 +899,15 @@ shell_machine = argv[argc-1]; shell_path = p+1; } - argc--; } - } else { - am_sender = 1; + argc--; + } else { /* read_batch */ local_server = 1; shell_path = argv[argc-1]; + if (find_colon(shell_path)) { + rprintf(FERROR, "remote destination is not allowed with --read-batch\n"); + exit_cleanup(RERR_SYNTAX); + } } if (shell_machine) { @@ -844,12 +927,14 @@ shell_path?shell_path:""); } + /* for remote source, only single dest arg can remain ... */ if (!am_sender && argc > 1) { usage(FERROR); exit_cleanup(RERR_SYNTAX); } - if (argc == 0 && !am_sender) { + /* ... or no dest at all */ + if (!am_sender && argc == 0) { list_only = 1; } @@ -965,11 +1050,8 @@ int main(int argc,char *argv[]) { int ret; - int orig_argc; - char **orig_argv; - - orig_argc = argc; - orig_argv = argv; + int orig_argc = argc; + char **orig_argv = argv; signal(SIGUSR1, sigusr1_handler); signal(SIGUSR2, sigusr2_handler); @@ -1018,8 +1100,24 @@ init_flist(); - if (write_batch && !am_server) { - write_batch_argvs_file(orig_argc, orig_argv); + if (write_batch || read_batch) { + if (write_batch) + write_batch_shell_file(orig_argc, orig_argv, argc); + + if (read_batch && strcmp(batch_name, "-") == 0) + batch_fd = STDIN_FILENO; + else { + batch_fd = do_open(batch_name, + write_batch ? O_WRONLY | O_CREAT | O_TRUNC + : O_RDONLY, S_IRUSR | S_IWUSR); + } + if (batch_fd < 0) { + rsyserr(FERROR, errno, "Batch file %s open error", + batch_name); + exit_cleanup(RERR_FILEIO); + } + if (read_batch) + read_stream_flags(batch_fd); } if (am_daemon && !am_server) diff -urN --exclude=patches rsync-2.6.2/match.c rsync-2.6.3/match.c --- rsync-2.6.2/match.c 2004-01-03 11:28:03.000000000 -0800 +++ rsync-2.6.3/match.c 2004-09-07 14:45:19.000000000 -0700 @@ -22,6 +22,9 @@ extern int verbose; extern int am_server; extern int do_progress; +extern int checksum_seed; +extern int inplace; +extern int make_backups; typedef unsigned short tag; @@ -139,16 +142,17 @@ static void hash_search(int f,struct sum_struct *s, struct map_struct *buf, OFF_T len) { - OFF_T offset, end; + OFF_T offset, end, backup; unsigned int k; - size_t last_i; + size_t want_i; char sum2[SUM_LENGTH]; uint32 s1, s2, sum; + int more; schar *map; - /* last_i is used to encourage adjacent matches, allowing the RLL coding of the - output to work more efficiently */ - last_i = (size_t)-1; + /* want_i is used to encourage adjacent matches, allowing the RLL + * coding of the output to work more efficiently. */ + want_i = 0; if (verbose > 2) { rprintf(FINFO,"hash search b=%u len=%.0f\n", @@ -187,7 +191,7 @@ sum = (s1 & 0xffff) | (s2 << 16); tag_hits++; - for (; j < s->count && targets[j].t == t; j++) { + do { unsigned int l; size_t i = targets[j].i; @@ -199,6 +203,12 @@ if (l != s->sums[i].len) continue; + /* inplace: ensure chunk's offset is either >= our + * offset or that the data didn't move. */ + if (inplace && !make_backups && s->sums[i].offset < offset + && !(s->sums[i].flags & SUMFLG_SAME_OFFSET)) + continue; + if (verbose > 3) rprintf(FINFO,"potential match at %.0f target=%.0f %.0f sum=%08x\n", (double)offset,(double)j,(double)i,sum); @@ -214,23 +224,42 @@ continue; } - /* we've found a match, but now check to see - * if last_i can hint at a better match */ - for (j++; j < s->count && targets[j].t == t; j++) { - size_t i2 = targets[j].i; - if (i2 == last_i + 1) { - if (sum != s->sums[i2].sum1) - break; - if (memcmp(sum2,s->sums[i2].sum2,s->s2length) != 0) - break; - /* we've found an adjacent match - the RLL coder - * will be happy */ - i = i2; - break; - } + /* If inplace is enabled, the best possible match is + * one with an identical offset, so we prefer that over + * the following want_i optimization. */ + if (inplace && !make_backups) { + do { + size_t i2 = targets[j].i; + if (s->sums[i2].offset != offset) + continue; + if (i2 != i) { + if (sum != s->sums[i2].sum1) + break; + if (memcmp(sum2, s->sums[i2].sum2, + s->s2length) != 0) + break; + i = i2; + } + /* This chunk was at the same offset on + * both the sender and the receiver. */ + s->sums[i].flags |= SUMFLG_SAME_OFFSET; + goto set_want_i; + } while (++j < s->count && targets[j].t == t); } - last_i = i; + /* we've found a match, but now check to see + * if want_i can hint at a better match. */ + if (i != want_i && want_i < s->count + && (!inplace || make_backups || s->sums[want_i].offset >= offset + || s->sums[want_i].flags & SUMFLG_SAME_OFFSET) + && sum == s->sums[want_i].sum1 + && memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) { + /* we've found an adjacent match - the RLL coder + * will be happy */ + i = want_i; + } + set_want_i: + want_i = i + 1; matched(f,s,buf,offset,i); offset += s->sums[i].len - 1; @@ -241,17 +270,24 @@ s2 = sum >> 16; matches++; break; - } + } while (++j < s->count && targets[j].t == t); null_tag: + backup = offset - last_match; + /* We sometimes read 1 byte prior to last_match... */ + if (backup < 0) + backup = 0; + /* Trim off the first byte from the checksum */ - map = (schar *)map_ptr(buf, offset, k+1); + more = offset + k < len; + map = (schar *)map_ptr(buf, offset - backup, k + more + backup) + + backup; s1 -= map[0] + CHAR_OFFSET; s2 -= k * (map[0]+CHAR_OFFSET); /* Add on the next byte (if there is one) to the checksum */ - if (k < (len-offset)) { - s1 += (map[k]+CHAR_OFFSET); + if (more) { + s1 += map[k] + CHAR_OFFSET; s2 += s1; } else --k; @@ -262,9 +298,8 @@ match. The 3 reads are caused by the running match, the checksum update and the literal send. */ - if (offset > last_match - && offset-last_match >= CHUNK_SIZE+s->blength - && end-offset > CHUNK_SIZE) { + if (backup >= CHUNK_SIZE + s->blength + && end - offset > CHUNK_SIZE) { matched(f,s,buf,offset - s->blength, -2); } } while (++offset < end); @@ -291,7 +326,6 @@ void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len) { char file_sum[MD4_SUM_LENGTH]; - extern int write_batch; last_match = 0; false_alarms = 0; @@ -299,7 +333,7 @@ matches = 0; data_transfer = 0; - sum_init(); + sum_init(checksum_seed); if (len > 0 && s->count>0) { build_hash_table(s); @@ -322,12 +356,13 @@ } sum_end(file_sum); + /* If we had a read error, send a bad checksum. */ + if (buf && buf->status != 0) + file_sum[0]++; if (verbose > 2) rprintf(FINFO,"sending file_sum\n"); write_buf(f,file_sum,MD4_SUM_LENGTH); - if (write_batch) - write_batch_delta_file(file_sum, MD4_SUM_LENGTH); if (targets) { free(targets); diff -urN --exclude=patches rsync-2.6.2/options.c rsync-2.6.3/options.c --- rsync-2.6.2/options.c 2004-04-17 10:07:23.000000000 -0700 +++ rsync-2.6.3/options.c 2004-09-23 10:39:05.000000000 -0700 @@ -22,8 +22,9 @@ #include "popt.h" extern int sanitize_paths; -extern char curr_dir[MAXPATHLEN]; +extern int select_timeout; extern struct exclude_list_struct exclude_list; +extern struct exclude_list_struct server_exclude_list; int make_backups = 0; @@ -38,6 +39,7 @@ int whole_file = -1; int archive_mode = 0; +int keep_dirlinks = 0; int copy_links = 0; int preserve_links = 0; int preserve_hard_links = 0; @@ -83,6 +85,7 @@ int copy_unsafe_links = 0; int size_only = 0; int bwlimit = 0; +size_t bwlimit_writemax = 0; int delete_after = 0; int only_existing = 0; int opt_ignore_existing = 0; @@ -91,6 +94,7 @@ int modify_window = 0; int blocking_io = -1; int checksum_seed = 0; +int inplace = 0; unsigned int block_size = 0; @@ -114,6 +118,7 @@ char *backup_suffix = NULL; char *tmpdir = NULL; +char *partial_dir = NULL; char *compare_dest = NULL; char *config_file = NULL; char *shell_cmd = NULL; @@ -130,15 +135,14 @@ int always_checksum = 0; int list_only = 0; -#define FIXED_CHECKSUM_SEED 32761 -#define MAX_BATCH_PREFIX_LEN 256 /* Must be less than MAXPATHLEN-13 */ -char *batch_prefix = NULL; +#define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */ +char *batch_name = NULL; static int daemon_opt; /* sets am_daemon after option error-reporting */ static int modify_window_set; /** Local address to bind. As a character string because it's - * interpreted by the IPv6 layer: should be a numeric IP4 or ip6 + * interpreted by the IPv6 layer: should be a numeric IP4 or IP6 * address, or a hostname. **/ char *bind_address; @@ -146,6 +150,7 @@ static void print_rsync_version(enum logcode f) { char const *got_socketpair = "no "; + char const *have_inplace = "no "; char const *hardlinks = "no "; char const *links = "no "; char const *ipv6 = "no "; @@ -155,6 +160,10 @@ got_socketpair = ""; #endif +#if HAVE_FTRUNCATE + have_inplace = ""; +#endif + #if SUPPORT_HARD_LINKS hardlinks = ""; #endif @@ -180,8 +189,8 @@ /* Note that this field may not have type ino_t. It depends * on the complicated interaction between largefile feature * macros. */ - rprintf(f, " %sIPv6, %d-bit system inums, %d-bit internal inums\n", - ipv6, + rprintf(f, " %sinplace, %sIPv6, %d-bit system inums, %d-bit internal inums\n", + have_inplace, ipv6, (int) (sizeof dumstat->st_ino * 8), (int) (sizeof (uint64) * 8)); #ifdef MAINTAINER_MODE @@ -189,8 +198,9 @@ get_panic_action()); #endif -#ifdef NO_INT64 - rprintf(f, "WARNING: no 64-bit integers on this platform!\n"); +#ifdef INT64_IS_OFF_T + if (sizeof (int64) < 8) + rprintf(f, "WARNING: no 64-bit integers on this platform!\n"); #endif rprintf(f, @@ -231,6 +241,8 @@ rprintf(F," --backup-dir make backups into this directory\n"); rprintf(F," --suffix=SUFFIX backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX); rprintf(F," -u, --update update only (don't overwrite newer files)\n"); + rprintf(F," --inplace update destination files inplace (SEE MAN PAGE)\n"); + rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n"); rprintf(F," -l, --links copy symlinks as symlinks\n"); rprintf(F," -L, --copy-links copy the referent of all symlinks\n"); rprintf(F," --copy-unsafe-links copy the referent of \"unsafe\" symlinks\n"); @@ -246,7 +258,7 @@ rprintf(F," -W, --whole-file copy whole files, no incremental checks\n"); rprintf(F," --no-whole-file turn off --whole-file\n"); rprintf(F," -x, --one-file-system don't cross filesystem boundaries\n"); - rprintf(F," -B, --block-size=SIZE checksum blocking size (default %d)\n",BLOCK_SIZE); + rprintf(F," -B, --block-size=SIZE force a fixed checksum block-size\n"); rprintf(F," -e, --rsh=COMMAND specify the remote shell\n"); rprintf(F," --rsync-path=PATH specify path to rsync on the remote machine\n"); rprintf(F," --existing only update files that already exist\n"); @@ -257,13 +269,14 @@ rprintf(F," --ignore-errors delete even if there are I/O errors\n"); rprintf(F," --max-delete=NUM don't delete more than NUM files\n"); rprintf(F," --partial keep partially transferred files\n"); + rprintf(F," --partial-dir=DIR put a partially transferred file into DIR\n"); rprintf(F," --force force deletion of directories even if not empty\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"); rprintf(F," --size-only ignore mod time for quick check (use size)\n"); rprintf(F," --modify-window=NUM compare mod times with reduced accuracy\n"); - rprintf(F," -T --temp-dir=DIR create temporary files in directory DIR\n"); + rprintf(F," -T, --temp-dir=DIR create temporary files in directory DIR\n"); rprintf(F," --compare-dest=DIR also compare destination files relative to DIR\n"); rprintf(F," --link-dest=DIR create hardlinks to DIR for unchanged files\n"); rprintf(F," -P equivalent to --partial --progress\n"); @@ -274,7 +287,7 @@ rprintf(F," --include=PATTERN don't exclude files matching PATTERN\n"); rprintf(F," --include-from=FILE don't exclude patterns listed in FILE\n"); rprintf(F," --files-from=FILE read FILE for list of source-file names\n"); - rprintf(F," -0 --from0 all *-from file lists are delimited by nulls\n"); + rprintf(F," -0, --from0 all *-from file lists are delimited by nulls\n"); rprintf(F," --version print version number\n"); rprintf(F," --daemon run as an rsync daemon\n"); rprintf(F," --no-detach do not detach from the parent\n"); @@ -288,13 +301,14 @@ rprintf(F," --log-format=FORMAT log file transfers using specified format\n"); rprintf(F," --password-file=FILE get password from FILE\n"); rprintf(F," --bwlimit=KBPS limit I/O bandwidth, KBytes per second\n"); - rprintf(F," --write-batch=PREFIX write batch fileset starting with PREFIX\n"); - rprintf(F," --read-batch=PREFIX read batch fileset starting with PREFIX\n"); - rprintf(F," -h, --help show this help screen\n"); + rprintf(F," --write-batch=FILE write a batch to FILE\n"); + rprintf(F," --read-batch=FILE read a batch from FILE\n"); + rprintf(F," --checksum-seed=NUM set block/file checksum seed\n"); #ifdef INET6 - rprintf(F," -4 prefer IPv4\n"); - rprintf(F," -6 prefer IPv6\n"); + rprintf(F," -4, --ipv4 prefer IPv4\n"); + rprintf(F," -6, --ipv6 prefer IPv6\n"); #endif + rprintf(F," -h, --help show this help screen\n"); rprintf(F,"\n"); @@ -305,15 +319,15 @@ enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST, OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, - OPT_READ_BATCH, OPT_WRITE_BATCH, + OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ {"version", 0, POPT_ARG_NONE, 0, OPT_VERSION, 0, 0}, {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 }, - {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, - {"password-file", 0, POPT_ARG_STRING, &password_file, 0, 0, 0 }, + {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, + {"password-file", 0, POPT_ARG_STRING, &password_file, 0, 0, 0 }, {"ignore-times", 'I', POPT_ARG_NONE, &ignore_times, 0, 0, 0 }, {"size-only", 0, POPT_ARG_NONE, &size_only, 0, 0, 0 }, {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 }, @@ -336,6 +350,8 @@ {"sparse", 'S', POPT_ARG_NONE, &sparse_files, 0, 0, 0 }, {"cvs-exclude", 'C', POPT_ARG_NONE, &cvs_exclude, 0, 0, 0 }, {"update", 'u', POPT_ARG_NONE, &update_only, 0, 0, 0 }, + {"inplace", 0, POPT_ARG_NONE, &inplace, 0, 0, 0 }, + {"keep-dirlinks", 'K', POPT_ARG_NONE, &keep_dirlinks, 0, 0, 0 }, {"links", 'l', POPT_ARG_NONE, &preserve_links, 0, 0, 0 }, {"copy-links", 'L', POPT_ARG_NONE, ©_links, 0, 0, 0 }, {"whole-file", 'W', POPT_ARG_VAL, &whole_file, 1, 0, 0 }, @@ -358,7 +374,7 @@ {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 }, {"block-size", 'B', POPT_ARG_INT, &block_size, 0, 0, 0 }, {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 }, - {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 }, + {"timeout", 0, POPT_ARG_INT, &io_timeout, OPT_TIMEOUT, 0, 0 }, {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, {"compare-dest", 0, POPT_ARG_STRING, &compare_dest, 0, 0, 0 }, {"link-dest", 0, POPT_ARG_STRING, &compare_dest, OPT_LINK_DEST, 0, 0 }, @@ -369,6 +385,7 @@ {"stats", 0, POPT_ARG_NONE, &do_stats, 0, 0, 0 }, {"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 }, {"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 }, @@ -380,21 +397,22 @@ {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 }, {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 }, {"hard-links", 'H', POPT_ARG_NONE, &preserve_hard_links, 0, 0, 0 }, - {"read-batch", 0, POPT_ARG_STRING, &batch_prefix, OPT_READ_BATCH, 0, 0 }, - {"write-batch", 0, POPT_ARG_STRING, &batch_prefix, OPT_WRITE_BATCH, 0, 0 }, + {"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 }, + {"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 }, {"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 }, {"from0", '0', POPT_ARG_NONE, &eol_nulls, 0, 0, 0}, {"no-implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 }, {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, + {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 }, #ifdef INET6 - {0, '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 }, - {0, '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 }, + {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 }, + {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 }, #endif {0,0,0,0, 0, 0, 0} }; -static char err_buf[100]; +static char err_buf[200]; /** @@ -404,15 +422,12 @@ **/ void option_error(void) { - if (err_buf[0]) { - rprintf(FLOG, "%s", err_buf); - rprintf(FERROR, RSYNC_NAME ": %s", err_buf); - } else { - rprintf (FERROR, "Error parsing options: " - "option may be supported on client but not on server?\n"); - rprintf (FERROR, RSYNC_NAME ": Error parsing options: " - "option may be supported on client but not on server?\n"); + if (!err_buf[0]) { + strcpy(err_buf, "Error parsing options: " + "option may be supported on client but not on server?\n"); } + + rprintf(FERROR, RSYNC_NAME ": %s", err_buf); } @@ -423,23 +438,38 @@ static void set_refuse_options(char *bp) { struct poptOption *op; - char *cp; + char *cp, shortname[2]; + int is_wild, found_match; + + shortname[1] = '\0'; while (1) { + while (*bp == ' ') bp++; + if (!*bp) + break; if ((cp = strchr(bp, ' ')) != NULL) *cp= '\0'; + /* If they specify "delete", reject all delete options. */ + if (strcmp(bp, "delete") == 0) + bp = "delete*"; + is_wild = strpbrk(bp, "*?[") != NULL; + found_match = 0; for (op = long_options; ; op++) { - if (!op->longName) { - rprintf(FLOG, - "Unknown option %s in \"refuse options\" setting\n", - bp); - break; - } - if (strcmp(bp, op->longName) == 0) { - op->val = (op - long_options)+OPT_REFUSED_BASE; + *shortname = op->shortName; + if (!op->longName && !*shortname) break; + if ((op->longName && wildmatch(bp, op->longName)) + || (*shortname && wildmatch(bp, shortname))) { + op->val = (op - long_options) + OPT_REFUSED_BASE; + found_match = 1; + if (!is_wild) + break; } } + if (!found_match) { + rprintf(FLOG, "No match for refuse-options string \"%s\"\n", + bp); + } if (!cp) break; *cp = ' '; @@ -519,19 +549,19 @@ break; case OPT_EXCLUDE_FROM: - arg = poptGetOptArg(pc); - if (sanitize_paths) - arg = alloc_sanitize_path(arg, curr_dir); - add_exclude_file(&exclude_list, arg, - XFLG_FATAL_ERRORS); - break; - case OPT_INCLUDE_FROM: arg = poptGetOptArg(pc); if (sanitize_paths) - arg = alloc_sanitize_path(arg, curr_dir); - add_exclude_file(&exclude_list, arg, - XFLG_FATAL_ERRORS | XFLG_DEF_INCLUDE); + arg = sanitize_path(NULL, arg, NULL, 0); + if (server_exclude_list.head) { + char *cp = (char *)arg; + clean_fname(cp, 1); + if (check_exclude(&server_exclude_list, cp, 0) < 0) + goto options_rejected; + } + add_exclude_file(&exclude_list, arg, XFLG_FATAL_ERRORS + | (opt == OPT_INCLUDE_FROM + ? XFLG_DEF_INCLUDE : 0)); break; case 'h': @@ -561,15 +591,18 @@ break; case OPT_WRITE_BATCH: - /* popt stores the filename in batch_prefix for us */ + /* batch_name is already set */ write_batch = 1; - checksum_seed = FIXED_CHECKSUM_SEED; break; case OPT_READ_BATCH: - /* popt stores the filename in batch_prefix for us */ + /* batch_name is already set */ read_batch = 1; - checksum_seed = FIXED_CHECKSUM_SEED; + break; + + case OPT_TIMEOUT: + if (io_timeout && io_timeout < select_timeout) + select_timeout = io_timeout; break; case OPT_LINK_DEST: @@ -580,7 +613,6 @@ snprintf(err_buf, sizeof err_buf, "hard links are not supported on this %s\n", am_server ? "server" : "client"); - rprintf(FERROR, "ERROR: %s", err_buf); return 0; #endif @@ -591,7 +623,7 @@ struct poptOption *op = &long_options[opt-OPT_REFUSED_BASE]; int n = snprintf(err_buf, sizeof err_buf, - "This server does not support --%s\n", + "The server is configured to refuse --%s\n", op->longName) - 1; if (op->shortName) { snprintf(err_buf+n, sizeof err_buf-n, @@ -613,7 +645,6 @@ snprintf(err_buf, sizeof err_buf, "symlinks are not supported on this %s\n", am_server ? "server" : "client"); - rprintf(FERROR, "ERROR: %s", err_buf); return 0; } #endif @@ -623,32 +654,49 @@ snprintf(err_buf, sizeof err_buf, "hard links are not supported on this %s\n", am_server ? "server" : "client"); - rprintf(FERROR, "ERROR: %s", err_buf); return 0; } #endif if (write_batch && read_batch) { - rprintf(FERROR, - "write-batch and read-batch can not be used together\n"); - exit_cleanup(RERR_SYNTAX); - } - if (batch_prefix && strlen(batch_prefix) > MAX_BATCH_PREFIX_LEN) { - rprintf(FERROR, - "the batch-file prefix must be %d characters or less.\n", - MAX_BATCH_PREFIX_LEN); - exit_cleanup(RERR_SYNTAX); + snprintf(err_buf, sizeof err_buf, + "--write-batch and --read-batch can not be used together\n"); + return 0; } - - if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) { - rprintf(FERROR, "the --temp-dir path is WAY too long.\n"); - exit_cleanup(RERR_SYNTAX); + if (write_batch || read_batch) { + if (dry_run) { + snprintf(err_buf, sizeof err_buf, + "--%s-batch cannot be used with --dry_run (-n)\n", + write_batch ? "write" : "read"); + return 0; + } + if (am_server) { + rprintf(FINFO, + "ignoring --%s-batch option sent to server\n", + write_batch ? "write" : "read"); + /* We don't actually exit_cleanup(), so that we can + * still service older version clients that still send + * batch args to server. */ + read_batch = write_batch = 0; + batch_name = NULL; + } + } + if (read_batch && files_from) { + snprintf(err_buf, sizeof err_buf, + "--read-batch cannot be used with --files-from\n"); + return 0; + } + if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) { + snprintf(err_buf, sizeof err_buf, + "the batch-file name must be %d characters or less.\n", + MAX_BATCH_NAME_LEN); + return 0; } - if (do_compression && (write_batch || read_batch)) { - rprintf(FERROR, - "compress can not be used with write-batch or read-batch\n"); - exit_cleanup(RERR_SYNTAX); + if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) { + snprintf(err_buf, sizeof err_buf, + "the --temp-dir path is WAY too long.\n"); + return 0; } if (archive_mode) { @@ -676,15 +724,49 @@ if (sanitize_paths) { int i; for (i = *argc; i-- > 0; ) - (*argv)[i] = alloc_sanitize_path((*argv)[i], NULL); + (*argv)[i] = sanitize_path(NULL, (*argv)[i], "", 0); if (tmpdir) - tmpdir = alloc_sanitize_path(tmpdir, curr_dir); + tmpdir = sanitize_path(NULL, tmpdir, NULL, 0); + if (partial_dir) + partial_dir = sanitize_path(NULL, partial_dir, NULL, 0); if (compare_dest) - compare_dest = alloc_sanitize_path(compare_dest, curr_dir); + compare_dest = sanitize_path(NULL, compare_dest, NULL, 0); if (backup_dir) - backup_dir = alloc_sanitize_path(backup_dir, curr_dir); + backup_dir = sanitize_path(NULL, backup_dir, NULL, 0); if (files_from) - files_from = alloc_sanitize_path(files_from, curr_dir); + files_from = sanitize_path(NULL, files_from, NULL, 0); + } + if (server_exclude_list.head && !am_sender) { + struct exclude_list_struct *elp = &server_exclude_list; + if (tmpdir) { + clean_fname(tmpdir, 1); + if (check_exclude(elp, tmpdir, 1) < 0) + goto options_rejected; + } + if (partial_dir) { + clean_fname(partial_dir, 1); + if (check_exclude(elp, partial_dir, 1) < 0) + goto options_rejected; + } + if (compare_dest) { + clean_fname(compare_dest, 1); + if (check_exclude(elp, compare_dest, 1) < 0) + goto options_rejected; + } + if (backup_dir) { + clean_fname(backup_dir, 1); + if (check_exclude(elp, backup_dir, 1) < 0) + goto options_rejected; + } + } + if (server_exclude_list.head && files_from) { + clean_fname(files_from, 1); + if (check_exclude(&server_exclude_list, files_from, 0) < 0) { + options_rejected: + snprintf(err_buf, sizeof err_buf, + "Your options have been rejected by the server.\n"); + return 0; + } } if (daemon_opt) { @@ -697,16 +779,18 @@ backup_suffix = backup_dir ? "" : BACKUP_SUFFIX; backup_suffix_len = strlen(backup_suffix); if (strchr(backup_suffix, '/') != NULL) { - rprintf(FERROR, "--suffix cannot contain slashes: %s\n", + snprintf(err_buf, sizeof err_buf, + "--suffix cannot contain slashes: %s\n", backup_suffix); - exit_cleanup(RERR_SYNTAX); + return 0; } if (backup_dir) { backup_dir_len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf); backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len; if (backup_dir_remainder < 32) { - rprintf(FERROR, "the --backup-dir path is WAY too long.\n"); - exit_cleanup(RERR_SYNTAX); + snprintf(err_buf, sizeof err_buf, + "the --backup-dir path is WAY too long.\n"); + return 0; } if (backup_dir_buf[backup_dir_len - 1] != '/') { backup_dir_buf[backup_dir_len++] = '/'; @@ -715,17 +799,57 @@ if (verbose > 1 && !am_sender) rprintf(FINFO, "backup_dir is %s\n", backup_dir_buf); } else if (!backup_suffix_len && (!am_server || !am_sender)) { - rprintf(FERROR, + snprintf(err_buf, sizeof err_buf, "--suffix cannot be a null string without --backup-dir\n"); - exit_cleanup(RERR_SYNTAX); + return 0; } if (do_progress && !verbose) verbose = 1; + if (bwlimit) { + bwlimit_writemax = (size_t)bwlimit * 128; + if (bwlimit_writemax < 512) + bwlimit_writemax = 512; + } + + if (inplace) { +#if HAVE_FTRUNCATE + if (partial_dir) { + snprintf(err_buf, sizeof err_buf, + "--inplace cannot be used with --partial-dir\n"); + return 0; + } + keep_partial = 0; +#else + snprintf(err_buf, sizeof err_buf, + "--inplace is not supported on this %s\n", + am_server ? "server" : "client"); + return 0; +#endif + if (compare_dest) { + snprintf(err_buf, sizeof err_buf, + "--inplace does not yet work with %s\n", + link_dest ? "--link-dest" : "--compare-dest"); + return 0; + } + } else { + if (keep_partial && !partial_dir) + partial_dir = getenv("RSYNC_PARTIAL_DIR"); + if (partial_dir) { + if (!*partial_dir || strcmp(partial_dir, ".") == 0) + partial_dir = NULL; + else if (*partial_dir != '/') { + add_exclude(&exclude_list, partial_dir, + XFLG_DIRECTORY); + } + keep_partial = 1; + } + } + if (files_from) { char *colon; - if (*argc != 2 && !(am_server && am_sender && *argc == 1)) { + if (*argc > 2 || (!am_daemon && *argc == 1)) { usage(FERROR); exit_cleanup(RERR_SYNTAX); } @@ -741,16 +865,17 @@ } remote_filesfrom_file = colon+1 + (colon[1] == ':'); if (strcmp(remote_filesfrom_file, "-") == 0) { - rprintf(FERROR, "Invalid --files-from remote filename\n"); - exit_cleanup(RERR_SYNTAX); + snprintf(err_buf, sizeof err_buf, + "Invalid --files-from remote filename\n"); + return 0; } } else { filesfrom_fd = open(files_from, O_RDONLY|O_BINARY); if (filesfrom_fd < 0) { - rsyserr(FERROR, errno, - "failed to open files-from file %s", - files_from); - exit_cleanup(RERR_FILEIO); + snprintf(err_buf, sizeof err_buf, + "failed to open files-from file %s: %s\n", + files_from, strerror(errno)); + return 0; } } } @@ -806,6 +931,8 @@ argstr[x++] = 'l'; if (copy_links) argstr[x++] = 'L'; + if (keep_dirlinks && am_sender) + argstr[x++] = 'K'; if (whole_file > 0) argstr[x++] = 'W'; @@ -866,13 +993,6 @@ args[ac++] = arg; } - if (batch_prefix) { - char *r_or_w = write_batch ? "write" : "read"; - if (asprintf(&arg, "--%s-batch=%s", r_or_w, batch_prefix) < 0) - goto oom; - args[ac++] = arg; - } - if (io_timeout) { if (asprintf(&arg, "--timeout=%d", io_timeout) < 0) goto oom; @@ -898,10 +1018,18 @@ args[ac++] = arg; } - if (delete_excluded) - args[ac++] = "--delete-excluded"; - else if (delete_mode) - args[ac++] = "--delete"; + if (am_sender) { + if (delete_excluded) + args[ac++] = "--delete-excluded"; + else if (delete_mode) + args[ac++] = "--delete"; + + if (delete_after) + args[ac++] = "--delete-after"; + + if (force_delete) + args[ac++] = "--force"; + } if (size_only) args[ac++] = "--size-only"; @@ -912,14 +1040,17 @@ args[ac++] = arg; } - if (keep_partial) - args[ac++] = "--partial"; - - if (force_delete) - args[ac++] = "--force"; + if (checksum_seed) { + if (asprintf(&arg, "--checksum-seed=%d", checksum_seed) < 0) + goto oom; + args[ac++] = arg; + } - if (delete_after) - args[ac++] = "--delete-after"; + if (partial_dir && am_sender) { + args[ac++] = "--partial-dir"; + args[ac++] = partial_dir; + } else if (keep_partial) + args[ac++] = "--partial"; if (ignore_errors) args[ac++] = "--ignore-errors"; @@ -939,6 +1070,9 @@ if (opt_ignore_existing && am_sender) args[ac++] = "--ignore-existing"; + if (inplace) + args[ac++] = "--inplace"; + if (tmpdir) { args[ac++] = "--temp-dir"; args[ac++] = tmpdir; diff -urN --exclude=patches rsync-2.6.2/packaging/lsb/rsync.spec rsync-2.6.3/packaging/lsb/rsync.spec --- rsync-2.6.2/packaging/lsb/rsync.spec 2004-04-30 11:02:43.000000000 -0700 +++ rsync-2.6.3/packaging/lsb/rsync.spec 2004-09-30 09:35:56.000000000 -0700 @@ -1,6 +1,6 @@ Summary: Program for efficient remote updates of files. Name: rsync -Version: 2.6.2 +Version: 2.6.3 Release: 1 Copyright: GPL Group: Applications/Networking diff -urN --exclude=patches rsync-2.6.2/params.c rsync-2.6.3/params.c --- rsync-2.6.2/params.c 2003-12-06 13:07:27.000000000 -0800 +++ rsync-2.6.3/params.c 2004-05-15 12:31:10.000000000 -0700 @@ -491,8 +491,8 @@ OpenedFile = fopen( FileName, "r" ); if( NULL == OpenedFile ) { - rprintf(FERROR,"rsync: unable to open configuration file \"%s\": %s\n", - FileName, strerror(errno)); + rsyserr(FERROR, errno, "rsync: unable to open configuration file \"%s\"", + FileName); } return( OpenedFile ); diff -urN --exclude=patches rsync-2.6.2/pipe.c rsync-2.6.3/pipe.c --- rsync-2.6.2/pipe.c 2004-01-27 14:35:15.000000000 -0800 +++ rsync-2.6.3/pipe.c 2004-08-11 17:52:58.000000000 -0700 @@ -1,19 +1,19 @@ /* -*- c-file-style: "linux" -*- - * - * Copyright (C) 1996-2000 by Andrew Tridgell + * + * Copyright (C) 1996-2000 by Andrew Tridgell * Copyright (C) Paul Mackerras 1996 * Copyright (C) 2001, 2002 by Martin Pool - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -25,14 +25,14 @@ extern int am_server; extern int blocking_io; extern int orig_umask; -extern int read_batch; +extern int write_batch; extern int filesfrom_fd; /** * Create a child connected to use on stdin/stdout. * - * This is derived from CVS code - * + * This is derived from CVS code + * * Note that in the child STDIN is set to blocking and STDOUT * is set to non-blocking. This is necessary as rsh relies on stdin being blocking * and ssh relies on stdout being non-blocking @@ -46,20 +46,19 @@ pid_t pid; int to_child_pipe[2]; int from_child_pipe[2]; - + if (verbose >= 2) { print_child_argv(command); } if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) { - rprintf(FERROR, "pipe: %s\n", strerror(errno)); + rsyserr(FERROR, errno, "pipe"); exit_cleanup(RERR_IPC); } - pid = do_fork(); if (pid == -1) { - rprintf(FERROR, "fork: %s\n", strerror(errno)); + rsyserr(FERROR, errno, "fork"); exit_cleanup(RERR_IPC); } @@ -68,8 +67,7 @@ close(to_child_pipe[1]) < 0 || close(from_child_pipe[0]) < 0 || dup2(from_child_pipe[1], STDOUT_FILENO) < 0) { - rprintf(FERROR, "Failed to dup/close : %s\n", - strerror(errno)); + rsyserr(FERROR, errno, "Failed to dup/close"); exit_cleanup(RERR_IPC); } if (to_child_pipe[0] != STDIN_FILENO) @@ -81,13 +79,12 @@ if (blocking_io > 0) set_blocking(STDOUT_FILENO); execvp(command[0], command); - rprintf(FERROR, "Failed to exec %s : %s\n", - command[0], strerror(errno)); + rsyserr(FERROR, errno, "Failed to exec %s", command[0]); exit_cleanup(RERR_IPC); } if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) { - rprintf(FERROR, "Failed to close : %s\n", strerror(errno)); + rsyserr(FERROR, errno, "Failed to close"); exit_cleanup(RERR_IPC); } @@ -97,7 +94,17 @@ return pid; } -pid_t local_child(int argc, char **argv,int *f_in,int *f_out, +/* This function forks a child which calls child_main(). First, + * however, it has to establish communication paths to and from the + * newborn child. It creates two socket pairs -- one for writing to + * the child (from the parent) and one for reading from the child + * (writing to the parent). Since that's four socket ends, each + * process has to close the two ends it doesn't need. The remaining + * two socket ends are retained for reading and writing. In the + * child, the STDIN and STDOUT file descriptors refer to these + * sockets. In the parent, the function arguments f_in and f_out are + * set to refer to these sockets. */ +pid_t local_child(int argc, char **argv, int *f_in, int *f_out, int (*child_main)(int, char*[])) { pid_t pid; @@ -106,20 +113,23 @@ if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) { - rprintf(FERROR,"pipe: %s\n",strerror(errno)); + rsyserr(FERROR, errno, "pipe"); exit_cleanup(RERR_IPC); } - pid = do_fork(); if (pid == -1) { - rprintf(FERROR,"fork: %s\n",strerror(errno)); + rsyserr(FERROR, errno, "fork"); exit_cleanup(RERR_IPC); } if (pid == 0) { - am_sender = read_batch ? 0 : !am_sender; - am_server = 1; + am_sender = !am_sender; + am_server = 1; + + /* The server side never writes the batch, even if it + * is local (it makes the logic easier elsewhere). */ + write_batch = 0; if (!am_sender) filesfrom_fd = -1; @@ -128,11 +138,13 @@ close(to_child_pipe[1]) < 0 || close(from_child_pipe[0]) < 0 || dup2(from_child_pipe[1], STDOUT_FILENO) < 0) { - rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno)); + rsyserr(FERROR, errno, "Failed to dup/close"); exit_cleanup(RERR_IPC); } - if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]); - if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]); + if (to_child_pipe[0] != STDIN_FILENO) + close(to_child_pipe[0]); + if (from_child_pipe[1] != STDOUT_FILENO) + close(from_child_pipe[1]); child_main(argc, argv); } @@ -141,12 +153,12 @@ if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) { - rprintf(FERROR,"Failed to close : %s\n",strerror(errno)); + rsyserr(FERROR, errno, "Failed to close"); exit_cleanup(RERR_IPC); } *f_in = from_child_pipe[0]; *f_out = to_child_pipe[1]; - + return pid; } diff -urN --exclude=patches rsync-2.6.2/popt/popthelp.c rsync-2.6.3/popt/popthelp.c --- rsync-2.6.2/popt/popthelp.c 2004-01-27 08:27:05.000000000 -0800 +++ rsync-2.6.3/popt/popthelp.c 2004-06-09 14:41:21.000000000 -0700 @@ -63,7 +63,7 @@ /** * @param table option(s) */ -/*@observer@*/ /*@null@*/ static const char *const +/*@observer@*/ /*@null@*/ static const char * getTableTranslationDomain(/*@null@*/ const struct poptOption *table) /*@*/ { @@ -81,10 +81,10 @@ * @param opt option(s) * @param translation_domain translation domain */ -/*@observer@*/ /*@null@*/ static const char *const +/*@observer@*/ /*@null@*/ static const char * getArgDescrip(const struct poptOption * opt, /*@-paramuse@*/ /* FIX: wazzup? */ - /*@null@*/ UNUSED(const char * translation_domain)) + /*@null@*/ const char * translation_domain) /*@=paramuse@*/ /*@*/ { @@ -115,7 +115,7 @@ singleOptionDefaultValue(int lineLength, const struct poptOption * opt, /*@-paramuse@*/ /* FIX: i18n macros disable with lclint */ - /*@null@*/ UNUSED(const char * translation_domain)) + /*@null@*/ const char * translation_domain) /*@=paramuse@*/ /*@*/ { diff -urN --exclude=patches rsync-2.6.2/proto.h rsync-2.6.3/proto.h --- rsync-2.6.2/proto.h 2004-04-22 02:58:09.000000000 -0700 +++ rsync-2.6.3/proto.h 2004-09-21 02:15:56.000000000 -0700 @@ -4,32 +4,24 @@ void base64_encode(char *buf, int len, char *out); char *auth_server(int f_in, int f_out, int module, char *addr, char *leader); void auth_client(int fd, char *user, char *challenge); +char *get_backup_name(char *fname); int make_backup(char *fname); -void write_batch_flist_info(int flist_count, struct file_struct **files); -void write_batch_argvs_file(int argc, char *argv[]); -struct file_list *create_flist_from_batch(void); -void write_batch_csums_file(void *buff, int bytes_to_write); -void close_batch_csums_file(void); -void write_batch_csum_info(int *flist_entry, struct sum_struct *s); -int read_batch_csums_file(char *buff, int len); -void read_batch_csum_info(int flist_entry, struct sum_struct *s, - int *checksums_match); -void write_batch_delta_file(char *buff, int bytes_to_write); -void close_batch_delta_file(void); -int read_batch_delta_file(char *buff, int len); +void write_stream_flags(int fd); +void read_stream_flags(int fd); +void write_batch_shell_file(int argc, char *argv[], int file_arg_cnt); void show_flist(int index, struct file_struct **fptr); void show_argvs(int argc, char *argv[]); uint32 get_checksum1(char *buf1,int len); void get_checksum2(char *buf,int len,char *sum); void file_checksum(char *fname,char *sum,OFF_T size); -void sum_init(void); +void sum_init(int seed); void sum_update(char *p, int len); void sum_end(char *sum); void close_all(void); void _exit_cleanup(int code, const char *file, int line); void cleanup_disable(void); void cleanup_set(char *fnametmp, char *fname, struct file_struct *file, - struct map_struct *buf, int fd1, int fd2); + int fd_r, int fd_w); void cleanup_set_pid(pid_t pid); char *client_addr(int fd); char *client_name(int fd); @@ -46,12 +38,13 @@ const struct sockaddr_storage *ss, char *name_buf); int start_socket_client(char *host, char *path, int argc, char *argv[]); -int start_inband_exchange(char *user, char *path, int f_in, int f_out, int argc); +int start_inband_exchange(char *user, char *path, int f_in, int f_out, + int argc); int start_daemon(int f_in, int f_out); int daemon_main(void); void setup_protocol(int f_out,int f_in); int claim_connection(char *fname,int max_connections); -void free_exclude_list(struct exclude_list_struct *listp); +void clear_exclude_list(struct exclude_list_struct *listp); int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir); void add_exclude(struct exclude_list_struct *listp, const char *pattern, int xflags); @@ -63,19 +56,19 @@ int sparse_end(int f); int flush_write_file(int f); int write_file(int f,char *buf,size_t len); -struct map_struct *map_file(int fd,OFF_T len); +struct map_struct *map_file(int fd, OFF_T len, OFF_T map_size, + size_t block_size); char *map_ptr(struct map_struct *map,OFF_T offset,int len); int unmap_file(struct map_struct *map); void init_flist(void); void show_flist_stats(void); -int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf); -int link_stat(const char *path, STRUCT_STAT * buffer); +int link_stat(const char *path, STRUCT_STAT *buffer, int follow_dirlinks); void flist_expand(struct file_list *flist); void send_file_entry(struct file_struct *file, int f, unsigned short base_flags); void receive_file_entry(struct file_struct **fptr, unsigned short flags, - struct file_list *flist, int f); -struct file_struct *make_file(char *fname, - struct file_list *flist, int exclude_level); + struct file_list *flist, int f); +struct file_struct *make_file(char *fname, struct file_list *flist, + int exclude_level); void send_file_name(int f, struct file_list *flist, char *fname, int recursive, unsigned short base_flags); struct file_list *send_file_list(int f, int argc, char *argv[]); @@ -89,11 +82,11 @@ char *f_name_to(struct file_struct *f, char *fbuf); char *f_name(struct file_struct *f); void write_sum_head(int f, struct sum_struct *sum); -void recv_generator(char *fname, struct file_struct *file, int i, int f_out); -void generate_files(int f, struct file_list *flist, char *local_name); +void generate_files(int f_out, struct file_list *flist, char *local_name); void init_hard_links(struct file_list *flist); int hard_link_check(struct file_struct *file, int skip); void do_hard_links(void); +void io_set_sock_fds(int f_in, int f_out); void set_msg_fd_in(int fd); void set_msg_fd_out(int fd); void send_msg(enum msgcode code, char *buf, int len); @@ -101,26 +94,30 @@ int get_redo_num(void); void io_set_filesfrom_fds(int f_in, int f_out); int read_filesfrom_line(int fd, char *fname); +void io_start_buffering_out(void); +void io_start_buffering_in(void); +void io_end_buffering(void); int32 read_int(int f); int64 read_longint(int f); void read_buf(int f,char *buf,size_t len); void read_sbuf(int f,char *buf,size_t len); unsigned char read_byte(int f); -void io_start_buffering_out(int fd); -void io_start_buffering_in(int fd); void io_flush(int flush_it_all); -void io_end_buffering(void); void write_int(int f,int32 x); void write_int_named(int f, int32 x, const char *phase); void write_longint(int f, int64 x); void write_buf(int f,char *buf,size_t len); +void write_sbuf(int f, char *buf); void write_byte(int f,unsigned char c); int read_line(int f, char *buf, size_t maxlen); void io_printf(int fd, const char *format, ...); -void io_start_multiplex_out(int fd); -void io_start_multiplex_in(int fd); +void io_start_multiplex_out(void); +void io_start_multiplex_in(void); int io_multiplex_write(enum msgcode code, char *buf, size_t len); -void io_multiplexing_close(void); +void close_multiplexing_in(void); +void close_multiplexing_out(void); +void start_write_batch(int fd); +void stop_write_batch(void); char *lp_motd_file(void); char *lp_log_file(void); char *lp_pid_file(void); @@ -132,6 +129,7 @@ char *lp_path(int ); char *lp_lock_file(int ); BOOL lp_read_only(int ); +BOOL lp_write_only(int ); BOOL lp_list(int ); BOOL lp_use_chroot(int ); BOOL lp_transfer_logging(int ); @@ -183,18 +181,19 @@ BOOL (*sfunc)(char *), BOOL (*pfunc)(char *, char *) ); pid_t piped_child(char **command, int *f_in, int *f_out); -pid_t local_child(int argc, char **argv,int *f_in,int *f_out, +pid_t local_child(int argc, char **argv, int *f_in, int *f_out, int (*child_main)(int, char*[])); void end_progress(OFF_T size); void show_progress(OFF_T ofs, OFF_T size); void delete_files(struct file_list *flist); -int recv_files(int f_in,struct file_list *flist,char *local_name); +int recv_files(int f_in, struct file_list *flist, char *local_name); void free_sums(struct sum_struct *s); int delete_file(char *fname); int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st, - int report); + int flags); void sig_int(void); -void finish_transfer(char *fname, char *fnametmp, struct file_struct *file); +void finish_transfer(char *fname, char *fnametmp, struct file_struct *file, + int ok_to_set_time); const char *who_am_i(void); void read_sum_head(int f, struct sum_struct *sum); void send_files(struct file_list *flist, int f_out, int f_in); @@ -244,6 +243,7 @@ void overflow(char *str); int set_modtime(char *fname, time_t modtime); int create_directory_path(char *fname, int base_umask); +int full_write(int desc, char *ptr, size_t len); int copy_file(char *source, char *dest, mode_t mode); int robust_unlink(char *fname); int robust_rename(char *from, char *to, int mode); @@ -252,16 +252,19 @@ int name_to_uid(char *name, uid_t *uid); int name_to_gid(char *name, gid_t *gid); int lock_range(int fd, int offset, int len); -void glob_expand(char *base1, char **argv, int *argc, int maxargs); +void glob_expand(char *base1, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr); void strlower(char *s); size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2); size_t stringjoin(char *dest, size_t destsize, ...); -void clean_fname(char *name); -void sanitize_path(char *p, char *reldir); -char *alloc_sanitize_path(const char *path, const char *rootdir); +int count_dir_elements(const char *p); +unsigned int clean_fname(char *name, BOOL collapse_dot_dot); +char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth); int push_dir(char *dir); int pop_dir(char *dir); -char *full_fname(char *fn); +const char *safe_fname(const char *fname); +char *full_fname(const char *fn); +char *partial_dir_fname(const char *fname); +int handle_partial_dir(const char *fname, int create); int u_strcmp(const char *cs1, const char *cs2); int unsafe_symlink(const char *dest, const char *src); char *timestring(time_t t); diff -urN --exclude=patches rsync-2.6.2/receiver.c rsync-2.6.3/receiver.c --- rsync-2.6.2/receiver.c 2004-04-27 12:51:33.000000000 -0700 +++ rsync-2.6.3/receiver.c 2004-09-21 02:24:06.000000000 -0700 @@ -22,19 +22,22 @@ extern int verbose; extern int recurse; -extern int delete_mode; extern int delete_after; extern int max_delete; extern int csum_length; extern struct stats stats; extern int dry_run; +extern int read_batch; +extern int batch_gen_fd; extern int am_server; extern int relative_paths; +extern int keep_dirlinks; extern int preserve_hard_links; extern int preserve_perms; extern int cvs_exclude; extern int io_error; extern char *tmpdir; +extern char *partial_dir; extern char *compare_dest; extern int make_backups; extern int do_progress; @@ -45,24 +48,31 @@ extern int module_id; extern int ignore_errors; extern int orig_umask; +extern int keep_partial; +extern int checksum_seed; +extern int inplace; + +extern struct exclude_list_struct server_exclude_list; + static void delete_one(char *fn, int is_dir) { if (!is_dir) { if (robust_unlink(fn) != 0) { - rprintf(FERROR, "delete_one: unlink %s failed: %s\n", - full_fname(fn), strerror(errno)); - } else if (verbose) { - rprintf(FINFO, "deleting %s\n", fn); - } + rsyserr(FERROR, errno, "delete_one: unlink %s failed", + full_fname(fn)); + } else if (verbose) + rprintf(FINFO, "deleting %s\n", safe_fname(fn)); } else { if (do_rmdir(fn) != 0) { if (errno != ENOTEMPTY && errno != EEXIST) { - rprintf(FERROR, "delete_one: rmdir %s failed: %s\n", - full_fname(fn), strerror(errno)); + rsyserr(FERROR, errno, + "delete_one: rmdir %s failed", + full_fname(fn)); } } else if (verbose) { - rprintf(FINFO, "deleting directory %s\n", fn); + rprintf(FINFO, "deleting directory %s\n", + safe_fname(fn)); } } } @@ -103,7 +113,7 @@ continue; if (verbose > 1) - rprintf(FINFO, "deleting in %s\n", fbuf); + rprintf(FINFO, "deleting in %s\n", safe_fname(fbuf)); for (i = local_file_list->count-1; i >= 0; i--) { if (max_delete && deletion_count > max_delete) @@ -113,9 +123,11 @@ if (flist_find(flist,local_file_list->files[i]) < 0) { char *f = f_name(local_file_list->files[i]); if (make_backups && (backup_dir || !is_backup_file(f))) { - (void) make_backup(f); - if (verbose) - rprintf(FINFO, "deleting %s\n", f); + make_backup(f); + if (verbose) { + rprintf(FINFO, "deleting %s\n", + safe_fname(f)); + } } else { int mode = local_file_list->files[i]->mode; delete_one(f, S_ISDIR(mode) != 0); @@ -189,22 +201,33 @@ } -static int receive_data(int f_in,struct map_struct *mapbuf,int fd,char *fname, - OFF_T total_size) +static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r, + char *fname, int fd, OFF_T total_size) { - int i; + static char file_sum1[MD4_SUM_LENGTH]; + static char file_sum2[MD4_SUM_LENGTH]; + struct map_struct *mapbuf; struct sum_struct sum; unsigned int len; OFF_T offset = 0; OFF_T offset2; char *data; - static char file_sum1[MD4_SUM_LENGTH]; - static char file_sum2[MD4_SUM_LENGTH]; - char *map=NULL; + int i; + char *map = NULL; read_sum_head(f_in, &sum); - sum_init(); + if (fd_r >= 0 && size_r > 0) { + OFF_T map_size = MAX(sum.blength * 2, 16*1024); + mapbuf = map_file(fd_r, size_r, map_size, sum.blength); + if (verbose > 2) { + rprintf(FINFO, "recv mapped %s of size %.0f\n", + safe_fname(fname_r), (double)size_r); + } + } else + mapbuf = NULL; + + sum_init(checksum_seed); while ((i = recv_token(f_in, &data)) != 0) { if (do_progress) @@ -221,11 +244,8 @@ sum_update(data,i); - if (fd != -1 && write_file(fd,data,i) != i) { - rprintf(FERROR, "write failed on %s: %s\n", - full_fname(fname), strerror(errno)); - exit_cleanup(RERR_FILEIO); - } + if (fd != -1 && write_file(fd,data,i) != i) + goto report_write_error; offset += i; continue; } @@ -233,7 +253,7 @@ i = -(i+1); offset2 = i*(OFF_T)sum.blength; len = sum.blength; - if (i == (int) sum.count-1 && sum.remainder != 0) + if (i == (int)sum.count-1 && sum.remainder != 0) len = sum.remainder; stats.matched_data += len; @@ -249,61 +269,83 @@ sum_update(map,len); } - if (fd != -1 && write_file(fd,map,len) != (int) len) { - rprintf(FERROR, "write failed on %s: %s\n", - full_fname(fname), strerror(errno)); - exit_cleanup(RERR_FILEIO); + if (inplace) { + if (offset == offset2 && fd != -1) { + if (flush_write_file(fd) < 0) + goto report_write_error; + offset += len; + if (do_lseek(fd, len, SEEK_CUR) != offset) { + rsyserr(FERROR, errno, + "lseek failed on %s", + full_fname(fname)); + exit_cleanup(RERR_FILEIO); + } + continue; + } } + if (fd != -1 && write_file(fd, map, len) != (int)len) + goto report_write_error; offset += len; } flush_write_file(fd); +#ifdef HAVE_FTRUNCATE + if (inplace && fd != -1) + ftruncate(fd, offset); +#endif + if (do_progress) end_progress(total_size); if (fd != -1 && offset > 0 && sparse_end(fd) != 0) { - rprintf(FERROR, "write failed on %s: %s\n", - full_fname(fname), strerror(errno)); + report_write_error: + rsyserr(FERROR, errno, "write failed on %s", + full_fname(fname)); exit_cleanup(RERR_FILEIO); } sum_end(file_sum1); + if (mapbuf) + unmap_file(mapbuf); + read_buf(f_in,file_sum2,MD4_SUM_LENGTH); - if (verbose > 2) { + if (verbose > 2) rprintf(FINFO,"got file_sum\n"); - } - if (fd != -1 && memcmp(file_sum1,file_sum2,MD4_SUM_LENGTH) != 0) { + if (fd != -1 && memcmp(file_sum1, file_sum2, MD4_SUM_LENGTH) != 0) return 0; - } return 1; } +static void discard_receive_data(int f_in, OFF_T length) +{ + receive_data(f_in, NULL, -1, 0, NULL, -1, length); +} + + /** * main routine for receiver process. * * Receiver process runs on the same host as the generator process. */ -int recv_files(int f_in,struct file_list *flist,char *local_name) +int recv_files(int f_in, struct file_list *flist, char *local_name) { + int next_gen_i = -1; int fd1,fd2; STRUCT_STAT st; char *fname, fbuf[MAXPATHLEN]; char template[MAXPATHLEN]; char fnametmp[MAXPATHLEN]; - char *fnamecmp; + char *fnamecmp, *partialptr; char fnamecmpbuf[MAXPATHLEN]; - struct map_struct *mapbuf; - int i; struct file_struct *file; - int phase=0; - int recv_ok; struct stats initial_stats; + int save_make_backups = make_backups; + int i, recv_ok, phase = 0; - if (verbose > 2) { + if (verbose > 2) rprintf(FINFO,"recv_files(%d) starting\n",flist->count); - } if (flist->hlink_pool) { pool_destroy(flist->hlink_pool); @@ -315,15 +357,23 @@ i = read_int(f_in); if (i == -1) { - if (phase == 0) { - phase++; - csum_length = SUM_LENGTH; - if (verbose > 2) - rprintf(FINFO,"recv_files phase=%d\n",phase); - send_msg(MSG_DONE, "", 0); - continue; + if (read_batch) { + if (next_gen_i != flist->count) + while (read_int(batch_gen_fd) != -1) {} + next_gen_i = -1; } - break; + + if (phase) + break; + + phase = 1; + csum_length = SUM_LENGTH; + if (verbose > 2) + rprintf(FINFO, "recv_files phase=%d\n", phase); + send_msg(MSG_DONE, "", 0); + if (keep_partial) + make_backups = 0; /* prevents double backup */ + continue; } if (i < 0 || i >= flist->count) { @@ -345,22 +395,58 @@ fname = f_name_to(file, fbuf); if (dry_run) { - if (!am_server && verbose) { /* log transfer */ - rprintf(FINFO, "%s\n", fname); - } + if (!am_server && verbose) /* log the transfer */ + rprintf(FINFO, "%s\n", safe_fname(fname)); continue; } initial_stats = stats; if (verbose > 2) - rprintf(FINFO,"recv_files(%s)\n",fname); + rprintf(FINFO, "recv_files(%s)\n", safe_fname(fname)); - fnamecmp = fname; + if (read_batch) { + while (i > next_gen_i) { + next_gen_i = read_int(batch_gen_fd); + if (next_gen_i == -1) + next_gen_i = flist->count; + } + if (i < next_gen_i) { + rprintf(FINFO, "skipping update for \"%s\"\n", + safe_fname(fname)); + discard_receive_data(f_in, file->length); + continue; + } + } + + if (server_exclude_list.head + && check_exclude(&server_exclude_list, fname, + S_ISDIR(file->mode)) < 0) { + rprintf(FERROR, "attempt to hack rsync failed.\n"); + exit_cleanup(RERR_PROTOCOL); + } + + if (partial_dir) { + if ((partialptr = partial_dir_fname(fname)) != NULL) + fnamecmp = partialptr; + else + fnamecmp = fname; + } else + fnamecmp = partialptr = fname; + + if (inplace && make_backups) { + if (!(fnamecmp = get_backup_name(fname))) + fnamecmp = partialptr; + } /* open the file */ fd1 = do_open(fnamecmp, O_RDONLY, 0); + if (fd1 == -1 && fnamecmp != fname) { + fnamecmp = fname; + fd1 = do_open(fnamecmp, O_RDONLY, 0); + } + if (fd1 == -1 && compare_dest != NULL) { /* try the file at compare_dest instead */ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, @@ -370,9 +456,9 @@ } if (fd1 != -1 && do_fstat(fd1,&st) != 0) { - rprintf(FERROR, "fstat %s failed: %s\n", - full_fname(fnamecmp), strerror(errno)); - receive_data(f_in,NULL,-1,NULL,file->length); + rsyserr(FERROR, errno, "fstat %s failed", + full_fname(fnamecmp)); + discard_receive_data(f_in, file->length); close(fd1); continue; } @@ -385,7 +471,7 @@ */ rprintf(FERROR,"recv_files: %s is a directory\n", full_fname(fnamecmp)); - receive_data(f_in, NULL, -1, NULL, file->length); + discard_receive_data(f_in, file->length); close(fd1); continue; } @@ -393,7 +479,6 @@ if (fd1 != -1 && !S_ISREG(st.st_mode)) { close(fd1); fd1 = -1; - mapbuf = NULL; } if (fd1 != -1 && !preserve_perms) { @@ -403,90 +488,123 @@ file->mode = st.st_mode; } - if (fd1 != -1 && st.st_size > 0) { - mapbuf = map_file(fd1,st.st_size); - if (verbose > 2) - rprintf(FINFO,"recv mapped %s of size %.0f\n",fnamecmp,(double)st.st_size); - } else - mapbuf = NULL; - - if (!get_tmpname(fnametmp,fname)) { - if (mapbuf) unmap_file(mapbuf); - if (fd1 != -1) close(fd1); - continue; - } + /* We now check to see if we are writing file "inplace" */ + if (inplace) { + fd2 = do_open(fname, O_WRONLY|O_CREAT, 0); + if (fd2 == -1) { + rsyserr(FERROR, errno, "open %s failed", + full_fname(fname)); + discard_receive_data(f_in, file->length); + if (fd1 != -1) + close(fd1); + continue; + } + } else { + if (!get_tmpname(fnametmp,fname)) { + discard_receive_data(f_in, file->length); + if (fd1 != -1) + close(fd1); + continue; + } - strlcpy(template, fnametmp, sizeof template); + strlcpy(template, fnametmp, sizeof template); - /* 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. We also set it initially without group - * access because of a similar race condition. */ - fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); - - /* in most cases parent directories will already exist - * because their information should have been previously - * transferred, but that may not be the case with -R */ - if (fd2 == -1 && relative_paths && errno == ENOENT && - create_directory_path(fnametmp, orig_umask) == 0) { - strlcpy(fnametmp, template, sizeof fnametmp); + /* 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. We also set it initially without group + * access because of a similar race condition. */ fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); - } - if (fd2 == -1) { - rprintf(FERROR, "mkstemp %s failed: %s\n", - full_fname(fnametmp), strerror(errno)); - receive_data(f_in,mapbuf,-1,NULL,file->length); - if (mapbuf) unmap_file(mapbuf); - if (fd1 != -1) close(fd1); - continue; - } - cleanup_set(fnametmp, fname, file, mapbuf, fd1, fd2); + /* in most cases parent directories will already exist + * because their information should have been previously + * transferred, but that may not be the case with -R */ + if (fd2 == -1 && relative_paths && errno == ENOENT + && create_directory_path(fnametmp, orig_umask) == 0) { + strlcpy(fnametmp, template, sizeof fnametmp); + fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); + } + if (fd2 == -1) { + rsyserr(FERROR, errno, "mkstemp %s failed", + full_fname(fnametmp)); + discard_receive_data(f_in, file->length); + if (fd1 != -1) + close(fd1); + continue; + } - if (!am_server && verbose) { /* log transfer */ - rprintf(FINFO, "%s\n", fname); + if (partialptr) + cleanup_set(fnametmp, partialptr, file, fd1, fd2); } + if (!am_server && verbose) /* log the transfer */ + rprintf(FINFO, "%s\n", safe_fname(fname)); + /* recv file data */ - recv_ok = receive_data(f_in,mapbuf,fd2,fname,file->length); + recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, + fname, fd2, file->length); log_recv(file, &initial_stats); - if (mapbuf) unmap_file(mapbuf); - if (fd1 != -1) { + if (fd1 != -1) close(fd1); - } if (close(fd2) < 0) { - rprintf(FERROR, "close failed on %s: %s\n", - full_fname(fnametmp), strerror(errno)); + rsyserr(FERROR, errno, "close failed on %s", + full_fname(fnametmp)); exit_cleanup(RERR_FILEIO); } - if (verbose > 2) - rprintf(FINFO,"renaming %s to %s\n",fnametmp,fname); + if (recv_ok || inplace) + finish_transfer(fname, fnametmp, file, recv_ok); + else if (keep_partial && partialptr + && handle_partial_dir(partialptr, PDIR_CREATE)) + finish_transfer(partialptr, fnametmp, file, 0); + else { + partialptr = NULL; + do_unlink(fnametmp); + } - finish_transfer(fname, fnametmp, file); + if (partialptr != fname && fnamecmp == partialptr && recv_ok) { + do_unlink(partialptr); + handle_partial_dir(partialptr, PDIR_DELETE); + } cleanup_disable(); if (!recv_ok) { - if (csum_length == SUM_LENGTH) { - rprintf(FERROR,"ERROR: file corruption in %s. File changed during transfer?\n", - full_fname(fname)); - } else { + int msgtype = csum_length == SUM_LENGTH || read_batch ? + FERROR : FINFO; + if (msgtype == FERROR || verbose) { + char *errstr, *redostr, *keptstr; + if (!(keep_partial && partialptr) && !inplace) + keptstr = "discarded"; + else if (partial_dir) + keptstr = "put into partial-dir"; + else + keptstr = "retained"; + if (msgtype == FERROR) { + errstr = "ERROR"; + redostr = ""; + } else { + errstr = "WARNING"; + redostr = " (will try again)"; + } + rprintf(msgtype, + "%s: %s failed verification -- update %s%s.\n", + errstr, safe_fname(fname), + keptstr, redostr); + } + if (csum_length != SUM_LENGTH) { char buf[4]; - if (verbose > 1) - rprintf(FINFO,"redoing %s(%d)\n",fname,i); SIVAL(buf, 0, i); send_msg(MSG_REDO, buf, 4); } } } + make_backups = save_make_backups; - if (delete_after && recurse && delete_mode && !local_name - && flist->count > 0) + if (delete_after && recurse && !local_name && flist->count > 0) delete_files(flist); if (verbose > 2) diff -urN --exclude=patches rsync-2.6.2/rsync.1 rsync-2.6.3/rsync.1 --- rsync-2.6.2/rsync.1 2004-04-30 11:02:48.000000000 -0700 +++ rsync-2.6.3/rsync.1 2004-09-30 09:36:01.000000000 -0700 @@ -1,4 +1,4 @@ -.TH "rsync" "1" "30 Apr 2004" "" "" +.TH "rsync" "1" "30 Sep 2004" "" "" .SH "NAME" rsync \- faster, flexible replacement for rcp .SH "SYNOPSIS" @@ -21,8 +21,8 @@ .PP rsync is a program that behaves in much the same way that rcp does, but has many more options and uses the rsync remote-update protocol to -greatly speed up file transfers when the destination file already -exists\&. +greatly speed up file transfers when the destination file is being +updated\&. .PP The rsync remote-update protocol allows rsync to transfer just the differences between two sets of files across the network connection, using @@ -32,7 +32,7 @@ Some of the additional features of rsync are: .PP .IP o -support for copying links, devices, owners, groups and permissions +support for copying links, devices, owners, groups, and permissions .IP o exclude and exclude-from options similar to GNU tar .IP o @@ -125,7 +125,7 @@ Perhaps the best way to explain the syntax is with some examples: .PP .RS -rsync *\&.c foo:src/ +rsync -t *\&.c foo:src/ .RE .PP This would transfer all files matching the pattern *\&.c from the @@ -159,10 +159,10 @@ /dest/foo: .PP .RS -rsync -avz /src/foo /dest +rsync -av /src/foo /dest .RE .RS -rsync -avz /src/foo/ /dest/foo +rsync -av /src/foo/ /dest/foo .RE .PP You can also use rsync in local-only mode, where both the source and @@ -176,6 +176,42 @@ This would list all the anonymous rsync modules available on the host somehost\&.mydomain\&.com\&. (See the following section for more details\&.) .PP +.SH "ADVANCED USAGE" +.PP +The syntax for requesting multiple files from a remote host involves using +quoted spaces in the SRC\&. Some examples: +.PP +.RS +rsync host::\&'modname/dir1/file1 modname/dir2/file2\&' /dest +.RE +.PP +This would copy file1 and file2 into /dest from an rsync daemon\&. Each +additional arg must include the same "modname/" prefix as the first one, +and must be preceded by a single space\&. All other spaces are assumed +to be a part of the filenames\&. +.PP +.RS +rsync -av host:\&'dir1/file1 dir2/file2\&' /dest +.RE +.PP +This would copy file1 and file2 into /dest using a remote shell\&. This +word-splitting is done by the remote shell, so if it doesn\&'t work it means +that the remote shell isn\&'t configured to split its args based on +whitespace (a very rare setting, but not unknown)\&. If you need to transfer +a filename that contains whitespace, you\&'ll need to either escape the +whitespace in a way that the remote shell will understand, or use wildcards +in place of the spaces\&. Two examples of this are: +.PP +.RS +rsync -av host:\&'file\e name\e with\e spaces\&' /dest +.RE +.RS +rsync -av host:file?name?with?spaces /dest +.RE +.PP +This latter example assumes that your shell passes through unmatched +wildcards\&. If it complains about "no match", put the name in quotes\&. +.PP .SH "CONNECTING TO AN RSYNC SERVER" .PP It is also possible to use rsync without a remote shell as the @@ -326,6 +362,8 @@ --backup-dir make backups into this directory --suffix=SUFFIX backup suffix (default ~ w/o --backup-dir) -u, --update update only (don\&'t overwrite newer files) + --inplace update the destination files inplace + -K, --keep-dirlinks treat symlinked dir on receiver as dir -l, --links copy symlinks as symlinks -L, --copy-links copy the referent of all symlinks --copy-unsafe-links copy the referent of "unsafe" symlinks @@ -341,7 +379,7 @@ -W, --whole-file copy whole files, no incremental checks --no-whole-file turn off --whole-file -x, --one-file-system don\&'t cross filesystem boundaries - -B, --block-size=SIZE checksum blocking size (default 700) + -B, --block-size=SIZE force a fixed checksum block-size -e, --rsh=COMMAND specify the remote shell --rsync-path=PATH specify path to rsync on the remote machine --existing only update files that already exist @@ -352,6 +390,7 @@ --ignore-errors delete even if there are I/O errors --max-delete=NUM don\&'t delete more than NUM files --partial keep partially transferred files + --partial-dir=DIR put a partially transferred file into DIR --force force deletion of dirs even if not empty --numeric-ids don\&'t map uid/gid values by user/group name --timeout=TIME set I/O timeout in seconds @@ -383,8 +422,11 @@ --log-format=FORMAT log file transfers using specified format --password-file=FILE get password from FILE --bwlimit=KBPS limit I/O bandwidth, KBytes per second - --write-batch=PREFIX write batch fileset starting with PREFIX - --read-batch=PREFIX read batch fileset starting with PREFIX + --write-batch=FILE write a batch to FILE + --read-batch=FILE read a batch from FILE + --checksum-seed=NUM set block/file checksum seed + -4 --ipv4 prefer IPv4 + -6 --ipv6 prefer IPv6 -h, --help show this help screen @@ -544,13 +586,46 @@ regardless of the timestamps\&. This might change in the future (feel free to comment on this on the mailing list if you have an opinion)\&. .IP +.IP "\fB-K, --keep-dirlinks\fP" +On the receiving side, if a symlink is +pointing to a directory, it will be treated as matching a directory +from the sender\&. +.IP +.IP "\fB--inplace\fP" +This causes rsync not to create a new copy of the file +and then move it into place\&. Instead rsync will overwrite the existing +file, meaning that the rsync algorithm can\&'t extract the full amount of +network reduction it might otherwise (since it does not yet try to sort +data matches -- a future version may improve this)\&. +.IP +This option is useful for transfer of large files with block-based changes +or appended data, and also on systems that are disk bound, not network +bound\&. +.IP +The option implies --partial (since an interrupted transfer does not delete +the file), but conflicts with --partial-dir, --compare-dest, and +--link-dest (a future rsync version will hopefully update the protocol to +remove these restrictions)\&. +.IP +WARNING: The file\&'s data will be in an inconsistent state during the +transfer (and possibly afterward if the transfer gets interrupted), so you +should not use this option to update files that are in use\&. Also note that +rsync will be unable to update a file inplace that is not writable by the +receiving user\&. +.IP .IP "\fB-l, --links\fP" When symlinks are encountered, recreate the symlink on the destination\&. .IP .IP "\fB-L, --copy-links\fP" When symlinks are encountered, the file that -they point to (the referent) is copied, rather than the symlink\&. +they point to (the referent) is copied, rather than the symlink\&. In older +versions of rsync, this option also had the side-effect of telling the +receiving side to follow symlinks, such as symlinks to directories\&. In a +modern rsync such as this one, you\&'ll need to specify --keep-dirlinks (-K) +to get this extra behavior\&. The only exception is when sending files to +an rsync that is too old to understand -K -- in that case, the -L option +will still have the side-effect of -K on that older receiving rsync\&. .IP .IP "\fB--copy-unsafe-links\fP" This tells rsync to copy the referent of @@ -578,9 +653,9 @@ With this option the incremental rsync algorithm is not used and the whole file is sent as-is instead\&. The transfer may be faster if this option is used when the bandwidth between the source and -target machines is higher than the bandwidth to disk (especially when the +destination machines is higher than the bandwidth to disk (especially when the "disk" is actually a networked filesystem)\&. This is the default when both -the source and target are on the local machine\&. +the source and destination are specified as local paths\&. .IP .IP "\fB--no-whole-file\fP" Turn off --whole-file, for use when it is the @@ -620,9 +695,9 @@ with the files and update them on the remote system\&. Note that if this option is not used, the optimization that excludes files that have not been modified cannot be effective; in other words, a missing -t or -a will -cause the next transfer to behave as if it used -I, and all files will have -their checksums compared and show up in log messages even if they haven\&'t -changed\&. +cause the next transfer to behave as if it used -I, causing all files to be +updated (though the rsync algorithm will make the update fairly efficient +if the files haven\&'t actually changed, you\&'re much better off using -t)\&. .IP .IP "\fB-n, --dry-run\fP" This tells rsync to not do any file transfers, @@ -693,9 +768,10 @@ is only relevant without --delete because deletions are now done depth-first\&. Requires the --recursive option (which is implied by -a) to have any effect\&. .IP -.IP "\fB-B , --block-size=BLOCKSIZE\fP" -This controls the block size used in -the rsync algorithm\&. See the technical report for details\&. +.IP "\fB-B, --block-size=BLOCKSIZE\fP" +This forces the block size used in +the rsync algorithm to a fixed value\&. It is normally selected based on +the size of each file being updated\&. See the technical report for details\&. .IP .IP "\fB-e, --rsh=COMMAND\fP" This option allows you to choose an alternative @@ -780,12 +856,12 @@ .IP "\fB--include-from=FILE\fP" This specifies a list of include patterns from a file\&. -If \fIFILE\fP is \fB-\fP the list will be read from standard input\&. +If \fIFILE\fP is "-" the list will be read from standard input\&. .IP .IP "\fB--files-from=FILE\fP" Using this option allows you to specify the exact list of files to transfer (as read from the specified FILE or "-" -for stdin)\&. It also tweaks the default behavior of rsync to make +for standard input)\&. It also tweaks the default behavior of rsync to make transferring just the specified files and directories easier\&. For instance, the --relative option is enabled by default when this option is used (use --no-relative if you want to turn that off), all @@ -849,15 +925,13 @@ This option increases the usefulness of --partial because partially transferred files will remain in the new temporary destination until they have a chance to be completed\&. If DIR is a relative path, it is relative -to the destination directory (which changes in a recursive transfer)\&. +to the destination directory\&. .IP .IP "\fB--link-dest=DIR\fP" This option behaves like \fB--compare-dest\fP but also will create hard links from \fIDIR\fP to the destination directory for unchanged files\&. Files with changed ownership or permissions will not be linked\&. -Like \fB--compare-dest\fP if DIR is a relative path, it is relative -to the destination directory (which changes in a recursive transfer)\&. An example: .IP @@ -870,6 +944,13 @@ .IP +Like \fB--compare-dest\fP if DIR is a relative path, it is relative to the +destination directory\&. +Note that rsync versions prior to 2\&.6\&.1 had a bug that could prevent +--link-dest from working properly for a non-root user when -o was specified +(or implied by -a)\&. If the receiving rsync is not new enough, you can work +around this bug by avoiding the -o option\&. +.IP .IP "\fB-z, --compress\fP" With this option, rsync compresses any data from the files that it sends to the destination machine\&. This @@ -972,6 +1053,47 @@ --partial option tells rsync to keep the partial file which should make a subsequent transfer of the rest of the file much faster\&. .IP +.IP "\fB--partial-dir=DIR\fP" +Turns on --partial mode, but tells rsync to +put a partially transferred file into DIR instead of writing out the +file to the destination dir\&. Rsync will also use a file found in this +dir as data to speed up the transfer (i\&.e\&. when you redo the send after +rsync creates a partial file) and delete such a file after it has served +its purpose\&. Note that if --whole-file is specified (or implied) that an +existing partial-dir file will not be used to speedup the transfer (since +rsync is sending files without using the incremental rsync algorithm)\&. +.IP +Rsync will create the dir if it is missing (just the last dir -- not the +whole path)\&. This makes it easy to use a relative path (such as +"--partial-dir=\&.rsync-partial") to have rsync create the partial-directory +in the destination file\&'s directory (rsync will also try to remove the DIR +if a partial file was found to exist at the start of the transfer and the +DIR was specified as a relative path)\&. +.IP +If the partial-dir value is not an absolute path, rsync will also add an +--exclude of this value at the end of all your existing excludes\&. This +will prevent partial-dir files from being transferred and also prevent the +untimely deletion of partial-dir items on the receiving side\&. An example: +the above --partial-dir option would add an "--exclude=\&.rsync-partial/" +rule at the end of any other include/exclude rules\&. Note that if you are +supplying your own include/exclude rules, you may need to manually insert a +rule for this directory exclusion somewhere higher up in the list so that +it has a high enough priority to be effective (e\&.g\&., if your rules specify +a trailing --exclude=* rule, the auto-added rule will be ineffective)\&. +.IP +IMPORTANT: the --partial-dir should not be writable by other users or it +is a security risk\&. E\&.g\&. AVOID "/tmp"\&. +.IP +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 +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)\&. +.IP .IP "\fB--progress\fP" This option tells rsync to print information showing the progress of the transfer\&. This gives a bored user @@ -1014,9 +1136,9 @@ what percent of the total number of files has been scanned\&. .IP .IP "\fB-P\fP" -The -P option is equivalent to --partial --progress\&. I -found myself typing that combination quite often so I created an -option to make it easier\&. +The -P option is equivalent to --partial --progress\&. Its +purpose is to make it much easier to specify these two options for a long +transfer that may be interrupted\&. .IP .IP "\fB--password-file\fP" This option allows you to provide a password @@ -1035,15 +1157,37 @@ result is an average transfer rate equaling the specified limit\&. A value of zero specifies no limit\&. .IP -.IP "\fB--write-batch=PREFIX\fP" -Generate a set of files that can be -transferred as a batch update\&. Each filename in the set starts with -PREFIX\&. See the "BATCH MODE" section for details\&. -.IP -.IP "\fB--read-batch=PREFIX\fP" -Apply a previously generated change batch, -using the fileset whose filenames start with PREFIX\&. See the "BATCH -MODE" section for details\&. +.IP "\fB--write-batch=FILE\fP" +Record a file that can later be applied to +another identical destination with --read-batch\&. See the "BATCH MODE" +section for details\&. +.IP +.IP "\fB--read-batch=FILE\fP" +Apply all of the changes stored in FILE, a +file previously generated by --write-batch\&. +If \fIFILE\fP is "-" the batch data will be read from standard input\&. +See the "BATCH MODE" section for details\&. +.IP +.IP "\fB-4, --ipv4\fP or \fB-6, --ipv6\fP" +Tells rsync to prefer IPv4/IPv6 +when creating sockets\&. This only affects sockets that rsync has direct +control over, such as the outgoing socket when directly contacting an +rsync daemon, or the incoming sockets that an rsync daemon uses to +listen for connections\&. One of these options may be required in older +versions of Linux to work around an IPv6 bug in the kernel (if you see +an "address already in use" error when nothing else is using the port, +try specifying --ipv6 or --ipv4 when starting the daemon)\&. +.IP +.IP "\fB--checksum-seed=NUM\fP" +Set the MD4 checksum seed to the integer +NUM\&. This 4 byte checksum seed is included in each block and file +MD4 checksum calculation\&. By default the checksum seed is generated +by the server and defaults to the current time()\&. This option +is used to set a specific checksum seed, which is useful for +applications that want repeatable block and file checksums, or +in the case where the user wants a more random checksum seed\&. +Note that setting NUM to 0 causes rsync to use the default of time() +for checksum seed\&. .IP .PP .SH "EXCLUDE PATTERNS" @@ -1243,7 +1387,8 @@ .SH "BATCH MODE" .PP \fBNote:\fP Batch mode should be considered experimental in this version -of rsync\&. The interface or behavior may change before it stabilizes\&. +of rsync\&. The interface and behavior have now stabilized, though, so +feel free to try this out\&. .PP Batch mode can be used to apply the same set of updates to many identical systems\&. Suppose one has a tree which is replicated on a @@ -1252,84 +1397,121 @@ hosts\&. In order to do this using batch mode, rsync is run with the write-batch option to apply the changes made to the source tree to one of the destination trees\&. The write-batch option causes the rsync -client to store the information needed to repeat this operation against -other destination trees in a batch update fileset (see below)\&. The -filename of each file in the fileset starts with a prefix specified by -the user as an argument to the write-batch option\&. This fileset is -then copied to each remote host, where rsync is run with the read-batch -option, again specifying the same prefix, and the destination tree\&. -Rsync updates the destination tree using the information stored in the -batch update fileset\&. +client to store in a "batch file" all the information needed to repeat +this operation against other, identical destination trees\&. .PP -The fileset consists of 4 files: +To apply the recorded changes to another destination tree, run rsync +with the read-batch option, specifying the name of the same batch +file, and the destination tree\&. Rsync updates the destination tree +using the information stored in the batch file\&. +.PP +For convenience, one additional file is creating when the write-batch +option is used\&. This file\&'s name is created by appending +"\&.sh" to the batch filename\&. The \&.sh file contains +a command-line suitable for updating a destination tree using that +batch file\&. It can be executed using a Bourne(-like) shell, optionally +passing in an alternate destination tree pathname which is then used +instead of the original path\&. This is useful when the destination tree +path differs from the original destination tree path\&. .PP -.IP o -\fB\&.rsync_argvs\fP command-line arguments -.IP o -\fB\&.rsync_flist\fP rsync internal file metadata -.IP o -\fB\&.rsync_csums\fP rsync checksums -.IP o -\fB\&.rsync_delta\fP data blocks for file update & change -.PP -The \&.rsync_argvs file contains a command-line suitable for updating a -destination tree using that batch update fileset\&. It can be executed -using a Bourne(-like) shell, optionally passing in an alternate -destination tree pathname which is then used instead of the original -path\&. This is useful when the destination tree path differs from the -original destination tree path\&. -.PP -Generating the batch update fileset once saves having to perform the -file status, checksum and data block generation more than once when +Generating the batch file once saves having to perform the file +status, checksum, and data block generation more than once when updating multiple destination trees\&. Multicast transport protocols can -be used to transfer the batch update files in parallel to many hosts at -once, instead of sending the same data to every host individually\&. +be used to transfer the batch update files in parallel to many hosts +at once, instead of sending the same data to every host individually\&. .PP -Example: +Examples: +.PP + +.nf + + + $ rsync --write-batch=foo -a host:/source/dir/ /adest/dir/ + $ scp foo* remote: + $ ssh remote \&./foo\&.sh /bdest/dir/ + +.fi + + .PP .nf - $ rsync --write-batch=pfx -a /source/dir/ /adest/dir/ - $ rcp pfx\&.rsync_* remote: - $ ssh remote rsync --read-batch=pfx -a /bdest/dir/ - # or alternatively - $ ssh remote \&./pfx\&.rsync_argvs /bdest/dir/ + $ rsync --write-batch=foo -a /source/dir/ /adest/dir/ + $ ssh remote rsync --read-batch=- -a /bdest/dir/ 0) - rprintf(FINFO, "deleting %s\n", buf); + rprintf(FINFO, "deleting %s\n", safe_fname(buf)); if (delete_file(buf) != 0) { closedir(d); return -1; } } if (errno) { - rprintf(FERROR, "delete_file: readdir %s failed: %s\n", - full_fname(fname), strerror(errno)); + rsyserr(FERROR, errno, "delete_file: readdir %s failed", + full_fname(fname)); closedir(d); return -1; } @@ -114,8 +115,8 @@ closedir(d); if (do_rmdir(fname) != 0) { - rprintf(FERROR, "delete_file: rmdir %s failed: %s\n", - full_fname(fname), strerror(errno)); + rsyserr(FERROR, errno, "delete_file: rmdir %s failed", + full_fname(fname)); return -1; } @@ -123,31 +124,35 @@ } int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st, - int report) + int flags) { int updated = 0; STRUCT_STAT st2; int change_uid, change_gid; - if (dry_run) return 0; + if (dry_run) + return 0; if (!st) { - if (link_stat(fname,&st2) != 0) { - rprintf(FERROR, "stat %s failed: %s\n", - full_fname(fname), strerror(errno)); + if (link_stat(fname, &st2, 0) < 0) { + rsyserr(FERROR, errno, "stat %s failed", + full_fname(fname)); return 0; } st = &st2; } - if (preserve_times && !S_ISLNK(st->st_mode) && - cmp_modtime(st->st_mtime, file->modtime) != 0) { + if (!preserve_times || S_ISLNK(st->st_mode) + || (make_backups && !backup_dir && S_ISDIR(st->st_mode))) + flags |= PERMS_SKIP_MTIME; + if (!(flags & PERMS_SKIP_MTIME) + && cmp_modtime(st->st_mtime, 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", - full_fname(fname), strerror(errno)); + rsyserr(FERROR, errno, "failed to set times on %s", + full_fname(fname)); return 0; } updated = 1; @@ -174,16 +179,17 @@ change_gid ? file->gid : st->st_gid) != 0) { /* shouldn't have attempted to change uid or gid * unless have the privilege */ - rprintf(FERROR, "%s %s failed: %s\n", + rsyserr(FERROR, errno, "%s %s failed", change_uid ? "chown" : "chgrp", - full_fname(fname), strerror(errno)); + full_fname(fname)); 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); + link_stat(fname, st, + keep_dirlinks && S_ISDIR(st->st_mode)); } updated = 1; } @@ -193,15 +199,15 @@ if ((st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) { updated = 1; if (do_chmod(fname,(file->mode & CHMOD_BITS)) != 0) { - rprintf(FERROR, "failed to set permissions on %s: %s\n", - full_fname(fname), strerror(errno)); + rsyserr(FERROR, errno, "failed to set permissions on %s", + full_fname(fname)); return 0; } } } #endif - if (verbose > 1 && report) { + if (verbose > 1 && flags & PERMS_REPORT) { if (updated) rprintf(FINFO,"%s\n",fname); else @@ -228,26 +234,43 @@ /* finish off a file transfer, renaming the file and setting the permissions and ownership */ -void finish_transfer(char *fname, char *fnametmp, struct file_struct *file) +void finish_transfer(char *fname, char *fnametmp, struct file_struct *file, + int ok_to_set_time) { int ret; + if (inplace) { + if (verbose > 2) + rprintf(FINFO, "finishing %s\n", fname); + goto do_set_perms; + } + if (make_backups && !make_backup(fname)) return; + /* Change permissions before putting the file into place. */ + set_perms(fnametmp, file, NULL, ok_to_set_time ? 0 : PERMS_SKIP_MTIME); + /* move tmp file over real file */ + if (verbose > 2) + rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname); ret = robust_rename(fnametmp, fname, file->mode & INITACCESSPERMS); if (ret < 0) { - rprintf(FERROR, "%s %s -> \"%s\": %s\n", + rsyserr(FERROR, errno, "%s %s -> \"%s\"", ret == -2 ? "copy" : "rename", - full_fname(fnametmp), fname, strerror(errno)); + full_fname(fnametmp), fname); do_unlink(fnametmp); - } else { - set_perms(fname,file,NULL,0); + return; + } + if (ret == 0) { + /* The file was moved into place (not copied), so it's done. */ + return; } + do_set_perms: + set_perms(fname, file, NULL, ok_to_set_time ? 0 : PERMS_SKIP_MTIME); } const char *who_am_i(void) { - return am_sender ? "sender" : am_generator ? "generator" : "receiver"; + return am_sender ? "sender" : am_generator ? "generator" : "receiver"; } diff -urN --exclude=patches rsync-2.6.2/rsync.h rsync-2.6.3/rsync.h --- rsync-2.6.2/rsync.h 2004-04-26 18:36:16.000000000 -0700 +++ rsync-2.6.3/rsync.h 2004-09-21 21:10:10.000000000 -0700 @@ -108,10 +108,17 @@ #define XFLG_DEF_INCLUDE (1<<1) #define XFLG_WORDS_ONLY (1<<2) #define XFLG_WORD_SPLIT (1<<3) +#define XFLG_DIRECTORY (1<<4) + +#define PERMS_REPORT (1<<0) +#define PERMS_SKIP_MTIME (1<<1) #define FULL_FLUSH 1 #define NORMAL_FLUSH 0 +#define PDIR_CREATE 1 +#define PDIR_DELETE 0 + /* Log-message categories. FLOG is only used on the daemon side to * output messages to the log file. */ @@ -120,10 +127,10 @@ /* Messages types that are sent over the message channel. The logcode * values must all be present here with identical numbers. */ enum msgcode { - MSG_DATA=0, /* raw data on the multiplexed stream */ - MSG_ERROR=FERROR, MSG_INFO=FINFO, MSG_LOG=FLOG, /* remote logging */ - MSG_REDO=4, /* reprocess indicated flist index */ MSG_DONE=5, /* current phase is done */ + MSG_REDO=4, /* reprocess indicated flist index */ + MSG_ERROR=FERROR, MSG_INFO=FINFO, MSG_LOG=FLOG, /* remote logging */ + MSG_DATA=0 /* raw data on the multiplexed stream */ }; #include "errcode.h" @@ -309,7 +316,7 @@ #else /* As long as it gets... */ #define int64 off_t -#define NO_INT64 +#define INT64_IS_OFF_T #endif #if (SIZEOF_LONG == 8) @@ -388,7 +395,7 @@ #define IN_LOOPBACKNET 127 #endif -#define GID_NONE (gid_t) -1 +#define GID_NONE ((gid_t)-1) #define HL_CHECK_MASTER 0 #define HL_SKIP 1 @@ -455,11 +462,13 @@ struct file_struct **files; }; +#define SUMFLG_SAME_OFFSET (1<<0) + struct sum_buf { OFF_T offset; /**< offset in file of this chunk */ unsigned int len; /**< length of chunk of file */ - int i; /**< index of this chunk */ uint32 sum1; /**< simple checksum */ + short flags; /**< flag bits */ char sum2[SUM_LENGTH]; /**< checksum */ }; @@ -475,11 +484,9 @@ struct map_struct { char *p; /* Window pointer */ int fd; /* File Descriptor */ - int p_size; /* Window size at allocation */ - int p_len; /* Window size after fill */ - /* p_size and p_len could be - * consolodated by using a local - * variable in map_ptr() */ + int p_size; /* Largest window size we allocated */ + int p_len; /* Latest (rounded) window size */ + int def_window_size; /* Default window size */ int status; /* first errno from read errors */ OFF_T file_size; /* File size (from stat) */ OFF_T p_offset; /* Window start */ @@ -490,12 +497,13 @@ #define MATCHFLG_WILD2 (1<<1) /* pattern has '**' */ #define MATCHFLG_WILD2_PREFIX (1<<2) /* pattern starts with '**' */ #define MATCHFLG_ABS_PATH (1<<3) /* path-match on absolute path */ +#define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */ +#define MATCHFLG_DIRECTORY (1<<5) /* this matches only directories */ +#define MATCHFLG_CLEAR_LIST (1<<6) /* this item is the "!" token */ struct exclude_struct { struct exclude_struct *next; char *pattern; - int match_flags; - int include; - int directory; + unsigned int match_flags; int slash_cnt; }; diff -urN --exclude=patches rsync-2.6.2/rsync.yo rsync-2.6.3/rsync.yo --- rsync-2.6.2/rsync.yo 2004-04-30 11:02:43.000000000 -0700 +++ rsync-2.6.3/rsync.yo 2004-09-30 09:35:56.000000000 -0700 @@ -1,5 +1,5 @@ mailto(rsync-bugs@samba.org) -manpage(rsync)(1)(30 Apr 2004)()() +manpage(rsync)(1)(30 Sep 2004)()() manpagename(rsync)(faster, flexible replacement for rcp) manpagesynopsis() @@ -21,8 +21,8 @@ rsync is a program that behaves in much the same way that rcp does, but has many more options and uses the rsync remote-update protocol to -greatly speed up file transfers when the destination file already -exists. +greatly speed up file transfers when the destination file is being +updated. The rsync remote-update protocol allows rsync to transfer just the differences between two sets of files across the network connection, using @@ -32,7 +32,7 @@ Some of the additional features of rsync are: itemize( - it() support for copying links, devices, owners, groups and permissions + it() support for copying links, devices, owners, groups, and permissions it() exclude and exclude-from options similar to GNU tar it() a CVS exclude mode for ignoring the same files that CVS would ignore it() can use any transparent remote shell, including ssh or rsh @@ -113,7 +113,7 @@ Perhaps the best way to explain the syntax is with some examples: -quote(rsync *.c foo:src/) +quote(rsync -t *.c foo:src/) This would transfer all files matching the pattern *.c from the current directory to the directory src on the machine foo. If any of @@ -141,8 +141,8 @@ files in the same way, including their setting of the attributes of /dest/foo: -quote(rsync -avz /src/foo /dest) -quote(rsync -avz /src/foo/ /dest/foo) +quote(rsync -av /src/foo /dest) +quote(rsync -av /src/foo/ /dest/foo) You can also use rsync in local-only mode, where both the source and destination don't have a ':' in the name. In this case it behaves like @@ -154,6 +154,35 @@ somehost.mydomain.com. (See the following section for more details.) +manpagesection(ADVANCED USAGE) + +The syntax for requesting multiple files from a remote host involves using +quoted spaces in the SRC. Some examples: + +quote(rsync host::'modname/dir1/file1 modname/dir2/file2' /dest) + +This would copy file1 and file2 into /dest from an rsync daemon. Each +additional arg must include the same "modname/" prefix as the first one, +and must be preceded by a single space. All other spaces are assumed +to be a part of the filenames. + +quote(rsync -av host:'dir1/file1 dir2/file2' /dest) + +This would copy file1 and file2 into /dest using a remote shell. This +word-splitting is done by the remote shell, so if it doesn't work it means +that the remote shell isn't configured to split its args based on +whitespace (a very rare setting, but not unknown). If you need to transfer +a filename that contains whitespace, you'll need to either escape the +whitespace in a way that the remote shell will understand, or use wildcards +in place of the spaces. Two examples of this are: + +quote(rsync -av host:'file\ name\ with\ spaces' /dest) +quote(rsync -av host:file?name?with?spaces /dest) + +This latter example assumes that your shell passes through unmatched +wildcards. If it complains about "no match", put the name in quotes. + + manpagesection(CONNECTING TO AN RSYNC SERVER) It is also possible to use rsync without a remote shell as the @@ -289,6 +318,8 @@ --backup-dir make backups into this directory --suffix=SUFFIX backup suffix (default ~ w/o --backup-dir) -u, --update update only (don't overwrite newer files) + --inplace update the destination files inplace + -K, --keep-dirlinks treat symlinked dir on receiver as dir -l, --links copy symlinks as symlinks -L, --copy-links copy the referent of all symlinks --copy-unsafe-links copy the referent of "unsafe" symlinks @@ -304,7 +335,7 @@ -W, --whole-file copy whole files, no incremental checks --no-whole-file turn off --whole-file -x, --one-file-system don't cross filesystem boundaries - -B, --block-size=SIZE checksum blocking size (default 700) + -B, --block-size=SIZE force a fixed checksum block-size -e, --rsh=COMMAND specify the remote shell --rsync-path=PATH specify path to rsync on the remote machine --existing only update files that already exist @@ -315,6 +346,7 @@ --ignore-errors delete even if there are I/O errors --max-delete=NUM don't delete more than NUM files --partial keep partially transferred files + --partial-dir=DIR put a partially transferred file into DIR --force force deletion of dirs even if not empty --numeric-ids don't map uid/gid values by user/group name --timeout=TIME set I/O timeout in seconds @@ -346,8 +378,11 @@ --log-format=FORMAT log file transfers using specified format --password-file=FILE get password from FILE --bwlimit=KBPS limit I/O bandwidth, KBytes per second - --write-batch=PREFIX write batch fileset starting with PREFIX - --read-batch=PREFIX read batch fileset starting with PREFIX + --write-batch=FILE write a batch to FILE + --read-batch=FILE read a batch from FILE + --checksum-seed=NUM set block/file checksum seed + -4 --ipv4 prefer IPv4 + -6 --ipv6 prefer IPv6 -h, --help show this help screen @@ -476,11 +511,42 @@ regardless of the timestamps. This might change in the future (feel free to comment on this on the mailing list if you have an opinion). +dit(bf(-K, --keep-dirlinks)) On the receiving side, if a symlink is +pointing to a directory, it will be treated as matching a directory +from the sender. + +dit(bf(--inplace)) This causes rsync not to create a new copy of the file +and then move it into place. Instead rsync will overwrite the existing +file, meaning that the rsync algorithm can't extract the full amount of +network reduction it might otherwise (since it does not yet try to sort +data matches -- a future version may improve this). + +This option is useful for transfer of large files with block-based changes +or appended data, and also on systems that are disk bound, not network +bound. + +The option implies --partial (since an interrupted transfer does not delete +the file), but conflicts with --partial-dir, --compare-dest, and +--link-dest (a future rsync version will hopefully update the protocol to +remove these restrictions). + +WARNING: The file's data will be in an inconsistent state during the +transfer (and possibly afterward if the transfer gets interrupted), so you +should not use this option to update files that are in use. Also note that +rsync will be unable to update a file inplace that is not writable by the +receiving user. + dit(bf(-l, --links)) When symlinks are encountered, recreate the symlink on the destination. dit(bf(-L, --copy-links)) When symlinks are encountered, the file that -they point to (the referent) is copied, rather than the symlink. +they point to (the referent) is copied, rather than the symlink. In older +versions of rsync, this option also had the side-effect of telling the +receiving side to follow symlinks, such as symlinks to directories. In a +modern rsync such as this one, you'll need to specify --keep-dirlinks (-K) +to get this extra behavior. The only exception is when sending files to +an rsync that is too old to understand -K -- in that case, the -L option +will still have the side-effect of -K on that older receiving rsync. dit(bf(--copy-unsafe-links)) This tells rsync to copy the referent of symbolic links that point outside the copied tree. Absolute symlinks @@ -504,9 +570,9 @@ dit(bf(-W, --whole-file)) With this option the incremental rsync algorithm is not used and the whole file is sent as-is instead. The transfer may be faster if this option is used when the bandwidth between the source and -target machines is higher than the bandwidth to disk (especially when the +destination machines is higher than the bandwidth to disk (especially when the "disk" is actually a networked filesystem). This is the default when both -the source and target are on the local machine. +the source and destination are specified as local paths. dit(bf(--no-whole-file)) Turn off --whole-file, for use when it is the default. @@ -540,9 +606,9 @@ with the files and update them on the remote system. Note that if this option is not used, the optimization that excludes files that have not been modified cannot be effective; in other words, a missing -t or -a will -cause the next transfer to behave as if it used -I, and all files will have -their checksums compared and show up in log messages even if they haven't -changed. +cause the next transfer to behave as if it used -I, causing all files to be +updated (though the rsync algorithm will make the update fairly efficient +if the files haven't actually changed, you're much better off using -t). dit(bf(-n, --dry-run)) This tells rsync to not do any file transfers, instead it will just report the actions it would have taken. @@ -603,8 +669,9 @@ is only relevant without --delete because deletions are now done depth-first. Requires the --recursive option (which is implied by -a) to have any effect. -dit(bf(-B , --block-size=BLOCKSIZE)) This controls the block size used in -the rsync algorithm. See the technical report for details. +dit(bf(-B, --block-size=BLOCKSIZE)) This forces the block size used in +the rsync algorithm to a fixed value. It is normally selected based on +the size of each file being updated. See the technical report for details. dit(bf(-e, --rsh=COMMAND)) This option allows you to choose an alternative remote shell program to use for communication between the local and @@ -678,11 +745,11 @@ dit(bf(--include-from=FILE)) This specifies a list of include patterns from a file. -If em(FILE) is bf(-) the list will be read from standard input. +If em(FILE) is "-" the list will be read from standard input. dit(bf(--files-from=FILE)) Using this option allows you to specify the exact list of files to transfer (as read from the specified FILE or "-" -for stdin). It also tweaks the default behavior of rsync to make +for standard input). It also tweaks the default behavior of rsync to make transferring just the specified files and directories easier. For instance, the --relative option is enabled by default when this option is used (use --no-relative if you want to turn that off), all @@ -739,20 +806,25 @@ This option increases the usefulness of --partial because partially transferred files will remain in the new temporary destination until they have a chance to be completed. If DIR is a relative path, it is relative -to the destination directory (which changes in a recursive transfer). +to the destination directory. dit(bf(--link-dest=DIR)) This option behaves like bf(--compare-dest) but also will create hard links from em(DIR) to the destination directory for unchanged files. Files with changed ownership or permissions will not be linked. -Like bf(--compare-dest) if DIR is a relative path, it is relative -to the destination directory (which changes in a recursive transfer). An example: verb( rsync -av --link-dest=$PWD/prior_dir host:src_dir/ new_dir/ ) +Like bf(--compare-dest) if DIR is a relative path, it is relative to the +destination directory. +Note that rsync versions prior to 2.6.1 had a bug that could prevent +--link-dest from working properly for a non-root user when -o was specified +(or implied by -a). If the receiving rsync is not new enough, you can work +around this bug by avoiding the -o option. + dit(bf(-z, --compress)) With this option, rsync compresses any data from the files that it sends to the destination machine. This option is useful on slow connections. The compression method used is the @@ -842,6 +914,46 @@ --partial option tells rsync to keep the partial file which should make a subsequent transfer of the rest of the file much faster. +dit(bf(--partial-dir=DIR)) Turns on --partial mode, but tells rsync to +put a partially transferred file into DIR instead of writing out the +file to the destination dir. Rsync will also use a file found in this +dir as data to speed up the transfer (i.e. when you redo the send after +rsync creates a partial file) and delete such a file after it has served +its purpose. Note that if --whole-file is specified (or implied) that an +existing partial-dir file will not be used to speedup the transfer (since +rsync is sending files without using the incremental rsync algorithm). + +Rsync will create the dir if it is missing (just the last dir -- not the +whole path). This makes it easy to use a relative path (such as +"--partial-dir=.rsync-partial") to have rsync create the partial-directory +in the destination file's directory (rsync will also try to remove the DIR +if a partial file was found to exist at the start of the transfer and the +DIR was specified as a relative path). + +If the partial-dir value is not an absolute path, rsync will also add an +--exclude of this value at the end of all your existing excludes. This +will prevent partial-dir files from being transferred and also prevent the +untimely deletion of partial-dir items on the receiving side. An example: +the above --partial-dir option would add an "--exclude=.rsync-partial/" +rule at the end of any other include/exclude rules. Note that if you are +supplying your own include/exclude rules, you may need to manually insert a +rule for this directory exclusion somewhere higher up in the list so that +it has a high enough priority to be effective (e.g., if your rules specify +a trailing --exclude=* rule, the auto-added rule will be ineffective). + +IMPORTANT: the --partial-dir should not be writable by other users or it +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 +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). + dit(bf(--progress)) This option tells rsync to print information showing the progress of the transfer. This gives a bored user something to watch. @@ -870,9 +982,9 @@ These additional numbers tell you how many files have been updated, and what percent of the total number of files has been scanned. -dit(bf(-P)) The -P option is equivalent to --partial --progress. I -found myself typing that combination quite often so I created an -option to make it easier. +dit(bf(-P)) The -P option is equivalent to --partial --progress. Its +purpose is to make it much easier to specify these two options for a long +transfer that may be interrupted. dit(bf(--password-file)) This option allows you to provide a password in a file for accessing a remote rsync server. Note that this option @@ -889,13 +1001,33 @@ result is an average transfer rate equaling the specified limit. A value of zero specifies no limit. -dit(bf(--write-batch=PREFIX)) Generate a set of files that can be -transferred as a batch update. Each filename in the set starts with -PREFIX. See the "BATCH MODE" section for details. - -dit(bf(--read-batch=PREFIX)) Apply a previously generated change batch, -using the fileset whose filenames start with PREFIX. See the "BATCH -MODE" section for details. +dit(bf(--write-batch=FILE)) Record a file that can later be applied to +another identical destination with --read-batch. See the "BATCH MODE" +section for details. + +dit(bf(--read-batch=FILE)) Apply all of the changes stored in FILE, a +file previously generated by --write-batch. +If em(FILE) is "-" the batch data will be read from standard input. +See the "BATCH MODE" section for details. + +dit(bf(-4, --ipv4) or bf(-6, --ipv6)) Tells rsync to prefer IPv4/IPv6 +when creating sockets. This only affects sockets that rsync has direct +control over, such as the outgoing socket when directly contacting an +rsync daemon, or the incoming sockets that an rsync daemon uses to +listen for connections. One of these options may be required in older +versions of Linux to work around an IPv6 bug in the kernel (if you see +an "address already in use" error when nothing else is using the port, +try specifying --ipv6 or --ipv4 when starting the daemon). + +dit(bf(--checksum-seed=NUM)) Set the MD4 checksum seed to the integer +NUM. This 4 byte checksum seed is included in each block and file +MD4 checksum calculation. By default the checksum seed is generated +by the server and defaults to the current time(). This option +is used to set a specific checksum seed, which is useful for +applications that want repeatable block and file checksums, or +in the case where the user wants a more random checksum seed. +Note that setting NUM to 0 causes rsync to use the default of time() +for checksum seed. enddit() @@ -1067,7 +1199,8 @@ manpagesection(BATCH MODE) bf(Note:) Batch mode should be considered experimental in this version -of rsync. The interface or behavior may change before it stabilizes. +of rsync. The interface and behavior have now stabilized, though, so +feel free to try this out. Batch mode can be used to apply the same set of updates to many identical systems. Suppose one has a tree which is replicated on a @@ -1076,76 +1209,108 @@ hosts. In order to do this using batch mode, rsync is run with the write-batch option to apply the changes made to the source tree to one of the destination trees. The write-batch option causes the rsync -client to store the information needed to repeat this operation against -other destination trees in a batch update fileset (see below). The -filename of each file in the fileset starts with a prefix specified by -the user as an argument to the write-batch option. This fileset is -then copied to each remote host, where rsync is run with the read-batch -option, again specifying the same prefix, and the destination tree. -Rsync updates the destination tree using the information stored in the -batch update fileset. +client to store in a "batch file" all the information needed to repeat +this operation against other, identical destination trees. -The fileset consists of 4 files: +To apply the recorded changes to another destination tree, run rsync +with the read-batch option, specifying the name of the same batch +file, and the destination tree. Rsync updates the destination tree +using the information stored in the batch file. + +For convenience, one additional file is creating when the write-batch +option is used. This file's name is created by appending +".sh" to the batch filename. The .sh file contains +a command-line suitable for updating a destination tree using that +batch file. It can be executed using a Bourne(-like) shell, optionally +passing in an alternate destination tree pathname which is then used +instead of the original path. This is useful when the destination tree +path differs from the original destination tree path. -itemize( -it() bf(.rsync_argvs) command-line arguments -it() bf(.rsync_flist) rsync internal file metadata -it() bf(.rsync_csums) rsync checksums -it() bf(.rsync_delta) data blocks for file update & change -) - -The .rsync_argvs file contains a command-line suitable for updating a -destination tree using that batch update fileset. It can be executed -using a Bourne(-like) shell, optionally passing in an alternate -destination tree pathname which is then used instead of the original -path. This is useful when the destination tree path differs from the -original destination tree path. - -Generating the batch update fileset once saves having to perform the -file status, checksum and data block generation more than once when +Generating the batch file once saves having to perform the file +status, checksum, and data block generation more than once when updating multiple destination trees. Multicast transport protocols can -be used to transfer the batch update files in parallel to many hosts at -once, instead of sending the same data to every host individually. +be used to transfer the batch update files in parallel to many hosts +at once, instead of sending the same data to every host individually. -Example: +Examples: verb( - $ rsync --write-batch=pfx -a /source/dir/ /adest/dir/ - $ rcp pfx.rsync_* remote: - $ ssh remote rsync --read-batch=pfx -a /bdest/dir/ - # or alternatively - $ ssh remote ./pfx.rsync_argvs /bdest/dir/ + $ rsync --write-batch=foo -a host:/source/dir/ /adest/dir/ + $ scp foo* remote: + $ ssh remote ./foo.sh /bdest/dir/ ) -In this example, rsync is used to update /adest/dir/ with /source/dir/ -and the information to repeat this operation is stored in the files -pfx.rsync_*. These files are then copied to the machine named "remote". -Rsync is then invoked on "remote" to update /bdest/dir/ the same way as -/adest/dir/. The last line shows the rsync_argvs file being used to -invoke rsync. +verb( + $ rsync --write-batch=foo -a /source/dir/ /adest/dir/ + $ ssh remote rsync --read-batch=- -a /bdest/dir/