diff -urN --exclude=patches rsync-2.6.6/INSTALL rsync-2.6.7/INSTALL --- rsync-2.6.6/INSTALL 2003-12-15 10:20:06.000000000 -0800 +++ rsync-2.6.7/INSTALL 2005-12-16 08:57:26.000000000 -0800 @@ -1,4 +1,4 @@ -To build and install rsync +To build and install rsync: $ ./configure $ make @@ -9,8 +9,15 @@ $ ./configure --help +Configure tries to figure out if the local system uses group "nobody" or +"nogroup" by looking in the /etc/group file. (This is only used for the +default group of an rsync daemon, which attempts to run with "nobody" +user and group permissions.) You can change the default user and group +for the daemon by editing the NOBODY_USER and NOBODY_GROUP defines in +config.h, or just override them in your /etc/rsyncd.conf file. + As of 2.4.7, rsync uses Eric Troan's popt option-parsing library. A -cut-down copy of release 1.5 is included in the rsync distribution, +cut-down copy of release 1.6.4 is included in the rsync distribution, and will be used if there is no popt library on your build host, or if the --with-included-popt option is passed to ./configure. @@ -18,7 +25,6 @@ to pop up an xterm on DISPLAY=:0 if it crashes. You might find this useful, but it should be turned off for production builds. - RPM NOTES --------- @@ -37,16 +43,15 @@ Install gcc or HP's "ANSI/C Compiler". - - MAC OSX NOTES ------------- -Mac OS X (Darwin) seems to have an IPv6 stack, but it does not -completely implement the "New Sockets" API. +Some versions of Mac OS X (Darwin) seem to have an IPv6 stack, but do +not completely implement the "New Sockets" API. - says that Apple do not support -IPv6 yet. If your build fails, try again with --disable-ipv6. + says that Apple started to support +IPv6 in 10.2 (Jaguar). If your build fails, try again after running +configure with --disable-ipv6. IBM AIX NOTES ------------- diff -urN --exclude=patches rsync-2.6.6/Makefile.in rsync-2.6.7/Makefile.in --- rsync-2.6.6/Makefile.in 2005-07-07 14:29:57.000000000 -0700 +++ rsync-2.6.7/Makefile.in 2006-02-05 20:54:30.000000000 -0800 @@ -33,7 +33,7 @@ OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \ main.o checksum.o match.o syscall.o log.o backup.o OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \ - fileio.o batch.o clientname.o + fileio.o batch.o clientname.o chmod.o OBJS3=progress.o pipe.o DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \ @@ -90,29 +90,14 @@ t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS) -gen: $(srcdir)/configure $(srcdir)/config.h.in proto man +gen: + $(MAKE) -C $(srcdir) -f prepare-source.mak gen -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 - -$(srcdir)/rsyncd.conf.5: $(srcdir)/rsyncd.conf.yo - yodl2man -o $(srcdir)/rsyncd.conf.5 $(srcdir)/rsyncd.conf.yo +man: + $(MAKE) -C $(srcdir) -f prepare-source.mak man proto: - 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 + $(MAKE) -C $(srcdir) -f prepare-source.mak proto.h clean: cleantests rm -f *~ $(OBJS) $(TLS_OBJ) $(CHECK_PROGS) $(CHECK_OBJS) @@ -160,8 +145,8 @@ POSIXLY_CORRECT=1 TOOLDIR=`pwd` rsync_bin=`pwd`/rsync$(EXEEXT) srcdir="$(srcdir)" $(srcdir)/runtests.sh wildtest.o: wildtest.c lib/wildmatch.c rsync.h -wildtest$(EXEEXT): wildtest.o - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ wildtest.o @BUILD_POPT@ $(LIBS) +wildtest$(EXEEXT): wildtest.o lib/compat.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ wildtest.o lib/compat.o @BUILD_POPT@ $(LIBS) # This does *not* depend on building or installing: you can use it to # check a version installed from a binary or some other source tree, diff -urN --exclude=patches rsync-2.6.6/NEWS rsync-2.6.7/NEWS --- rsync-2.6.6/NEWS 2005-07-28 12:31:05.000000000 -0700 +++ rsync-2.6.7/NEWS 2006-03-11 10:25:04.000000000 -0800 @@ -1,65 +1,320 @@ -NEWS for rsync 2.6.6 (28 Jul 2005) +NEWS for rsync 2.6.7 (11 Mar 2006) + Protocol: 29 (unchanged) -Changes since 2.6.5: +Changes since 2.6.6: - SECURITY FIXES: + OUTPUT CHANGES: - - The zlib code was upgraded to version 1.2.3 in order to make it more - secure. While the widely-publicized security problem in zlib 1.2.2 did - not affect rsync, another security problem surfaced that affects rsync's - zlib 1.1.4. + - The letter 'D' in the itemized output was being used for both devices + (character or block) as well as other special files (such as fifos and + named sockets). This has changed to separate non-device special files + under the 'S' designation (e.g. "cS+++++++ path/fifo"). See also the + "--specials" option, below. + + - The way rsync escapes unreadable characters has changed. First, rsync + now has support for recognizing valid multibyte character sequences in + your current locale, allowing it to escape fewer characters than before + for a locale such as UTF-8. Second, it now uses an escape idiom of + "\#123", which is the literal string "\#" followed by exactly 3 octal + digits. Rsync no longer doubles a backslash character in a filename + (e.g. it used to output "foo\\bar" when copying "foo\bar") -- now it only + escapes a backslash that is followed by a hash-sign and 3 digits (0-9) + (e.g. it will output "foo\#134#789" when copying "foo\#789"). See also + the --8-bit-output (-8) option, mentioned below. + + Script writers: the local rsync is the one that outputs escaped names, + so if you need to support unescaping of filenames for older rsyncs, I'd + suggest that you parse the output of "rsync --version" and only use the + old unescaping rules for 2.6.5 and 2.6.6. BUG FIXES: - - The setting of flist->high in clean_flist() was wrong for an empty list. - This could cause flist_find() to crash in certain rare circumstances - (e.g. if just the right directory setup was around when --fuzzy was - combined with --link-dest). - - - The outputting of hard-linked files when verbosity was > 1 was not right: - without -i it would output the name of each hard-linked file as though - it had been changed (it now outputs a "is hard linked" message for the - file); with -i it would output all dots for the unchanged attributes of - a hard-link (it now changes those dots to spaces, as is done for other - totally unchanged items). - - - When backing up a changed symlink or device, get rid of any old backup - item so that we don't get an "already exists" error. - - - A couple places that were comparing a local and a remote modification- - time were not honoring the --modify-window option. - - - Fixed a bug where the 'p' (permissions) itemized-changes flag might get - set too often (if some non-significant mode bits differed). - - - Fixed a really old, minor bug that could cause rsync to warn about being - unable to mkdir() a path that ends in "/." because it just created the - directory (required --relative, --no-implied-dirs, a source path that - ended in either a trailing slash or a trailing "/.", and a non-existing - destination dir to tickle the bug in a recent version). + - Fixed a really old bug that caused --checksum (-c) to checksum all the + files encountered during the delete scan (ouch). + + - Fixed a potential hang in a remote generator: when the receiver gets a + read-error on the socket, it now signals the generator about this so that + the generator does not try to send any of the terminating error messages + to the client (avoiding a potential hang in some setups). + + - Made hard-links work with symlinks and devices again. + + - If the sender gets an early EOF reading a source file, we propagate this + error to the receiver so that it can discard the file and try requesting + it again (which is the existing behavior for other kinds of read errors). + + - If a device-file/special-file changes permissions, rsync now updates the + permissions without recreating the file. + + - If the user specifies a remote-host for both the source and destination, + we now output a syntax error rather than trying to open the destination + hostspec as a filename. + + - When --inplace creates a new destination file, rsync now creates it with + permissions 0600 instead of 0000 -- this makes restarting possible when + the transfer gets interrupted in the middle of sending a new file. + + - Reject the combination of --inplace and --sparse since the sparse-output + algorithm doesn't work when overwriting existing data. + + - Fixed the directory name in the error that is output when pop_dir() + fails. + + - Really fixed the parsing of a "!" entry in .cvsignore files this time. + + - If the generator gets a stat() error on a file, output it (this used to + require at least -vv for the error to be seen). + + - If waitpid() fails or the child rsync didn't exit cleanly, we now handle + the exit status properly and generate a better error. + + - Fixed some glitches in the double-verbose output when using --copy-dest, + --link-dest, or --compare-dest. Also improved how the verbose output + handles hard-links (within the transfer) that had an up-to-date alternate + "dest" file, and copied files (via --copy-dest). + + - Fixed the matching of the dont-compress items (e.g. *.gz) against files + that have a path component containing a slash. + + - If code reading a filter/exclude file an EINTR error, rsync now clears + the error flag on the file handle so it can keep on reading. + + - If --relative is active, the sending side cleans up trailing "/" or "/." + suffixes to avoid triggering a bug in older rsync versions. Also, we now + reject a ".." dir if it would be sent as a relative dir. + + - If a non-directory is in the way of a directory and rsync is run with + --dry-run and --delete, rsync no longer complains about not being able + to opendir() the not-yet present directory. + + - When --list-only is used and a non-existent local destination dir was + also specified as a destination, rsync no longer generates a warning + about being unable to create the missing directory. + + - Fixed some problems with --relative --no-implied-dirs when the + destination directory did not yet exist: we can now create a symlink or + device when it is the first thing in the missing dir, and --fuzzy no + longer complains about being unable to open the missing dir. + + - Fixed a bug where the --copy-links option would not affect implied + directories without --copy-unsafe-links (see --relative). + + - Got rid of the need for --force to be used in some circumstances with + --delete-after (making it consistent with --delete-before/-during). + + - Rsync now ignores the SIGXFSZ signal, just in case your OS sends this + when a file is too large (rsync handles the write error). + + - Fixed a bug in the Proxy-Authorization header's base64-encoded value: it + was not properly padded with trailing '=' chars. This only affects a + user that need to use a password-authenticated proxy for an outgoing + daemon-rsync connection. + + - If we're transferring an empty directory to a new name, rsync no longer + forces S_IWUSR if it wasn't already set, nor does it accidentally leave + it set. + + - Fixed a bug in the debug output (-vvvvv) that could mention the wrong + checksum for the current file offset. + + - Rsync no longer allows a single directory to be copied over a non- + directory destination arg. ENHANCEMENTS: - - Made the "max verbosity" setting in the rsyncd.conf file settable on a - per-module basis (which now matches the documentation). + - Added the --append option that makes rsync append data onto files that + are longer on the source than the destination (this includes new files). + + - Added the --min-size=SIZE option to exclude small files from the + transfer. + + - Added the --compress-level option to allow you to set how aggressive + rsync's compression should be (this option implies --compress). + + - Enhanced the parsing of the SIZE value for --min-size and --max-size to + allow easy entry of multiples of 1000 (instead of just multiples of 1024) + and off-by-one values too (e.g. --max-size=8mb-1). + + - Added the --8-bit-output (-8) option, which tells rsync to avoid escaping + high-bit characters that it thinks are unreadable in the current locale. + + - The new option --human-readable (-h) changes the output of --progress, + --stats, and the end-of-run summary to be easier to read. If repeated, + the units become powers of 1024 instead of powers of 1000. (The old + meaning of -h, as a shorthand for --help, still works as long as you + just use it on its own, as in "rsync -h".) + + - If lutimes() and/or lchmod() are around, use them to allow the + preservation of attributes on symlinks. + + - The --link-dest option now affects symlinks and devices (when possible). + + - Added two config items to the rsyncd.conf parsing: "pre-xfer exec" and + "post-xfer exec". These allow a command to be specified on a per-module + basis that will be run before and/or after a daemon-mode transfer. (See + the man page for a list of the environment variables that are set with + information about the transfer.) + + - When using the --relative option, you can now insert a dot dir in + the source path to indicate where the replication of the source dirs + should start. For example, if you specify a source path of + rsync://host/module/foo/bar/./baz/dir with -R, rsync will now only + replicate the "baz/dir" part of the source path (note: a trailing + dot dir is unaffected unless it also has a trailing slash). + + - Added some new --no-FOO options that make it easier to override unwanted + implied or default options. For example, "-a --no-o" (aka "--archive + --no-owner") can be used to turn off the preservation of file ownership + that is implied by -a. + + - Added the --chmod=MODE option that allows the destination permissions to + be changed from the source permissions. E.g. --chmod=g+w,o-rwx + + - Added the "incoming chmod" and "outgoing chmod" daemon options that allow + a module to specify what permissions changes should be applied to all + files copied to and from the daemon. + + - Allow the --temp-dir option to be specified when starting a daemon, which + sets the default temporary directory for incoming files. + + - If --delete is combined with --dirs without --recursive, rsync will now + delete in any directory whose content is being synchronized. + + - If --backup is combined with --delete without --backup-dir (and without + --delete-excluded), we add a "protect" filter-rule to ensure that files + with the backup suffix are not deleted. + + - The file-count stats that are output by --progress were improved to + better indicate what the numbers mean. For instance, the output: + "(xfer#5, to-check=8383/9999)" indicates that this was the fifth file + to be transferred, and we still need to check 8383 more files out of + a total of 9999. + + - The include/exclude code now allows a dir/*** directive (with 3 trailing + stars) to match both the dir itself as well as all the content below the + dir (dir/** would not match the dir). + + - Added the --prune-empty-dirs (-m) option that makes the receiving rsync + discard empty chains of directories from the file-list. This makes it + easier to selectively copy files from a source hierarchy and end up with + just the directories needed to hold the resulting files. + + - If the --itemize-changes (-i) option is repeated, rsync now includes + unchanged files in the itemized output (similar to -vv, but without all + the other verbose messages that can get in the way). Of course, the + client must be version 2.6.7 for this to work, but the remote rsync only + needs to be 2.6.7 if you're pushing files. + + - Added the --specials option to tell rsync to copy non-device special + files (which rsync now attempts even as a normal user). The --devices + option now requests the copying of just devices (character and block). + The -D option still requests both (e.g. --devices and --specials), -a + still implies -D, and non-root users still get a silent downgrade that + omits device copying. + + - Added the --super option to make the receiver always attempt super-user + activities. This is useful for systems that allow things such as devices + to be created or ownership to be set without being UID 0, and is also + useful for someone who wants to ensure that errors will be output if the + receiving rsync isn't being run as root. + + - Added the --sockopts option for those few who want to customize the TCP + options used to contact a daemon rsync. + + - Added a way for the --temp-dir option to be combined with a partial-dir + setting that lets rsync avoid non-atomic updates (for those times when + --temp-dir is not being used because space is tight). + + - A new support script, files-to-excludes, will transform a list of files + into a set of include/exclude directives that will copy those files. + + - A new option, --executability (-E) can be used to preserve just the + execute bit on files, for those times when using the --perms option is + not desired. + + - The daemon now logs each connection and also each module-list request + that it receives. + + - New log-format options: %M (modtime), %U (uid), %G (gid), and %B + (permission bits, e.g. "rwxr-xrwt"). + + - The --dry-run option no longer forces the enabling of --verbose. + + - The --remove-sent-files option now does a better job of incrementally + removing the sent files on the sending side (older versions tended to + clump up all the removals at the end). + + - A daemon now supersedes its minimal SIGCHLD handler with the standard + PID-remembering version after forking. This ensures that the generator + can get the child-exit status from the receiver. + + - Use of the --bwlimit option no longer interferes with the remote rsync + sending error messages about invalid/refused options. + + - Rsync no longer returns a usage error when used with one local source arg + and no destination: this now implies the --list-only option, just like + the comparable situation with a remote source arg. + + - Added the --copy-dirlinks option, a more limited version of --copy-links. + + - Various documentation improvements, including: a better synopsis, some + improved examples, a better discussion of the presence and absence of + --perms (including how it interacts with the new --executability and + --chmod options), an extended discussion of --temp-dir, an improved + discussion of --partial-dir, a better description of rsync's pattern + matching characters, an improved --no-implied-dirs section, and the + documenting of what the --stats option outputs. + + - Various new and updated diffs in the patches dir, including: acls.diff, + xattrs.diff, atimes.diff, detect-renamed.diff, and slp.diff. + + INTERNAL: + + - We now use sigaction() and sigprocmask() if possible, and fall back on + signal() if not. Using sigprocmask() ensures that rsync enables all the + signals that it needs, just in case it was started in a masked state. + + - Some buffer sizes were expanded a bit, particularly on systems where + MAXPATHLEN is overly small (e.g. cygwin). + + - If io_printf() tries to format more data than fits in the buffer, exit + with an error instead of transmitting a truncated buffer. + + - If a va_copy macro is defined, lib/snprintf.c will use it when defining + the VA_COPY macro. + + - Reduced the amount of stack memory needed for each level of directory + recursion by nearly MAXPATHLEN bytes. + + - The wildmatch function was extended to allow an array of strings to be + supplied as the string to match. This allows the exclude code to do less + string copying. + + - Got rid of the safe_fname() function (and all the myriad calls) and + replaced it with a new function in the log.c code that filters all the + output going to the terminal. - - The support/rrsync script has been upgraded to verify the args of options - that take args (instead of rejecting any such options). The script was - also changed to try to be more secure and to fix a problem in the parsing - of a pull operation that has multiple sources. + - Unified the f_name() and the f_name_to() functions. - - Improved the documentation that explains the difference between a - normal daemon transfer and a daemon-over remote-shell transfer. + - Improved the hash-table code the sender uses to handle checksums to make + it use slightly less memory and run just a little faster. - - Some of the diffs supplied in the patches dir were fixed and/or - improved. + DEVELOPER RELATED: - BUILD CHANGES: + - The diffs in the patches dir now require "patch -p1 high in clean_flist() was wrong for an empty list. + This could cause flist_find() to crash in certain rare circumstances + (e.g. if just the right directory setup was around when --fuzzy was + combined with --link-dest). + + - The outputting of hard-linked files when verbosity was > 1 was not right: + (1) Without -i it would output the name of each hard-linked file as + though it had been changed; it now outputs a "is hard linked" message for + the file. (2) With -i it would output all dots for the unchanged + attributes of a hard-link; it now changes those dots to spaces, as is + done for other totally unchanged items. + + - When backing up a changed symlink or device, get rid of any old backup + item so that we don't get an "already exists" error. + + - A couple places that were comparing a local and a remote modification- + time were not honoring the --modify-window option. + + - Fixed a bug where the 'p' (permissions) itemized-changes flag might get + set too often (if some non-significant mode bits differed). + + - Fixed a really old, minor bug that could cause rsync to warn about being + unable to mkdir() a path that ends in "/." because it just created the + directory (required --relative, --no-implied-dirs, a source path that + ended in either a trailing slash or a trailing "/.", and a non-existing + destination dir to tickle the bug in a recent version). + + ENHANCEMENTS: + + - Made the "max verbosity" setting in the rsyncd.conf file settable on a + per-module basis (which now matches the documentation). + + - The support/rrsync script has been upgraded to verify the args of options + that take args (instead of rejecting any such options). The script was + also changed to try to be more secure and to fix a problem in the parsing + of a pull operation that has multiple sources. + + - Improved the documentation that explains the difference between a + normal daemon transfer and a daemon-over remote-shell transfer. + + - Some of the diffs supplied in the patches dir were fixed and/or + improved. + + BUILD CHANGES: + + - Made configure define NOBODY_USER (currently hard-wired to "nobody") and + NOBODY_GROUP (set to either "nobody" or "nogroup" depending on what we + find in the /etc/group file). + + - Added a test to the test suite, itemized.test, that tests the output of + -i (log-format w/%i) and some double-verbose messages. + + NEWS for rsync 2.6.5 (1 Jun 2005) Protocol: 29 (unchanged) Changes since 2.6.4: @@ -228,7 +294,7 @@ - An OS that has a binary mode for its files (such as cygwin) needed setmode(fd, O_BINARY) called on the temp-file we opened with - mkstemp(). (Fix derived from the cygwin's 2.6.3 rsync package.) + mkstemp(). (Fix derived from cygwin's 2.6.3 rsync package.) - Fixed a potential hang when verbosity is high, the client side is the sender, and the file-list is large. @@ -293,9 +359,11 @@ (since the forked process already has a copy of the exclude list, there's no need to send them a set of duplicates). - - When --progress is specified, the output of items that the generator - is creating (e.g. dirs, symlinks) is now integrated into the progress - output without overlapping it. (Requires protocol 29.) + - The output of the items that are being updated by the generator (dirs, + symlinks, devices) is now intermingled in the proper order with the + output from the items that the receiver is updating (regular files) + when pulling. This misordering was particularly bad when --progress + was specified. (Requires protocol 29.) - When --timeout is specified, lulls that occur in the transfer while the generator is doing work that does not generate socket traffic @@ -915,7 +983,7 @@ - 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 + than the current basis file when no new data has been transferred over the wire for that file. - Fixed a byte-order problem in --batch-mode on big-endian machines. @@ -1619,6 +1687,8 @@ Partial Protocol History RELEASE DATE VER. DATE OF COMMIT* PROTOCOL + 11 Mar 2006 2.6.7 29 + 28 Jul 2005 2.6.6 29 01 Jun 2005 2.6.5 29 30 Mar 2005 2.6.4 17 Jan 2005 29 30 Sep 2004 2.6.3 28 diff -urN --exclude=patches rsync-2.6.6/TODO rsync-2.6.7/TODO --- rsync-2.6.6/TODO 2005-02-28 19:11:28.000000000 -0800 +++ rsync-2.6.7/TODO 2006-02-03 12:49:25.000000000 -0800 @@ -1,22 +1,17 @@ -*- indented-text -*- -BUGS --------------------------------------------------------------- -Do not rely on having a group called "nobody" - FEATURES ------------------------------------------------------------ Use chroot only if supported Allow supplementary groups in rsyncd.conf 2002/04/09 Handling IPv6 on old machines -Other IPv6 stuff: +Other IPv6 stuff Add ACL support 2001/12/02 -Lazy directory creation proxy authentication 2002/01/23 SOCKS 2002/01/23 FAT support -Allow forcing arbitrary permissions 2002/03/12 --diff david.e.sewell 2002/03/15 Add daemon --no-fork option -Create more granular verbosity jw 2003/05/15 +Create more granular verbosity 2003/05/15 DOCUMENTATION -------------------------------------------------------- Keep list of open issues and todos on the web site @@ -25,22 +20,19 @@ LOGGING -------------------------------------------------------------- Memory accounting Improve error messages -Better statistics: Rasmus 2002/03/08 +Better statistics Rasmus 2002/03/08 Perhaps flush stdout like syslog -Log deamon sessions that just list modules Log child death on signal -Log errors with function that reports process of origin verbose output David Stein 2001/12/20 internationalization DEVELOPMENT -------------------------------------------------------- Handling duplicate names Use generic zlib 2002/02/25 -TDB: 2002/03/12 +TDB 2002/03/12 Splint 2002/03/12 PERFORMANCE ---------------------------------------------------------- -File list structure in memory Traverse just one directory at a time Allow skipping MD4 file_sum 2002/04/08 Accelerate MD4 @@ -52,8 +44,6 @@ Test large files Create mutator program for testing Create configure option to enable dangerous tests -If tests are skipped, say why. -Test daemon feature to disallow particular options. Create pipe program for testing Create test makefile target for some tests @@ -66,17 +56,6 @@ -BUGS --------------------------------------------------------------- - - -Do not rely on having a group called "nobody" - - http://www.linuxbase.org/spec/refspecs/LSB_1.1.0/gLSB/usernames.html - - On Debian it's "nogroup" - - -- -- - FEATURES ------------------------------------------------------------ @@ -133,7 +112,7 @@ -- -- -Other IPv6 stuff: +Other IPv6 stuff Implement suggestions from http://www.kame.net/newsletter/19980604/ and ftp://ftp.iij.ad.jp/pub/RFC/rfc2553.txt @@ -159,15 +138,6 @@ -- -- -Lazy directory creation - - With the current common --include '*/' --exclude '*' pattern, people - can end up with many empty directories. We might avoid this by - lazily creating such directories. - - -- -- - - proxy authentication 2002/01/23 Allow RSYNC_PROXY to be http://user:pass@proxy.foo:3128/, and do @@ -199,35 +169,6 @@ -- -- -Allow forcing arbitrary permissions 2002/03/12 - - On 12 Mar 2002, Dave Dykstra wrote: - > If we would add an option to do that functionality, I - > would vote for one that was more general which could mask - > off any set of permission bits and possibly add any set of - > bits. Perhaps a chmod-like syntax if it could be - > implemented simply. - - I think that would be good too. For example, people uploading files - to a web server might like to say - - rsync -avzP --chmod a+rX ./ sourcefrog.net:/home/www/sourcefrog/ - - Ideally the patch would implement as many of the gnu chmod semantics - as possible. I think the mode parser should be a separate function - that passes back something like (mask,set) description to the rest - of the program. For bonus points there would be a test case for the - parser. - - Possibly also --chown - - (Debian #23628) - - NOTE: there is a patch that implements this in the "patches" subdir. - - -- -- - - --diff david.e.sewell 2002/03/15 Allow people to specify the diff command. (Might want to use wdiff, @@ -252,7 +193,7 @@ -- -- -Create more granular verbosity jw 2003/05/15 +Create more granular verbosity 2003/05/15 Control output with the --report option. @@ -322,14 +263,10 @@ our load? (Debian #28416) Probably fixed now, but a test case would be good. - When running as a daemon, some errors should both be returned to the - user and logged. This will make interacting with a daemon less - cryptic. - -- -- -Better statistics: Rasmus 2002/03/08 +Better statistics Rasmus 2002/03/08 hey, how about an rsync option that just gives you the @@ -355,14 +292,6 @@ -- -- -Log deamon sessions that just list modules - - At the connections that just get a list of modules are not logged, - but they should be. - - -- -- - - Log child death on signal If a child of the rsync daemon dies with a signal, we should notice @@ -371,15 +300,6 @@ -- -- -Log errors with function that reports process of origin - - Use a separate function for reporting errors; prefix it with - "rsync:" or "rsync(remote)", or perhaps even "rsync(local - generator): ". - - -- -- - - verbose output David Stein 2001/12/20 At end of transfer, show how many files were or were not transferred @@ -445,7 +365,7 @@ -- -- -TDB: 2002/03/12 +TDB 2002/03/12 Rather than storing the file list in memory, store it in a TDB. @@ -472,20 +392,6 @@ PERFORMANCE ---------------------------------------------------------- -File list structure in memory - - Rather than one big array, perhaps have a tree in memory mirroring - the directory tree. - - This might make sorting much faster! (I'm not sure it's a big CPU - problem, mind you.) - - It might also reduce memory use in storing repeated directory names - -- again I'm not sure this is a problem. - - -- -- - - Traverse just one directory at a time Traverse just one directory at a time. Tridge says it's possible. @@ -586,16 +492,6 @@ -- -- -If tests are skipped, say why. - - -- -- - - -Test daemon feature to disallow particular options. - - -- -- - - Create pipe program for testing Create pipe program that makes slow/jerky connections for diff -urN --exclude=patches rsync-2.6.6/access.c rsync-2.6.7/access.c --- rsync-2.6.6/access.c 2005-02-13 16:53:43.000000000 -0800 +++ rsync-2.6.7/access.c 2005-11-10 08:42:46.000000000 -0800 @@ -208,7 +208,7 @@ ret = match_binary(a, t, mask, addrlen); -out: + out: freeaddrinfo(resa); freeaddrinfo(rest); return ret; diff -urN --exclude=patches rsync-2.6.6/authenticate.c rsync-2.6.7/authenticate.c --- rsync-2.6.6/authenticate.c 2005-04-10 10:09:10.000000000 -0700 +++ rsync-2.6.7/authenticate.c 2006-03-06 10:22:20.000000000 -0800 @@ -21,21 +21,18 @@ #include "rsync.h" extern char *password_file; -extern int am_root; /*************************************************************************** encode a buffer using base64 - simple and slow algorithm. null terminates the result. ***************************************************************************/ -void base64_encode(char *buf, int len, char *out) +void base64_encode(char *buf, int len, char *out, int pad) { char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int bit_offset, byte_offset, idx, i; unsigned char *d = (unsigned char *)buf; int bytes = (len*8 + 5)/6; - memset(out, 0, bytes+1); - for (i = 0; i < bytes; i++) { byte_offset = (i*6)/8; bit_offset = (i*6)%8; @@ -49,6 +46,11 @@ } out[i] = b64[idx]; } + + while (pad && (i % 4)) + out[i++] = '='; + + out[i] = '\0'; } /* Generate a challenge buffer and return it base64-encoded. */ @@ -70,7 +72,7 @@ sum_update(input, sizeof input); sum_end(md4_out); - base64_encode(md4_out, MD4_SUM_LENGTH, challenge); + base64_encode(md4_out, MD4_SUM_LENGTH, challenge, 0); } @@ -90,13 +92,13 @@ return 0; if (do_stat(fname, &st) == -1) { - rsyserr(FLOG, errno, "stat(%s)", safe_fname(fname)); + rsyserr(FLOG, errno, "stat(%s)", fname); ok = 0; } else if (lp_strict_modes(module)) { if ((st.st_mode & 06) != 0) { rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n"); ok = 0; - } else if (am_root && (st.st_uid != 0)) { + } else if (MY_UID() == 0 && st.st_uid != 0) { rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n"); ok = 0; } @@ -160,19 +162,19 @@ if ((fd = open(filename,O_RDONLY)) < 0) { rsyserr(FERROR, errno, "could not open password file \"%s\"", - safe_fname(filename)); + filename); 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)", safe_fname(filename)); + 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 (MY_UID() == 0 && st.st_uid != 0) { rprintf(FERROR,"password file must be owned by root when running as root\n"); ok = 0; } @@ -209,7 +211,7 @@ sum_update(challenge, strlen(challenge)); sum_end(buf); - base64_encode(buf, MD4_SUM_LENGTH, out); + base64_encode(buf, MD4_SUM_LENGTH, out, 0); } /* Possibly negotiate authentication with the client. Use "leader" to @@ -223,7 +225,7 @@ { char *users = lp_auth_users(module); char challenge[MD4_SUM_LENGTH*2]; - char line[MAXPATHLEN]; + char line[BIGPATHBUFLEN]; char secret[512]; char pass2[MD4_SUM_LENGTH*2]; char *tok, *pass; diff -urN --exclude=patches rsync-2.6.6/backup.c rsync-2.6.7/backup.c --- rsync-2.6.6/backup.c 2005-06-10 10:57:18.000000000 -0700 +++ rsync-2.6.7/backup.c 2006-02-24 08:43:44.000000000 -0800 @@ -21,7 +21,6 @@ #include "rsync.h" extern int verbose; -extern int backup_suffix_len; extern int backup_dir_len; extern unsigned int backup_dir_remainder; extern char backup_dir_buf[MAXPATHLEN]; @@ -30,9 +29,8 @@ extern int am_root; extern int preserve_devices; +extern int preserve_specials; extern int preserve_links; -extern int preserve_hard_links; -extern int orig_umask; extern int safe_symlinks; /* make a complete pathname for backup file */ @@ -65,8 +63,7 @@ if (do_rename(fname, fnamebak) == 0) { if (verbose > 1) { rprintf(FINFO, "backed up %s to %s\n", - safe_fname(fname), - safe_fname(fnamebak)); + fname, fnamebak); } break; } @@ -81,7 +78,7 @@ continue; rsyserr(FERROR, rename_errno, "rename %s to backup %s", - safe_fname(fname), safe_fname(fnamebak)); + fname, fnamebak); errno = rename_errno; return 0; } @@ -112,7 +109,7 @@ } if (*p == '/') { *p = '\0'; - if (do_mkdir(fullpath, 0777 & ~orig_umask) == 0) + if (mkdir_defmode(fullpath) == 0) break; if (errno != ENOENT) { rsyserr(FERROR, errno, @@ -141,7 +138,7 @@ p += strlen(p); if (p == end) break; - if (do_mkdir(fullpath, 0777 & ~orig_umask) < 0) { + if (mkdir_defmode(fullpath) < 0) { rsyserr(FERROR, errno, "make_bak_dir mkdir %s failed", full_fname(fullpath)); goto failure; @@ -149,7 +146,7 @@ } return 0; -failure: + failure: while (p != end) { *p = '/'; p += strlen(p); @@ -160,8 +157,9 @@ /* robustly move a file, creating new directory structures if necessary */ static int robust_move(char *src, char *dst) { - if (robust_rename(src, dst, 0755) < 0 && (errno != ENOENT - || make_bak_dir(dst) < 0 || robust_rename(src, dst, 0755) < 0)) + if (robust_rename(src, dst, NULL, 0755) < 0 + && (errno != ENOENT || make_bak_dir(dst) < 0 + || robust_rename(src, dst, NULL, 0755) < 0)) return -1; return 0; } @@ -181,14 +179,15 @@ if (do_lstat(fname, &st) < 0) return 1; - if (!(file = make_file(fname, NULL, NO_FILTERS))) + if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) return 1; /* the file could have disappeared */ if (!(buf = get_backup_name(fname))) return 0; /* Check to see if this is a device file, or link */ - if (IS_DEVICE(file->mode) && am_root && preserve_devices) { + if ((am_root && preserve_devices && IS_DEVICE(file->mode)) + || (preserve_specials && IS_SPECIAL(file->mode))) { do_unlink(buf); if (do_mknod(buf, file->mode, file->u.rdev) < 0 && (errno != ENOENT || make_bak_dir(buf) < 0 @@ -197,7 +196,7 @@ full_fname(buf)); } else if (verbose > 2) { rprintf(FINFO, "make_backup: DEVICE %s successful.\n", - safe_fname(fname)); + fname); } kept = 1; do_unlink(fname); @@ -235,7 +234,7 @@ || do_symlink(file->u.link, buf) < 0)) { rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), - safe_fname(file->u.link)); + file->u.link); } do_unlink(fname); kept = 1; @@ -245,7 +244,7 @@ if (!kept && !S_ISREG(file->mode)) { rprintf(FINFO, "make_bak: skipping non-regular file %s\n", - safe_fname(fname)); + fname); return 1; } @@ -253,19 +252,19 @@ if (!kept) { if (robust_move(fname, buf) != 0) { rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"", - full_fname(fname), safe_fname(buf)); + 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(buf, file, NULL, 0); + set_file_attrs(buf, file, NULL, 0); free(file); if (verbose > 1) { rprintf(FINFO, "backed up %s to %s\n", - safe_fname(fname), safe_fname(buf)); + fname, buf); } return 1; } diff -urN --exclude=patches rsync-2.6.6/batch.c rsync-2.6.7/batch.c --- rsync-2.6.6/batch.c 2005-04-09 11:59:44.000000000 -0700 +++ rsync-2.6.7/batch.c 2006-01-24 13:40:43.000000000 -0800 @@ -6,9 +6,9 @@ */ #include "rsync.h" +#include "zlib/zlib.h" #include -extern int am_sender; extern int eol_nulls; extern int recurse; extern int xfer_dirs; @@ -19,11 +19,14 @@ extern int preserve_gid; extern int always_checksum; extern int do_compression; +extern int def_compress_level; extern int protocol_version; extern char *batch_name; extern struct filter_list_struct filter_list; +static int tweaked_compress_level; + static int *flag_ptr[] = { &recurse, /* 0 */ &preserve_uid, /* 1 */ @@ -33,7 +36,7 @@ &preserve_hard_links, /* 5 */ &always_checksum, /* 6 */ &xfer_dirs, /* 7 (protocol 29) */ - &do_compression, /* 8 (protocol 29) */ + &tweaked_compress_level,/* 8 (protocol 29) */ NULL }; @@ -54,6 +57,12 @@ { int i, flags; +#if Z_DEFAULT_COMPRESSION == -1 + tweaked_compress_level = do_compression ? def_compress_level + 2 : 0; +#else +#error internal logic error! Fix def_compress_level logic above and below too! +#endif + /* Start the batch file with a bitmap of data-stream-affecting * flags. */ if (protocol_version < 29) @@ -88,6 +97,13 @@ else if (xfer_dirs < 2) xfer_dirs = 0; } + + if (tweaked_compress_level == 0 || tweaked_compress_level == 2) + do_compression = 0; + else { + do_compression = 1; + def_compress_level = tweaked_compress_level - 2; + } } static void write_arg(int fd, char *arg) @@ -148,7 +164,7 @@ S_IRUSR | S_IWUSR | S_IEXEC); if (fd < 0) { rsyserr(FERROR, errno, "Batch file %s open error", - safe_fname(filename)); + filename); exit_cleanup(1); } @@ -194,42 +210,7 @@ write_filter_rules(fd); if (write(fd, "\n", 1) != 1 || close(fd) < 0) { rsyserr(FERROR, errno, "Batch file %s write error", - safe_fname(filename)); + filename); exit_cleanup(1); } } - -void show_flist(int index, struct file_struct **fptr) -{ - /* for debugging show_flist(flist->count, flist->files * */ - - int i; - for (i = 0; i < index; i++) { - rprintf(FINFO, "flist->flags=%#x\n", fptr[i]->flags); - rprintf(FINFO, "flist->modtime=%#lx\n", - (long unsigned) fptr[i]->modtime); - rprintf(FINFO, "flist->length=%.0f\n", - (double) fptr[i]->length); - rprintf(FINFO, "flist->mode=%#o\n", (int) fptr[i]->mode); - rprintf(FINFO, "flist->basename=%s\n", - safe_fname(fptr[i]->basename)); - if (fptr[i]->dirname) { - rprintf(FINFO, "flist->dirname=%s\n", - safe_fname(fptr[i]->dirname)); - } - if (am_sender && fptr[i]->dir.root) { - rprintf(FINFO, "flist->dir.root=%s\n", - safe_fname(fptr[i]->dir.root)); - } - } -} - -/* for debugging */ -void show_argvs(int argc, char *argv[]) -{ - int i; - - rprintf(FINFO, "BATCH.C:show_argvs,argc=%d\n", argc); - for (i = 0; i < argc; i++) - rprintf(FINFO, "i=%d,argv[i]=%s\n", i, safe_fname(argv[i])); -} diff -urN --exclude=patches rsync-2.6.6/chmod.c rsync-2.6.7/chmod.c --- rsync-2.6.6/chmod.c 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.7/chmod.c 2006-02-23 17:56:18.000000000 -0800 @@ -0,0 +1,205 @@ +#include "rsync.h" + +extern mode_t orig_umask; + +#define FLAG_X_KEEP (1<<0) +#define FLAG_DIRS_ONLY (1<<1) +#define FLAG_FILES_ONLY (1<<2) + +struct chmod_mode_struct { + struct chmod_mode_struct *next; + int ModeAND, ModeOR; + char flags; +}; + +#define CHMOD_ADD 1 +#define CHMOD_SUB 2 +#define CHMOD_EQ 3 + +#define STATE_ERROR 0 +#define STATE_1ST_HALF 1 +#define STATE_2ND_HALF 2 + +/* Parse a chmod-style argument, and break it down into one or more AND/OR + * pairs in a linked list. We return a pointer to new items on succcess + * (appending the items to the specified list), or NULL on error. */ +struct chmod_mode_struct *parse_chmod(const char *modestr, + struct chmod_mode_struct **root_mode_ptr) +{ + int state = STATE_1ST_HALF; + int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0; + struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL, + *prev_mode = NULL; + + while (state != STATE_ERROR) { + if (!*modestr || *modestr == ',') { + int bits; + + if (!op) { + state = STATE_ERROR; + break; + } + prev_mode = curr_mode; + curr_mode = new_array(struct chmod_mode_struct, 1); + if (prev_mode) + prev_mode->next = curr_mode; + else + first_mode = curr_mode; + curr_mode->next = NULL; + + if (where) + bits = where * what; + else { + where = 0111; + bits = (where * what) & ~orig_umask; + } + + switch (op) { + case CHMOD_ADD: + curr_mode->ModeAND = CHMOD_BITS; + curr_mode->ModeOR = bits + topoct; + break; + case CHMOD_SUB: + curr_mode->ModeAND = CHMOD_BITS - bits - topoct; + curr_mode->ModeOR = 0; + break; + case CHMOD_EQ: + curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0); + curr_mode->ModeOR = bits + topoct; + break; + } + + curr_mode->flags = flags; + + if (!*modestr) + break; + modestr++; + + state = STATE_1ST_HALF; + where = what = op = topoct = topbits = flags = 0; + } + + if (state != STATE_2ND_HALF) { + switch (*modestr) { + case 'D': + if (flags & FLAG_FILES_ONLY) + state = STATE_ERROR; + flags |= FLAG_DIRS_ONLY; + break; + case 'F': + if (flags & FLAG_DIRS_ONLY) + state = STATE_ERROR; + flags |= FLAG_FILES_ONLY; + break; + case 'u': + where |= 0100; + topbits |= 04000; + break; + case 'g': + where |= 0010; + topbits |= 02000; + break; + case 'o': + where |= 0001; + break; + case 'a': + where |= 0111; + break; + case '+': + op = CHMOD_ADD; + state = STATE_2ND_HALF; + break; + case '-': + op = CHMOD_SUB; + state = STATE_2ND_HALF; + break; + case '=': + op = CHMOD_EQ; + state = STATE_2ND_HALF; + break; + default: + state = STATE_ERROR; + break; + } + } else { + switch (*modestr) { + case 'r': + what |= 4; + break; + case 'w': + what |= 2; + break; + case 'X': + flags |= FLAG_X_KEEP; + /* FALL THROUGH */ + case 'x': + what |= 1; + break; + case 's': + if (topbits) + topoct |= topbits; + else + topoct = 04000; + break; + case 't': + topoct |= 01000; + break; + default: + state = STATE_ERROR; + break; + } + } + modestr++; + } + + if (state == STATE_ERROR) { + free_chmod_mode(first_mode); + return NULL; + } + + if (!(curr_mode = *root_mode_ptr)) + *root_mode_ptr = first_mode; + else { + while (curr_mode->next) + curr_mode = curr_mode->next; + curr_mode->next = first_mode; + } + + return first_mode; +} + + +/* Takes an existing file permission and a list of AND/OR changes, and + * create a new permissions. */ +int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes) +{ + int IsX = mode & 0111; + int NonPerm = mode & ~CHMOD_BITS; + + for ( ; chmod_modes; chmod_modes = chmod_modes->next) { + if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm)) + continue; + if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm)) + continue; + mode &= chmod_modes->ModeAND; + if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm)) + mode |= chmod_modes->ModeOR & ~0111; + else + mode |= chmod_modes->ModeOR; + } + + return mode | NonPerm; +} + +/* Free the linked list created by parse_chmod. */ +int free_chmod_mode(struct chmod_mode_struct *chmod_modes) +{ + struct chmod_mode_struct *next; + + while (chmod_modes) { + next = chmod_modes->next; + free(chmod_modes); + chmod_modes = next; + } + return 0; +} diff -urN --exclude=patches rsync-2.6.6/cleanup.c rsync-2.6.7/cleanup.c --- rsync-2.6.6/cleanup.c 2005-03-05 10:58:38.000000000 -0800 +++ rsync-2.6.7/cleanup.c 2006-02-03 10:46:38.000000000 -0800 @@ -26,6 +26,10 @@ extern int log_got_error; extern char *partial_dir; +#ifdef HAVE_SIGACTION +static struct sigaction sigact; +#endif + /** * Close all open sockets and files, allowing a (somewhat) graceful * shutdown() of socket connections. This eliminates the abortive @@ -94,17 +98,18 @@ } inside_cleanup++; - signal(SIGUSR1, SIG_IGN); - signal(SIGUSR2, SIG_IGN); + SIGACTION(SIGUSR1, SIG_IGN); + SIGACTION(SIGUSR2, SIG_IGN); if (verbose > 3) { rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): entered\n", - code, safe_fname(file), line); + code, file, line); } if (cleanup_child_pid != -1) { int status; - if (waitpid(cleanup_child_pid, &status, WNOHANG) == cleanup_child_pid) { + if (wait_process(cleanup_child_pid, &status, WNOHANG) + == cleanup_child_pid) { status = WEXITSTATUS(status); if (status > code) code = status; @@ -121,8 +126,8 @@ flush_write_file(cleanup_fd_w); close(cleanup_fd_w); } - finish_transfer(cleanup_new_fname, fname, cleanup_file, 0, - !partial_dir); + finish_transfer(cleanup_new_fname, fname, NULL, + cleanup_file, 0, !partial_dir); } io_flush(FULL_FLUSH); if (cleanup_fname) @@ -149,7 +154,7 @@ if (verbose > 2) { rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): about to call exit(%d)\n", - ocode, safe_fname(file), line, code); + ocode, file, line, code); } close_all(); @@ -166,7 +171,7 @@ void cleanup_set(char *fnametmp, char *fname, struct file_struct *file, int fd_r, int fd_w) { - cleanup_fname = fnametmp; + cleanup_fname = fname ? fnametmp : NULL; cleanup_new_fname = fname; cleanup_file = file; cleanup_fd_r = fd_r; diff -urN --exclude=patches rsync-2.6.6/clientname.c rsync-2.6.7/clientname.c --- rsync-2.6.6/clientname.c 2005-02-13 16:53:43.000000000 -0800 +++ rsync-2.6.7/clientname.c 2006-02-24 08:43:44.000000000 -0800 @@ -34,7 +34,6 @@ #include "rsync.h" static const char default_name[] = "UNKNOWN"; -extern int am_daemon; extern int am_server; @@ -321,7 +320,6 @@ return error; } - /* Given all these results, we expect that one of them will be * the same as ss. The comparison is a bit complicated. */ for (res = res0; res; res = res->ai_next) { diff -urN --exclude=patches rsync-2.6.6/clientserver.c rsync-2.6.7/clientserver.c --- rsync-2.6.6/clientserver.c 2005-06-10 09:57:43.000000000 -0700 +++ rsync-2.6.7/clientserver.c 2006-02-23 17:56:26.000000000 -0800 @@ -28,6 +28,7 @@ #include "rsync.h" extern int verbose; +extern int quiet; extern int list_only; extern int am_sender; extern int am_server; @@ -41,23 +42,31 @@ extern int remote_protocol; extern int protocol_version; extern int io_timeout; -extern int orig_umask; extern int no_detach; extern int default_af_hint; +extern mode_t orig_umask; extern char *bind_address; -extern struct filter_list_struct server_filter_list; +extern char *sockopts; extern char *config_file; extern char *files_from; +extern char *tmpdir; +extern struct chmod_mode_struct *chmod_modes; +extern struct filter_list_struct server_filter_list; char *auth_user; int read_only = 0; int daemon_log_format_has_i = 0; int daemon_log_format_has_o_or_i = 0; int module_id = -1; +struct chmod_mode_struct *daemon_chmod_modes; /* Length of lp_path() string when in daemon mode & not chrooted, else 0. */ unsigned int module_dirlen = 0; +#ifdef HAVE_SIGACTION +static struct sigaction sigact; +#endif + /** * Run a client connected to an rsyncd. The alternative to this * function for remote-shell connections is do_cmd(). @@ -97,6 +106,8 @@ if (fd == -1) exit_cleanup(RERR_SOCKETIO); + set_socket_options(fd, sockopts); + ret = start_inband_exchange(user, path, fd, fd, argc); return ret ? ret : client_run(fd, fd, -1, argc, argv); @@ -108,7 +119,7 @@ int i; char *sargs[MAX_ARGS]; int sargc = 0; - char line[MAXPATHLEN]; + char line[BIGPATHBUFLEN]; char *p; if (argc == 0 && !am_sender) @@ -215,24 +226,62 @@ return 0; } +static char *finish_pre_exec(pid_t pid, int fd, char *request, + int argc, char *argv[]) +{ + int j, status = -1; + + if (request) { + write_buf(fd, request, strlen(request)+1); + for (j = 0; j < argc; j++) + write_buf(fd, argv[j], strlen(argv[j])+1); + } + + write_byte(fd, 0); + + close(fd); + + if (wait_process(pid, &status, 0) < 0 + || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { + char *e; + if (asprintf(&e, "pre-xfer exec returned failure (%d)\n", status) < 0) + out_of_memory("finish_pre_exec"); + return e; + } + return NULL; +} + +static int read_arg_from_pipe(int fd, char *buf, int limit) +{ + char *bp = buf, *eob = buf + limit - 1; + while (1) { + if (read(fd, bp, 1) != 1) + return -1; + if (*bp == '\0') + break; + if (bp < eob) + bp++; + } + *bp = '\0'; + + return bp - buf; +} -static int rsync_module(int f_in, int f_out, int i) +static int rsync_module(int f_in, int f_out, int i, char *addr, char *host) { int argc = 0; int maxargs; char **argv; - char **argp; - char line[MAXPATHLEN]; + char line[BIGPATHBUFLEN]; uid_t uid = (uid_t)-2; /* canonically "nobody" */ gid_t gid = (gid_t)-2; - char *p; - char *addr = client_addr(f_in); - char *host = client_name(f_in); + char *p, *err_msg = NULL; char *name = lp_name(i); int use_chroot = lp_use_chroot(i); int start_glob = 0; - int ret; + int ret, pre_exec_fd = -1; + pid_t pre_exec_pid = 0; char *request = NULL; if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) { @@ -256,7 +305,7 @@ if (!claim_connection(lp_lock_file(i), lp_max_connections(i))) { if (errno) { rsyserr(FLOG, errno, "failed to open lock file %s", - safe_fname(lp_lock_file(i))); + lp_lock_file(i)); io_printf(f_out, "@ERROR: failed to open lock file\n"); } else { rprintf(FLOG, "max connections (%d) reached\n", @@ -326,27 +375,118 @@ p = lp_filter(i); parse_rule(&server_filter_list, p, MATCHFLG_WORD_SPLIT, - XFLG_ANCHORED2ABS); + XFLG_ABS_IF_SLASH); p = lp_include_from(i); parse_filter_file(&server_filter_list, p, MATCHFLG_INCLUDE, - XFLG_ANCHORED2ABS | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS); + XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS); p = lp_include(i); parse_rule(&server_filter_list, p, MATCHFLG_INCLUDE | MATCHFLG_WORD_SPLIT, - XFLG_ANCHORED2ABS | XFLG_OLD_PREFIXES); + XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES); p = lp_exclude_from(i); parse_filter_file(&server_filter_list, p, 0, - XFLG_ANCHORED2ABS | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS); + XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS); p = lp_exclude(i); parse_rule(&server_filter_list, p, MATCHFLG_WORD_SPLIT, - XFLG_ANCHORED2ABS | XFLG_OLD_PREFIXES); + XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES); log_init(); +#ifdef HAVE_PUTENV + if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) { + char *modname, *modpath, *hostaddr, *hostname, *username; + int status; + if (asprintf(&modname, "RSYNC_MODULE_NAME=%s", name) < 0 + || asprintf(&modpath, "RSYNC_MODULE_PATH=%s", lp_path(i)) < 0 + || asprintf(&hostaddr, "RSYNC_HOST_ADDR=%s", addr) < 0 + || asprintf(&hostname, "RSYNC_HOST_NAME=%s", host) < 0 + || asprintf(&username, "RSYNC_USER_NAME=%s", auth_user) < 0) + out_of_memory("rsync_module"); + putenv(modname); + putenv(modpath); + putenv(hostaddr); + putenv(hostname); + putenv(username); + umask(orig_umask); + /* For post-xfer exec, fork a new process to run the rsync + * daemon while this process waits for the exit status and + * runs the indicated command at that point. */ + if (*lp_postxfer_exec(i)) { + pid_t pid = fork(); + if (pid < 0) { + rsyserr(FLOG, errno, "fork failed"); + io_printf(f_out, "@ERROR: fork failed\n"); + return -1; + } + if (pid) { + char *ret1, *ret2; + if (wait_process(pid, &status, 0) < 0) + status = -1; + if (asprintf(&ret1, "RSYNC_RAW_STATUS=%d", status) > 0) + putenv(ret1); + if (WIFEXITED(status)) + status = WEXITSTATUS(status); + else + status = -1; + if (asprintf(&ret2, "RSYNC_EXIT_STATUS=%d", status) > 0) + putenv(ret2); + system(lp_postxfer_exec(i)); + _exit(status); + } + } + /* For pre-xfer exec, fork a child process to run the indicated + * command, though it first waits for the parent process to + * send us the user's request via a pipe. */ + if (*lp_prexfer_exec(i)) { + int fds[2]; + if (pipe(fds) < 0 || (pre_exec_pid = fork()) < 0) { + rsyserr(FLOG, errno, "pre-xfer exec preparation failed"); + io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n"); + return -1; + } + if (pre_exec_pid == 0) { + char buf[BIGPATHBUFLEN]; + int j, len; + close(fds[1]); + set_blocking(fds[0]); + len = read_arg_from_pipe(fds[0], buf, BIGPATHBUFLEN); + if (len <= 0) + _exit(1); + if (asprintf(&p, "RSYNC_REQUEST=%s", buf) < 0) + out_of_memory("rsync_module"); + putenv(p); + for (j = 0; ; j++) { + len = read_arg_from_pipe(fds[0], buf, + BIGPATHBUFLEN); + if (len <= 0) { + if (!len) + break; + _exit(1); + } + if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) < 0) + out_of_memory("rsync_module"); + putenv(p); + } + close(fds[0]); + close(STDIN_FILENO); + close(STDOUT_FILENO); + status = system(lp_prexfer_exec(i)); + if (!WIFEXITED(status)) + _exit(1); + _exit(WEXITSTATUS(status)); + } + close(fds[0]); + set_blocking(fds[1]); + pre_exec_fd = fds[1]; + } + umask(0); + } +#endif + if (use_chroot) { /* * XXX: The 'use chroot' flag is a fairly reliable @@ -362,14 +502,14 @@ */ if (chroot(lp_path(i))) { rsyserr(FLOG, errno, "chroot %s failed", - safe_fname(lp_path(i))); + lp_path(i)); io_printf(f_out, "@ERROR: chroot failed\n"); return -1; } if (!push_dir("/")) { rsyserr(FLOG, errno, "chdir %s failed\n", - safe_fname(lp_path(i))); + lp_path(i)); io_printf(f_out, "@ERROR: chdir failed\n"); return -1; } @@ -377,7 +517,7 @@ } else { if (!push_dir(lp_path(i))) { rsyserr(FLOG, errno, "chdir %s failed\n", - safe_fname(lp_path(i))); + lp_path(i)); io_printf(f_out, "@ERROR: chdir failed\n"); return -1; } @@ -419,6 +559,16 @@ am_root = (MY_UID() == 0); } + if (lp_temp_dir(i) && *lp_temp_dir(i)) { + tmpdir = lp_temp_dir(i); + if (strlen(tmpdir) >= MAXPATHLEN - 10) { + rprintf(FLOG, + "the 'temp dir' value for %s is WAY too long -- ignoring.\n", + name); + tmpdir = NULL; + } + } + io_printf(f_out, "@RSYNCD: OK\n"); maxargs = MAX_ARGS; @@ -443,22 +593,37 @@ 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, &maxargs); - } else + switch (start_glob) { + case 0: argc++; + if (strcmp(line, ".") == 0) + start_glob = 1; + break; + case 1: + if (pre_exec_pid) { + err_msg = finish_pre_exec(pre_exec_pid, + pre_exec_fd, p, + argc, argv); + pre_exec_pid = 0; + } + request = strdup(p); + start_glob = 2; + /* FALL THROUGH */ + default: + if (!err_msg) + glob_expand(name, &argv, &argc, &maxargs); + break; + } + } - if (strcmp(line, ".") == 0) - start_glob = 1; + if (pre_exec_pid) { + err_msg = finish_pre_exec(pre_exec_pid, pre_exec_fd, request, + argc, argv); } verbose = 0; /* future verbosity is controlled by client options */ - argp = argv; - ret = parse_arguments(&argc, (const char ***) &argp, 0); + ret = parse_arguments(&argc, (const char ***) &argv, 0); + quiet = 0; /* Don't let someone try to be tricky. */ if (filesfrom_fd == 0) filesfrom_fd = f_in; @@ -485,7 +650,7 @@ if (protocol_version < 23 && (protocol_version == 22 || am_sender)) io_start_multiplex_out(); - else if (!ret) { + else if (!ret || err_msg) { /* 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. */ @@ -511,8 +676,11 @@ io_start_multiplex_out(); } - if (!ret) { - option_error(); + if (!ret || err_msg) { + if (err_msg) + rprintf(FERROR, err_msg); + else + option_error(); msleep(400); exit_cleanup(RERR_UNSUPPORTED); } @@ -520,7 +688,20 @@ if (lp_timeout(i) && lp_timeout(i) > io_timeout) set_io_timeout(lp_timeout(i)); - start_server(f_in, f_out, argc, argp); + /* If we have some incoming/outgoing chmod changes, append them to + * any user-specified changes (making our changes have priority). + * We also get a pointer to just our changes so that a receiver + * process can use them separately if --perms wasn't specified. */ + if (am_sender) + p = lp_outgoing_chmod(i); + else + p = lp_incoming_chmod(i); + if (*p && !(daemon_chmod_modes = parse_chmod(p, &chmod_modes))) { + rprintf(FLOG, "Invalid \"%sing chmod\" directive: %s\n", + am_sender ? "outgo" : "incom", p); + } + + start_server(f_in, f_out, argc, argv); return 0; } @@ -546,10 +727,14 @@ here */ int start_daemon(int f_in, int f_out) { - char line[200]; + char line[1024]; char *motd; + char *addr = client_addr(f_in); + char *host = client_name(f_in); int i; + rprintf(FLOG, "connect from %s (%s)\n", host, addr); + io_set_sock_fds(f_in, f_out); if (!lp_load(config_file, 0)) @@ -559,7 +744,10 @@ if (!am_server) { set_socket_options(f_in, "SO_KEEPALIVE"); - set_socket_options(f_in, lp_socket_options()); + if (sockopts) + set_socket_options(f_in, sockopts); + else + set_socket_options(f_in, lp_socket_options()); set_nonblocking(f_in); } @@ -595,6 +783,8 @@ return -1; if (!*line || strcmp(line, "#list") == 0) { + rprintf(FLOG, "module-list request from %s (%s)\n", + host, addr); send_listing(f_out); return -1; } @@ -606,17 +796,19 @@ } 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); -} +#ifdef HAVE_SIGACTION + sigact.sa_flags = SA_NOCLDSTOP; +#endif + SIGACTION(SIGCHLD, remember_children); + return rsync_module(f_in, f_out, i, addr, host); +} int daemon_main(void) { @@ -664,7 +856,7 @@ 0666 & ~orig_umask)) == -1) { cleanup_set_pid(0); rsyserr(FLOG, errno, "failed to create pid file %s", - safe_fname(pid_file)); + pid_file); exit_cleanup(RERR_FILEIO); } snprintf(pidbuf, sizeof pidbuf, "%ld\n", (long)pid); diff -urN --exclude=patches rsync-2.6.6/compat.c rsync-2.6.7/compat.c --- rsync-2.6.6/compat.c 2005-03-09 10:53:55.000000000 -0800 +++ rsync-2.6.7/compat.c 2006-02-24 08:43:44.000000000 -0800 @@ -29,12 +29,12 @@ extern int verbose; extern int am_server; -extern int am_sender; extern int inplace; extern int fuzzy_basis; extern int read_batch; extern int checksum_seed; extern int basis_dir_cnt; +extern int prune_empty_dirs; extern int protocol_version; extern char *dest_option; @@ -78,25 +78,38 @@ exit_cleanup(RERR_PROTOCOL); } - if (fuzzy_basis && protocol_version < 29) { - rprintf(FERROR, - "--fuzzy requires protocol 29 or higher (negotiated %d).\n", - protocol_version); - exit_cleanup(RERR_PROTOCOL); - } - - if (basis_dir_cnt && inplace && protocol_version < 29) { - rprintf(FERROR, - "%s with --inplace requires protocol 29 or higher (negotiated %d).\n", - dest_option, protocol_version); - exit_cleanup(RERR_PROTOCOL); - } - - if (basis_dir_cnt > 1 && protocol_version < 29) { - rprintf(FERROR, - "Multiple %s options requires protocol 29 or higher (negotiated %d).\n", - dest_option, protocol_version); - exit_cleanup(RERR_PROTOCOL); + if (protocol_version < 29) { + if (fuzzy_basis) { + rprintf(FERROR, + "--fuzzy requires protocol 29 or higher" + " (negotiated %d).\n", + protocol_version); + exit_cleanup(RERR_PROTOCOL); + } + + if (basis_dir_cnt && inplace) { + rprintf(FERROR, + "%s with --inplace requires protocol 29 or higher" + " (negotiated %d).\n", + dest_option, protocol_version); + exit_cleanup(RERR_PROTOCOL); + } + + if (basis_dir_cnt > 1) { + rprintf(FERROR, + "Using more than one %s option requires protocol" + " 29 or higher (negotiated %d).\n", + dest_option, protocol_version); + exit_cleanup(RERR_PROTOCOL); + } + + if (prune_empty_dirs) { + rprintf(FERROR, + "--prune-empty-dirs requires protocol 29 or higher" + " (negotiated %d).\n", + protocol_version); + exit_cleanup(RERR_PROTOCOL); + } } if (am_server) { diff -urN --exclude=patches rsync-2.6.6/config.h.in rsync-2.6.7/config.h.in --- rsync-2.6.6/config.h.in 2005-06-10 09:47:15.000000000 -0700 +++ rsync-2.6.7/config.h.in 2006-03-11 10:25:02.000000000 -0800 @@ -113,6 +113,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_GRP_H +/* Define to 1 if you have the header file. */ +#undef HAVE_ICONV_H + +/* Define to 1 if you have the `iconv_open' function. */ +#undef HAVE_ICONV_OPEN + /* Define to 1 if you have the `inet_ntop' function. */ #undef HAVE_INET_NTOP @@ -122,9 +128,18 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LANGINFO_H + +/* Define to 1 if you have the `lchmod' function. */ +#undef HAVE_LCHMOD + /* Define to 1 if you have the `lchown' function. */ #undef HAVE_LCHOWN +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBCHARSET_H + /* Define to 1 if you have the `inet' library (-linet). */ #undef HAVE_LIBINET @@ -143,9 +158,15 @@ /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + /* Define to 1 if you have the `link' function. */ #undef HAVE_LINK +/* Define to 1 if you have the `locale_charset' function. */ +#undef HAVE_LOCALE_CHARSET + /* Define to 1 if you have the header file. */ #undef HAVE_LOCALE_H @@ -156,6 +177,9 @@ /* Define to 1 if you have the `lseek64' function. */ #undef HAVE_LSEEK64 +/* Define to 1 if you have the `lutimes' function. */ +#undef HAVE_LUTIMES + /* Define to 1 if you have the `mallinfo' function. */ #undef HAVE_MALLINFO @@ -189,9 +213,15 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H +/* Define to 1 if you have the `nl_langinfo' function. */ +#undef HAVE_NL_LANGINFO + /* Define to 1 if you have the `open64' function. */ #undef HAVE_OPEN64 +/* Define to 1 if you have the `putenv' function. */ +#undef HAVE_PUTENV + /* Define to 1 if you have the `readlink' function. */ #undef HAVE_READLINK @@ -213,6 +243,12 @@ /* Define to 1 if you have the `setsid' function. */ #undef HAVE_SETSID +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the `sigprocmask' function. */ +#undef HAVE_SIGPROCMASK + /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF diff -urN --exclude=patches rsync-2.6.6/configure rsync-2.6.7/configure --- rsync-2.6.6/configure 2005-07-28 12:31:05.000000000 -0700 +++ rsync-2.6.7/configure 2006-03-11 10:25:04.000000000 -0800 @@ -845,9 +845,8 @@ Optional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-debug including debugging symbols and features (default - yes) - --enable-profile turn on CPU profiling (default no) + --disable-debug turn off debugging symbols and features + --enable-profile turn on CPU profiling --enable-maintainer-mode turn on extra debug features --disable-largefile omit support for large files @@ -1315,7 +1314,7 @@ -RSYNC_VERSION=2.6.6 +RSYNC_VERSION=2.6.7 { echo "$as_me:$LINENO: Configuring rsync $RSYNC_VERSION" >&5 echo "$as_me: Configuring rsync $RSYNC_VERSION" >&6;} @@ -2697,8 +2696,7 @@ _ACEOF -if test "x$ac_cv_prog_cc_stdc" = xno -then +if test x"$ac_cv_prog_cc_stdc" = x"no"; then { echo "$as_me:$LINENO: WARNING: rsync requires an ANSI C compiler and you don't seem to have one" >&5 echo "$as_me: WARNING: rsync requires an ANSI C compiler and you don't seem to have one" >&2;} fi @@ -2716,8 +2714,7 @@ fi; -if test x"$enable_debug" = x"no" -then +if test x"$enable_debug" = x"no"; then echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 CFLAGS=${CFLAGS-"-O"} @@ -2733,8 +2730,7 @@ enableval="$enable_profile" fi; -if test x"$enable_profile" = xyes -then +if test x"$enable_profile" = x"yes"; then CFLAGS="$CFLAGS -pg" fi @@ -2745,8 +2741,7 @@ enableval="$enable_maintainer_mode" fi; -if test x"$enable_maintainer_mode" = xyes -then +if test x"$enable_maintainer_mode" = x"yes"; then CFLAGS="$CFLAGS -DMAINTAINER_MODE" fi @@ -2756,8 +2751,7 @@ CFLAGS="$CFLAGS -DHAVE_CONFIG_H" # If GCC, turn on warnings. -if test x"$GCC" = x"yes" -then +if test x"$GCC" = x"yes"; then CFLAGS="$CFLAGS -Wall -W" fi @@ -2865,8 +2859,7 @@ fi -if test x"$with_rsh" != x -then +if test x"$with_rsh" != x; then RSYNC_RSH="$with_rsh" else RSYNC_RSH="ssh" @@ -2899,7 +2892,7 @@ _ACEOF -# arrgh. libc in the current debian stable screws up the largefile +# arrgh. libc in some old debian version screwed up the largefile # stuff, getting byte range locking wrong echo "$as_me:$LINENO: checking for broken largefile support" >&5 @@ -3346,28 +3339,7 @@ enableval="$enable_ipv6" fi; - - - -# Check whether --enable-locale or --disable-locale was given. -if test "${enable_locale+set}" = set; then - enableval="$enable_locale" - if test x$enableval = xyes; then - cat >>confdefs.h <<\_ACEOF -#define CONFIG_LOCALE 1 -_ACEOF - -fi -else - cat >>confdefs.h <<\_ACEOF -#define CONFIG_LOCALE 1 -_ACEOF - - -fi; - -if test "x$enable_ipv6" != xno -then +if test x"$enable_ipv6" != x"no"; then echo "$as_me:$LINENO: checking ipv6 stack type" >&5 echo $ECHO_N "checking ipv6 stack type... $ECHO_C" >&6 for i in inria kame linux-glibc linux-inet6 toshiba v6d zeta; do @@ -3682,6 +3654,20 @@ fi +# Check whether --enable-locale or --disable-locale was given. +if test "${enable_locale+set}" = set; then + enableval="$enable_locale" + +fi; + + +if test x"$enable_locale" != x"no"; then + cat >>confdefs.h <<\_ACEOF +#define CONFIG_LOCALE 1 +_ACEOF + +fi + echo "$as_me:$LINENO: checking whether to call shutdown on all sockets" >&5 echo $ECHO_N "checking whether to call shutdown on all sockets... $ECHO_C" >&6 case $host_os in @@ -4646,11 +4632,15 @@ + + + + for ac_header in sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \ unistd.h utime.h grp.h compat.h sys/param.h ctype.h sys/wait.h \ sys/ioctl.h sys/filio.h string.h stdlib.h sys/socket.h sys/mode.h \ sys/un.h glob.h mcheck.h arpa/inet.h arpa/nameser.h locale.h \ - netdb.h malloc.h float.h + netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -8921,14 +8911,13 @@ fi fi - -echo "$as_me:$LINENO: checking for inet_ntop in -lresolv" >&5 -echo $ECHO_N "checking for inet_ntop in -lresolv... $ECHO_C" >&6 -if test "${ac_cv_lib_resolv_inet_ntop+set}" = set; then +echo "$as_me:$LINENO: checking for library containing inet_ntop" >&5 +echo $ECHO_N "checking for library containing inet_ntop... $ECHO_C" >&6 +if test "${ac_cv_search_inet_ntop+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lresolv $LIBS" + ac_func_search_save_LIBS=$LIBS +ac_cv_search_inet_ntop=no cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF @@ -8972,25 +8961,204 @@ ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ac_cv_lib_resolv_inet_ntop=yes + ac_cv_search_inet_ntop="none required" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -ac_cv_lib_resolv_inet_ntop=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +if test "$ac_cv_search_inet_ntop" = no; then + for ac_lib in resolv; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char inet_ntop (); +int +main () +{ +inet_ntop (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_inet_ntop="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + fi -echo "$as_me:$LINENO: result: $ac_cv_lib_resolv_inet_ntop" >&5 -echo "${ECHO_T}$ac_cv_lib_resolv_inet_ntop" >&6 -if test $ac_cv_lib_resolv_inet_ntop = yes; then - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBRESOLV 1 +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_inet_ntop" >&5 +echo "${ECHO_T}$ac_cv_search_inet_ntop" >&6 +if test "$ac_cv_search_inet_ntop" != no; then + test "$ac_cv_search_inet_ntop" = "none required" || LIBS="$ac_cv_search_inet_ntop $LIBS" + +fi + + +# Solaris and HP-UX weirdness: +# Search for libiconv_open (not iconv_open) to discover if -liconv is needed! +echo "$as_me:$LINENO: checking for library containing libiconv_open" >&5 +echo $ECHO_N "checking for library containing libiconv_open... $ECHO_C" >&6 +if test "${ac_cv_search_libiconv_open+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_libiconv_open=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ _ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ - LIBS="-lresolv $LIBS" +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char libiconv_open (); +int +main () +{ +libiconv_open (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_libiconv_open="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_libiconv_open" = no; then + for ac_lib in iconv; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char libiconv_open (); +int +main () +{ +libiconv_open (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_libiconv_open="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_libiconv_open" >&5 +echo "${ECHO_T}$ac_cv_search_libiconv_open" >&6 +if test "$ac_cv_search_libiconv_open" != no; then + test "$ac_cv_search_libiconv_open" = "none required" || LIBS="$ac_cv_search_libiconv_open $LIBS" fi @@ -10787,11 +10955,21 @@ -for ac_func in waitpid wait4 getcwd strdup strerror chown chmod mknod mkfifo \ - fchmod fstat strchr readlink link utime utimes strftime mtrace ftruncate \ + + + + + + + + +for ac_func in waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \ + fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \ memmove lchown vsnprintf snprintf vasprintf asprintf setsid glob strpbrk \ strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \ - setlocale setmode open64 lseek64 mkstemp64 va_copy __va_copy + setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \ + strerror putenv iconv_open locale_charset nl_langinfo \ + sigaction sigprocmask do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 @@ -11179,8 +11357,7 @@ fi -if test x"$with_included_popt" != x"yes" -then +if test x"$with_included_popt" != x"yes"; then echo "$as_me:$LINENO: checking for poptGetContext in -lpopt" >&5 echo $ECHO_N "checking for poptGetContext in -lpopt... $ECHO_C" >&6 @@ -11260,8 +11437,7 @@ echo "$as_me:$LINENO: checking whether to use included libpopt" >&5 echo $ECHO_N "checking whether to use included libpopt... $ECHO_C" >&6 -if test x"$with_included_popt" = x"yes" -then +if test x"$with_included_popt" = x"yes"; then echo "$as_me:$LINENO: result: $srcdir/popt" >&5 echo "${ECHO_T}$srcdir/popt" >&6 BUILD_POPT='$(popt_OBJS)' @@ -11794,7 +11970,7 @@ #include main() { int rc, ec; char *fn = "fifo-test"; unlink(fn); rc = mknod(fn,S_IFIFO,0600); ec = errno; unlink(fn); -if (rc) {printf("%d %d\n",rc,ec); return ec;} +if (rc) {printf("(%d %d) ",rc,ec); return ec;} return 0;} _ACEOF rm -f conftest$ac_exeext @@ -11851,7 +12027,7 @@ #include main() { int rc, ec; char *fn = "sock-test"; unlink(fn); rc = mknod(fn,S_IFSOCK,0600); ec = errno; unlink(fn); -if (rc) {printf("%d %d\n",rc,ec); return ec;} +if (rc) {printf("(%d %d) ",rc,ec); return ec;} return 0;} _ACEOF rm -f conftest$ac_exeext diff -urN --exclude=patches rsync-2.6.6/configure.in rsync-2.6.7/configure.in --- rsync-2.6.6/configure.in 2005-07-28 12:31:05.000000000 -0700 +++ rsync-2.6.7/configure.in 2006-03-11 10:25:04.000000000 -0800 @@ -5,7 +5,7 @@ AC_CONFIG_HEADER(config.h) AC_PREREQ(2.59) -RSYNC_VERSION=2.6.6 +RSYNC_VERSION=2.6.7 AC_SUBST(RSYNC_VERSION) AC_MSG_NOTICE([Configuring rsync $RSYNC_VERSION]) @@ -26,8 +26,7 @@ AC_DEFINE([_GNU_SOURCE], 1, [Define _GNU_SOURCE so that we get all necessary prototypes]) -if test "x$ac_cv_prog_cc_stdc" = xno -then +if test x"$ac_cv_prog_cc_stdc" = x"no"; then AC_MSG_WARN([rsync requires an ANSI C compiler and you don't seem to have one]) fi @@ -38,12 +37,10 @@ AC_MSG_CHECKING([whether to include debugging symbols]) AC_ARG_ENABLE(debug, - AC_HELP_STRING([--enable-debug], - [including debugging symbols and features (default yes)]), - [], []) + AC_HELP_STRING([--disable-debug], + [turn off debugging symbols and features])) -if test x"$enable_debug" = x"no" -then +if test x"$enable_debug" = x"no"; then AC_MSG_RESULT(no) CFLAGS=${CFLAGS-"-O"} else @@ -56,10 +53,8 @@ AC_ARG_ENABLE(profile, AC_HELP_STRING([--enable-profile], - [turn on CPU profiling (default no)], - [], [])) -if test x"$enable_profile" = xyes -then + [turn on CPU profiling])) +if test x"$enable_profile" = x"yes"; then CFLAGS="$CFLAGS -pg" fi @@ -67,10 +62,8 @@ # Specifically, this turns on panic_action handling. AC_ARG_ENABLE(maintainer-mode, AC_HELP_STRING([--enable-maintainer-mode], - [turn on extra debug features], - [], [])) -if test x"$enable_maintainer_mode" = xyes -then + [turn on extra debug features])) +if test x"$enable_maintainer_mode" = x"yes"; then CFLAGS="$CFLAGS -DMAINTAINER_MODE" fi @@ -80,16 +73,15 @@ CFLAGS="$CFLAGS -DHAVE_CONFIG_H" # If GCC, turn on warnings. -if test x"$GCC" = x"yes" -then +if test x"$GCC" = x"yes"; then CFLAGS="$CFLAGS -Wall -W" fi AC_ARG_WITH(included-popt, - [ --with-included-popt use bundled popt library, not from system]) + AC_HELP_STRING([--with-included-popt], [use bundled popt library, not from system])) AC_ARG_WITH(rsync-path, - [ --with-rsync-path=PATH set default --rsync-path to PATH (default: rsync)], + AC_HELP_STRING([--with-rsync-path=PATH], [set default --rsync-path to PATH (default: rsync)]), [ RSYNC_PATH="$with_rsync_path" ], [ RSYNC_PATH="rsync" ]) @@ -124,8 +116,7 @@ AC_DEFINE(HAVE_REMSH, 1, [Define to 1 if remote shell is remsh, not rsh]) fi -if test x"$with_rsh" != x -then +if test x"$with_rsh" != x; then RSYNC_RSH="$with_rsh" else RSYNC_RSH="ssh" @@ -144,7 +135,7 @@ AC_DEFINE_UNQUOTED(NOBODY_USER, "nobody", [unprivileged user--e.g. nobody]) AC_DEFINE_UNQUOTED(NOBODY_GROUP, "$NOBODY_GROUP", [unprivileged group for unprivileged user]) -# arrgh. libc in the current debian stable screws up the largefile +# arrgh. libc in some old debian version screwed up the largefile # stuff, getting byte range locking wrong AC_CACHE_CHECK([for broken largefile support],rsync_cv_HAVE_BROKEN_LARGEFILE,[ AC_TRY_RUN([ @@ -190,21 +181,9 @@ ipv6trylibc=yes AC_ARG_ENABLE(ipv6, - AC_HELP_STRING([--disable-ipv6], [don't even try to use IPv6])) - -dnl Do you want to disable use of locale functions -AH_TEMPLATE([CONFIG_LOCALE], -[Undefine if you don't want locale features. By default this is defined.]) -AC_ARG_ENABLE([locale], - AC_HELP_STRING([--disable-locale], [turn off locale features]), -[if test x$enableval = xyes; then - AC_DEFINE(CONFIG_LOCALE) -fi], -AC_DEFINE(CONFIG_LOCALE) -) - -if test "x$enable_ipv6" != xno -then + AC_HELP_STRING([--disable-ipv6], + [don't even try to use IPv6])) +if test x"$enable_ipv6" != x"no"; then AC_MSG_CHECKING([ipv6 stack type]) for i in inria kame linux-glibc linux-inet6 toshiba v6d zeta; do case $i in @@ -293,6 +272,16 @@ AC_SEARCH_LIBS(getaddrinfo, inet6) fi +dnl Do you want to disable use of locale functions +AC_ARG_ENABLE([locale], + AC_HELP_STRING([--disable-locale], + [turn off locale features])) +AH_TEMPLATE([CONFIG_LOCALE], +[Undefine if you don't want locale features. By default this is defined.]) +if test x"$enable_locale" != x"no"; then + AC_DEFINE(CONFIG_LOCALE) +fi + AC_MSG_CHECKING([whether to call shutdown on all sockets]) case $host_os in *cygwin* ) AC_MSG_RESULT(yes) @@ -310,7 +299,7 @@ unistd.h utime.h grp.h compat.h sys/param.h ctype.h sys/wait.h \ sys/ioctl.h sys/filio.h string.h stdlib.h sys/socket.h sys/mode.h \ sys/un.h glob.h mcheck.h arpa/inet.h arpa/nameser.h locale.h \ - netdb.h malloc.h float.h) + netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h) AC_HEADER_MAJOR AC_CHECK_SIZEOF(int) @@ -377,7 +366,11 @@ fi fi -AC_CHECK_LIB(resolv, inet_ntop) +AC_SEARCH_LIBS(inet_ntop, resolv) + +# Solaris and HP-UX weirdness: +# Search for libiconv_open (not iconv_open) to discover if -liconv is needed! +AC_SEARCH_LIBS(libiconv_open, iconv) dnl AC_MSG_NOTICE([Looking in libraries: $LIBS]) @@ -496,11 +489,13 @@ 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 ftruncate \ +AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \ + fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \ memmove lchown vsnprintf snprintf vasprintf asprintf setsid glob strpbrk \ strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \ - setlocale setmode open64 lseek64 mkstemp64 va_copy __va_copy) + setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \ + strerror putenv iconv_open locale_charset nl_langinfo \ + sigaction sigprocmask) AC_CHECK_FUNCS(getpgrp tcgetpgrp) if test $ac_cv_func_getpgrp = yes; then @@ -541,14 +536,12 @@ AC_DEFINE(HAVE_SOCKETPAIR, 1, [Define to 1 if you have the "socketpair" function]) fi -if test x"$with_included_popt" != x"yes" -then +if test x"$with_included_popt" != x"yes"; then AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes]) fi AC_MSG_CHECKING([whether to use included libpopt]) -if test x"$with_included_popt" = x"yes" -then +if test x"$with_included_popt" = x"yes"; then AC_MSG_RESULT($srcdir/popt) BUILD_POPT='$(popt_OBJS)' CFLAGS="$CFLAGS -I$srcdir/popt" @@ -693,7 +686,7 @@ #include main() { int rc, ec; char *fn = "fifo-test"; unlink(fn); rc = mknod(fn,S_IFIFO,0600); ec = errno; unlink(fn); -if (rc) {printf("%d %d\n",rc,ec); return ec;} +if (rc) {printf("(%d %d) ",rc,ec); return ec;} return 0;}], rsync_cv_MKNOD_CREATES_FIFOS=yes,rsync_cv_MKNOD_CREATES_FIFOS=no,rsync_cv_MKNOD_CREATES_FIFOS=cross)]) if test x"$rsync_cv_MKNOD_CREATES_FIFOS" = x"yes"; then @@ -707,7 +700,7 @@ #include main() { int rc, ec; char *fn = "sock-test"; unlink(fn); rc = mknod(fn,S_IFSOCK,0600); ec = errno; unlink(fn); -if (rc) {printf("%d %d\n",rc,ec); return ec;} +if (rc) {printf("(%d %d) ",rc,ec); return ec;} return 0;}], rsync_cv_MKNOD_CREATES_SOCKETS=yes,rsync_cv_MKNOD_CREATES_SOCKETS=no,rsync_cv_MKNOD_CREATES_SOCKETS=cross)]) if test x"$rsync_cv_MKNOD_CREATES_SOCKETS" = x"yes"; then diff -urN --exclude=patches rsync-2.6.6/errcode.h rsync-2.6.7/errcode.h --- rsync-2.6.6/errcode.h 2005-04-12 16:04:10.000000000 -0700 +++ rsync-2.6.7/errcode.h 2005-12-16 15:48:28.000000000 -0800 @@ -34,8 +34,11 @@ #define RERR_STREAMIO 12 /* error in rsync protocol data stream */ #define RERR_MESSAGEIO 13 /* errors with program diagnostics */ #define RERR_IPC 14 /* error in IPC code */ +#define RERR_CRASHED 15 /* sibling crashed */ +#define RERR_TERMINATED 16 /* sibling terminated abnormally */ -#define RERR_SIGNAL 20 /* status returned when sent SIGUSR1, SIGINT */ +#define RERR_SIGNAL1 19 /* status returned when sent SIGUSR1 */ +#define RERR_SIGNAL 20 /* status returned when sent SIGINT, SIGTERM, SIGHUP */ #define RERR_WAITCHILD 21 /* some error returned by waitpid() */ #define RERR_MALLOC 22 /* error allocating core memory buffers */ #define RERR_PARTIAL 23 /* partial transfer */ diff -urN --exclude=patches rsync-2.6.6/exclude.c rsync-2.6.7/exclude.c --- rsync-2.6.6/exclude.c 2005-07-07 12:49:14.000000000 -0700 +++ rsync-2.6.7/exclude.c 2006-01-27 16:14:02.000000000 -0800 @@ -34,6 +34,9 @@ extern int recurse; extern int io_error; extern int local_server; +extern int saw_delete_opt; +extern int saw_delete_excluded_opt; +extern int prune_empty_dirs; extern int delete_mode; extern int delete_excluded; extern int cvs_exclude; @@ -46,8 +49,8 @@ extern unsigned int module_dirlen; struct filter_list_struct filter_list = { 0, 0, "" }; -struct filter_list_struct cvs_filter_list = { 0, 0, " [cvsignore]" }; -struct filter_list_struct server_filter_list = { 0, 0, " [server]" }; +struct filter_list_struct cvs_filter_list = { 0, 0, " [global CVS]" }; +struct filter_list_struct server_filter_list = { 0, 0, " [daemon]" }; /* Need room enough for ":MODS " prefix plus some room to grow. */ #define MAX_RULE_PREFIX (16) @@ -133,9 +136,9 @@ listp->debug_type); } - /* This flag also indicates that we're reading a list that + /* These flags also indicate that we're reading a list that * needs to be filtered now, not post-filtered later. */ - if (xflags & XFLG_ANCHORED2ABS) { + if (xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH)) { uint32 mf = mflags & (MATCHFLG_RECEIVER_SIDE|MATCHFLG_SENDER_SIDE); if (am_sender) { if (mf == MATCHFLG_RECEIVER_SIDE) @@ -150,10 +153,14 @@ out_of_memory("add_rule"); memset(ret, 0, sizeof ret[0]); - if (xflags & XFLG_ANCHORED2ABS && *pat == '/' - && !(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE))) { + if (!(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE)) + && ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/') + || (xflags & XFLG_ABS_IF_SLASH && strchr(pat, '/') != NULL))) { mflags |= MATCHFLG_ABS_PATH; - ex_len = dirbuf_len - module_dirlen - 1; + if (*pat == '/') + ex_len = dirbuf_len - module_dirlen - 1; + else + ex_len = 0; } else ex_len = 0; if (!(ret->pattern = new_array(char, ex_len + pat_len + 1))) @@ -170,6 +177,12 @@ /* If the pattern starts with **, note that. */ if (cp == ret->pattern) mflags |= MATCHFLG_WILD2_PREFIX; + /* If the pattern ends with ***, note that. */ + if (pat_len >= 3 + && ret->pattern[pat_len-3] == '*' + && ret->pattern[pat_len-2] == '*' + && ret->pattern[pat_len-1] == '*') + mflags |= MATCHFLG_WILD3_SUFFIX; } } @@ -292,7 +305,7 @@ } if (!sanitize_path(fn, merge_file, r, dirbuf_depth)) { rprintf(FERROR, "merge-file name overflows: %s\n", - safe_fname(merge_file)); + merge_file); return NULL; } } else { @@ -305,8 +318,7 @@ goto done; if (dirbuf_len + fn_len >= MAXPATHLEN) { - rprintf(FERROR, "merge-file name overflows: %s\n", - safe_fname(fn)); + rprintf(FERROR, "merge-file name overflows: %s\n", fn); return NULL; } memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip); @@ -488,70 +500,68 @@ static int rule_matches(char *name, struct filter_struct *ex, int name_is_dir) { - char *p, full_name[MAXPATHLEN]; - int match_start = 0; + int slash_handling, str_cnt = 0, anchored_match = 0; int ret_match = ex->match_flags & MATCHFLG_NEGATE ? 0 : 1; - char *pattern = ex->pattern; + char *p, *pattern = ex->pattern; + const char *strings[16]; /* more than enough */ 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. */ if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) { + /* 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. */ if ((p = strrchr(name,'/')) != NULL) name = p+1; - } - else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/' + } else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/' && curr_dir_len > module_dirlen + 1) { - pathjoin(full_name, sizeof full_name, - curr_dir + module_dirlen + 1, name); - name = full_name; - } - - if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir) + /* If we're matching against an absolute-path pattern, + * we need to prepend our full path info. */ + strings[str_cnt++] = curr_dir + module_dirlen + 1; + strings[str_cnt++] = "/"; + } else if (ex->match_flags & MATCHFLG_WILD2_PREFIX && *name != '/') { + /* Allow "**"+"/" to match at the start of the string. */ + strings[str_cnt++] = "/"; + } + strings[str_cnt++] = name; + if (name_is_dir) { + /* Allow a trailing "/"+"***" to match the directory. */ + if (ex->match_flags & MATCHFLG_WILD3_SUFFIX) + strings[str_cnt++] = "/"; + } else if (ex->match_flags & MATCHFLG_DIRECTORY) return !ret_match; + strings[str_cnt] = NULL; if (*pattern == '/') { - match_start = 1; + anchored_match = 1; pattern++; - if (*name == '/') - name++; + if (strings[0][0] == '/') + strings[0]++; } - if (ex->match_flags & MATCHFLG_WILD) { + if (!anchored_match && ex->u.slash_cnt + && !(ex->match_flags & MATCHFLG_WILD2)) { /* A non-anchored match with an infix slash and no "**" * needs to match the last slash_cnt+1 name elements. */ - if (!match_start && ex->u.slash_cnt - && !(ex->match_flags & MATCHFLG_WILD2)) { - int cnt = ex->u.slash_cnt + 1; - for (p = name + strlen(name) - 1; p >= name; p--) { - if (*p == '/' && !--cnt) - break; - } - name = p+1; - } - if (wildmatch(pattern, name)) + slash_handling = ex->u.slash_cnt + 1; + } else if (!anchored_match && !(ex->match_flags & MATCHFLG_WILD2_PREFIX) + && ex->match_flags & MATCHFLG_WILD2) { + /* A non-anchored match with an infix or trailing "**" (but not + * a prefixed "**") needs to try matching after every slash. */ + slash_handling = -1; + } else { + /* The pattern matches only at the start of the path or name. */ + slash_handling = 0; + } + + if (ex->match_flags & MATCHFLG_WILD) { + if (wildmatch_array(pattern, strings, slash_handling)) return ret_match; - if (ex->match_flags & MATCHFLG_WILD2_PREFIX) { - /* If the **-prefixed pattern has a '/' as the next - * character, then try to match the rest of the - * pattern at the root. */ - if (pattern[2] == '/' && wildmatch(pattern+3, name)) - return ret_match; - } - else if (!match_start && ex->match_flags & MATCHFLG_WILD2) { - /* A non-anchored match with an infix or trailing "**" - * (but not a prefixed "**") needs to try matching - * after every slash. */ - while ((name = strchr(name, '/')) != NULL) { - name++; - if (wildmatch(pattern, name)) - return ret_match; - } - } - } else if (match_start) { + } else if (str_cnt > 1) { + if (litmatch_array(pattern, strings, slash_handling)) + return ret_match; + } else if (anchored_match) { if (strcmp(name,pattern) == 0) return ret_match; } else { @@ -577,11 +587,13 @@ * case we add it back in here. */ if (verbose >= 2) { - rprintf(FINFO, "[%s] %scluding %s %s because of pattern %s%s%s\n", - who_am_i(), - ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex", - name_is_dir ? "directory" : "file", name, ent->pattern, - ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type); + static char *actions[2][2] + = { {"show", "hid"}, {"risk", "protect"} }; + const char *w = who_am_i(); + rprintf(FINFO, "[%s] %sing %s %s because of pattern %s%s%s\n", + w, actions[*w!='s'][!(ent->match_flags&MATCHFLG_INCLUDE)], + name_is_dir ? "directory" : "file", name, ent->pattern, + ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type); } } @@ -827,7 +839,8 @@ len = strlen((char*)s); if (new_mflags & MATCHFLG_CLEAR_LIST) { - if (!(xflags & XFLG_OLD_PREFIXES) && len) { + if (!(mflags & MATCHFLG_NO_PREFIXES) + && !(xflags & XFLG_OLD_PREFIXES) && len) { rprintf(FERROR, "'!' rule has trailing characters: %s\n", p); exit_cleanup(RERR_SYNTAX); @@ -954,7 +967,7 @@ uint32 mflags, int xflags) { FILE *fp; - char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */ + char line[BIGPATHBUFLEN]; char *eob = line + sizeof line - 1; int word_split = mflags & MATCHFLG_WORD_SPLIT; @@ -976,7 +989,7 @@ if (verbose > 2) { rprintf(FINFO, "[%s] parse_filter_file(%s,%x,%x)%s\n", - who_am_i(), safe_fname(fname), mflags, xflags, + who_am_i(), fname, mflags, xflags, fp ? "" : " [not found]"); } @@ -985,7 +998,7 @@ rsyserr(FERROR, errno, "failed to open %sclude file %s", mflags & MATCHFLG_INCLUDE ? "in" : "ex", - safe_fname(fname)); + fname); exit_cleanup(RERR_FILEIO); } return; @@ -997,8 +1010,10 @@ int ch, overflow = 0; while (1) { if ((ch = getc(fp)) == EOF) { - if (ferror(fp) && errno == EINTR) + if (ferror(fp) && errno == EINTR) { + clearerr(fp); continue; + } break; } if (word_split && isspace(ch)) @@ -1135,8 +1150,8 @@ /* This is only called by the client. */ void send_filter_list(int f_out) { - int receiver_wants_list = delete_mode - && (!delete_excluded || protocol_version >= 29); + int receiver_wants_list = prune_empty_dirs + || (delete_mode && (!delete_excluded || protocol_version >= 29)); if (local_server || (am_sender && !receiver_wants_list)) f_out = -1; @@ -1167,10 +1182,11 @@ /* This is only called by the server. */ void recv_filter_list(int f_in) { - char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */ + char line[BIGPATHBUFLEN]; int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES; - int receiver_wants_list = delete_mode - && (!delete_excluded || protocol_version >= 29); + int receiver_wants_list = prune_empty_dirs + || (saw_delete_opt + && (!saw_delete_excluded_opt || protocol_version >= 29)); unsigned int len; if (!local_server && (am_sender || receiver_wants_list)) { diff -urN --exclude=patches rsync-2.6.6/fileio.c rsync-2.6.7/fileio.c --- rsync-2.6.6/fileio.c 2005-02-27 13:12:13.000000000 -0800 +++ rsync-2.6.7/fileio.c 2005-09-21 10:37:41.000000000 -0700 @@ -22,6 +22,10 @@ */ #include "rsync.h" +#ifndef ENODATA +#define ENODATA EAGAIN +#endif + extern int sparse_files; static char last_byte; @@ -217,34 +221,35 @@ rprintf(FERROR, "invalid read_size of %ld in map_ptr\n", (long)read_size); exit_cleanup(RERR_FILEIO); - } else { - if (map->p_fd_offset != read_start) { - OFF_T ret = do_lseek(map->fd, read_start, SEEK_SET); - if (ret != read_start) { - rsyserr(FERROR, errno, - "lseek returned %.0f, not %.0f", - (double)ret, (double)read_start); - exit_cleanup(RERR_FILEIO); - } - map->p_fd_offset = read_start; - } + } - if ((nread=read(map->fd,map->p + read_offset,read_size)) != read_size) { - if (nread < 0) { - nread = 0; - if (!map->status) - map->status = errno; - } - /* the best we can do is zero the buffer - the file - has changed mid transfer! */ - memset(map->p+read_offset+nread, 0, read_size - nread); + if (map->p_fd_offset != read_start) { + OFF_T ret = do_lseek(map->fd, read_start, SEEK_SET); + if (ret != read_start) { + rsyserr(FERROR, errno, "lseek returned %.0f, not %.0f", + (double)ret, (double)read_start); + exit_cleanup(RERR_FILEIO); } - map->p_fd_offset += nread; + map->p_fd_offset = read_start; } - + map->p_fd_offset += read_size; map->p_offset = window_start; map->p_len = window_size; + while (read_size > 0) { + nread = read(map->fd, map->p + read_offset, read_size); + if (nread <= 0) { + if (!map->status) + map->status = nread ? errno : ENODATA; + /* The best we can do is zero the buffer -- the file + * has changed mid transfer! */ + memset(map->p + read_offset, 0, read_size); + break; + } + read_offset += nread; + read_size -= nread; + } + return map->p; } diff -urN --exclude=patches rsync-2.6.6/flist.c rsync-2.6.7/flist.c --- rsync-2.6.6/flist.c 2005-07-07 12:49:14.000000000 -0700 +++ rsync-2.6.7/flist.c 2006-02-24 09:54:35.000000000 -0800 @@ -28,7 +28,6 @@ #include "rsync.h" extern int verbose; -extern int dry_run; extern int list_only; extern int am_root; extern int am_server; @@ -43,35 +42,39 @@ extern int xfer_dirs; extern int filesfrom_fd; extern int one_file_system; +extern int copy_dirlinks; extern int keep_dirlinks; extern int preserve_links; extern int preserve_hard_links; -extern int preserve_perms; extern int preserve_devices; +extern int preserve_specials; extern int preserve_uid; extern int preserve_gid; extern int relative_paths; extern int implied_dirs; +extern int prune_empty_dirs; extern int copy_links; extern int copy_unsafe_links; extern int protocol_version; extern int sanitize_paths; -extern int orig_umask; +extern const char *io_write_phase; extern struct stats stats; extern struct file_list *the_file_list; extern char curr_dir[MAXPATHLEN]; +extern struct chmod_mode_struct *chmod_modes; + extern struct filter_list_struct filter_list; extern struct filter_list_struct server_filter_list; int io_error; +int checksum_len; dev_t filesystem_dev; /* used to implement -x */ +unsigned int file_struct_len; static char empty_sum[MD4_SUM_LENGTH]; static int flist_count_offset; -static unsigned int file_struct_len; -static struct file_list *sorting_flist; static void clean_flist(struct file_list *flist, int strip_root, int no_dups); static void output_flist(struct file_list *flist); @@ -82,9 +85,9 @@ /* Figure out how big the file_struct is without trailing padding */ file_struct_len = offsetof(struct file_struct, flags) + sizeof f.flags; + checksum_len = protocol_version < 21 ? 2 : MD4_SUM_LENGTH; } - static int show_filelist_p(void) { return verbose && xfer_dirs && !am_server; @@ -98,20 +101,17 @@ rflush(FINFO); } - static void emit_filelist_progress(int count) { rprintf(FINFO, " %d files...\r", count); } - static void maybe_emit_filelist_progress(int count) { if (do_progress && show_filelist_p() && (count % 100) == 0) emit_filelist_progress(count); } - static void finish_filelist_progress(const struct file_list *flist) { if (do_progress) { @@ -127,35 +127,33 @@ /* Nothing yet */ } - static void list_file_entry(struct file_struct *f) { - char perms[11]; + char permbuf[PERMSTRING_SIZE]; if (!f->basename) { /* this can happen if duplicate names were removed */ return; } - permstring(perms, f->mode); + permstring(permbuf, f->mode); #ifdef SUPPORT_LINKS if (preserve_links && S_ISLNK(f->mode)) { rprintf(FINFO, "%s %11.0f %s %s -> %s\n", - perms, + permbuf, (double)f->length, timestring(f->modtime), - safe_fname(f_name(f)), safe_fname(f->u.link)); + f_name(f, NULL), f->u.link); } else #endif { rprintf(FINFO, "%s %11.0f %s %s\n", - perms, + permbuf, (double)f->length, timestring(f->modtime), - safe_fname(f_name(f))); + f_name(f, NULL)); } } - /** * Stat either a symlink or its referent, depending on the settings of * copy_links, copy_unsafe_links, etc. @@ -175,7 +173,7 @@ #ifdef SUPPORT_LINKS if (copy_links) return do_stat(path, buffer); - if (link_stat(path, buffer, 0) < 0) + if (link_stat(path, buffer, copy_dirlinks) < 0) return -1; if (S_ISLNK(buffer->st_mode)) { int l = readlink((char *)path, linkbuf, MAXPATHLEN - 1); @@ -185,7 +183,7 @@ if (copy_unsafe_links && unsafe_symlink(linkbuf, path)) { if (verbose > 1) { rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n", - safe_fname(path), safe_fname(linkbuf)); + path, linkbuf); } return do_stat(path, buffer); } @@ -261,7 +259,6 @@ return (mode_t)mode; } - static void send_directory(int f, struct file_list *flist, char *fbuf, int len); @@ -310,7 +307,7 @@ out_of_memory("flist_expand"); } -void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) +static void send_file_entry(struct file_struct *file, int f) { unsigned short flags; static time_t modtime; @@ -339,24 +336,22 @@ io_write_phase = "send_file_entry"; - f_name_to(file, fname); + f_name(file, fname); - flags = base_flags; + flags = file->flags & XMIT_TOP_DIR; if (file->mode == mode) flags |= XMIT_SAME_MODE; else mode = file->mode; - if (preserve_devices) { + if ((preserve_devices && IS_DEVICE(mode)) + || (preserve_specials && IS_SPECIAL(mode))) { if (protocol_version < 28) { - if (IS_DEVICE(mode)) { - if (file->u.rdev == rdev) - flags |= XMIT_SAME_RDEV_pre28; - else - rdev = file->u.rdev; - } else - rdev = makedev(0, 0); - } else if (IS_DEVICE(mode)) { + if (file->u.rdev == rdev) + flags |= XMIT_SAME_RDEV_pre28; + else + rdev = file->u.rdev; + } else { rdev = file->u.rdev; if ((uint32)major(rdev) == rdev_major) flags |= XMIT_SAME_RDEV_MAJOR; @@ -365,7 +360,8 @@ if ((uint32)minor(rdev) <= 0xFFu) flags |= XMIT_RDEV_MINOR_IS_SMALL; } - } + } else if (protocol_version < 28) + rdev = makedev(0, 0); if (file->uid == uid) flags |= XMIT_SAME_UID; else @@ -414,10 +410,8 @@ } else write_byte(f, flags); } else { - if (!(flags & 0xFF) && !S_ISDIR(mode)) - flags |= XMIT_TOP_DIR; if (!(flags & 0xFF)) - flags |= XMIT_LONG_NAME; + flags |= S_ISDIR(mode) ? XMIT_LONG_NAME : XMIT_TOP_DIR; write_byte(f, flags); } if (flags & XMIT_SAME_NAME) @@ -443,7 +437,8 @@ add_gid(gid); write_int(f, gid); } - if (preserve_devices && IS_DEVICE(mode)) { + if ((preserve_devices && IS_DEVICE(mode)) + || (preserve_specials && IS_SPECIAL(mode))) { if (protocol_version < 28) { if (!(flags & XMIT_SAME_RDEV_pre28)) write_int(f, (int)rdev); @@ -480,19 +475,15 @@ } #endif - if (always_checksum) { + if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) { char *sum; if (S_ISREG(mode)) sum = file->u.sum; - else if (protocol_version < 28) { + else { /* Prior to 28, we sent a useless set of nulls. */ sum = empty_sum; - } else - sum = NULL; - if (sum) { - write_buf(f, sum, - protocol_version < 21 ? 2 : MD4_SUM_LENGTH); } + write_buf(f, sum, checksum_len); } strlcpy(lastname, fname, MAXPATHLEN); @@ -500,8 +491,6 @@ io_write_phase = "unknown"; } - - static struct file_struct *receive_file_entry(struct file_list *flist, unsigned short flags, int f) { @@ -545,7 +534,7 @@ if (l2 >= MAXPATHLEN - l1) { rprintf(FERROR, "overflow: flags=0x%x l1=%d l2=%d lastname=%s\n", - flags, l1, l2, safe_fname(lastname)); + flags, l1, l2, lastname); overflow_exit("receive_file_entry"); } @@ -581,19 +570,20 @@ if (!(flags & XMIT_SAME_MODE)) mode = from_wire_mode(read_int(f)); + if (chmod_modes && !S_ISLNK(mode)) + mode = tweak_mode(mode, chmod_modes); + if (preserve_uid && !(flags & XMIT_SAME_UID)) uid = (uid_t)read_int(f); if (preserve_gid && !(flags & XMIT_SAME_GID)) gid = (gid_t)read_int(f); - if (preserve_devices) { + if ((preserve_devices && IS_DEVICE(mode)) + || (preserve_specials && IS_SPECIAL(mode))) { if (protocol_version < 28) { - if (IS_DEVICE(mode)) { - if (!(flags & XMIT_SAME_RDEV_pre28)) - rdev = (dev_t)read_int(f); - } else - rdev = makedev(0, 0); - } else if (IS_DEVICE(mode)) { + if (!(flags & XMIT_SAME_RDEV_pre28)) + rdev = (dev_t)read_int(f); + } else { uint32 rdev_minor; if (!(flags & XMIT_SAME_RDEV_MAJOR)) rdev_major = read_int(f); @@ -603,7 +593,8 @@ rdev_minor = read_int(f); rdev = makedev(rdev_major, rdev_minor); } - } + } else if (protocol_version < 28) + rdev = makedev(0, 0); #ifdef SUPPORT_LINKS if (preserve_links && S_ISLNK(mode)) { @@ -628,7 +619,6 @@ memset(bp, 0, file_struct_len); bp += file_struct_len; - file->flags = 0; file->modtime = modtime; file->length = file_length; file->mode = mode; @@ -653,16 +643,17 @@ if (basename_len == 1+1 && *basename == '.') /* +1 for '\0' */ file->dir.depth--; if (flags & XMIT_TOP_DIR) { - in_del_hier = 1; + in_del_hier = recurse; del_hier_name_len = file->dir.depth == 0 ? 0 : l1 + l2; if (relative_paths && del_hier_name_len > 2 - && basename_len == 1+1 && *basename == '.') + && lastname[del_hier_name_len-1] == '.' + && lastname[del_hier_name_len-2] == '/') del_hier_name_len -= 2; file->flags |= FLAG_TOP_DIR | FLAG_DEL_HERE; } else if (in_del_hier) { if (!relative_paths || !del_hier_name_len || (l1 >= del_hier_name_len - && thisname[del_hier_name_len] == '/')) + && lastname[del_hier_name_len] == '/')) file->flags |= FLAG_DEL_HERE; else in_del_hier = 0; @@ -673,7 +664,8 @@ memcpy(bp, basename, basename_len); bp += basename_len; - if (preserve_devices && IS_DEVICE(mode)) + if ((preserve_devices && IS_DEVICE(mode)) + || (preserve_specials && IS_SPECIAL(mode))) file->u.rdev = rdev; #ifdef SUPPORT_LINKS @@ -708,32 +700,21 @@ } #endif - if (always_checksum) { + if (always_checksum && (sum_len || protocol_version < 28)) { char *sum; if (sum_len) { file->u.sum = sum = bp; /*bp += sum_len;*/ - } else if (protocol_version < 28) { + } else { /* Prior to 28, we get a useless set of nulls. */ sum = empty_sum; - } else - sum = NULL; - if (sum) { - read_buf(f, sum, - protocol_version < 21 ? 2 : MD4_SUM_LENGTH); } - } - - if (!preserve_perms) { - /* set an appropriate set of permissions based on original - * permissions and umask. This emulates what GNU cp does */ - file->mode &= ~orig_umask; + read_buf(f, sum, checksum_len); } return file; } - /** * Create a file_struct for a named file by reading its stat() * information and performing extensive checks against global @@ -750,6 +731,7 @@ * important case. Some systems may not have d_type. **/ struct file_struct *make_file(char *fname, struct file_list *flist, + STRUCT_STAT *stp, unsigned short flags, int filter_level) { static char *lastdir; @@ -761,15 +743,13 @@ char linkname[MAXPATHLEN]; int alloc_len, basename_len, dirname_len, linkname_len, sum_len; char *basename, *dirname, *bp; - unsigned short flags = 0; if (!flist || !flist->count) /* Ignore lastdir when invalid. */ lastdir_len = -1; if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname - flist_dir_len) { - rprintf(FINFO, "skipping overly long name: %s\n", - safe_fname(fname)); + rprintf(FINFO, "skipping overly long name: %s\n", fname); return NULL; } clean_fname(thisname, 0); @@ -778,7 +758,9 @@ memset(sum, 0, SUM_LENGTH); - if (readlink_stat(thisname, &st, linkname) != 0) { + if (stp && S_ISDIR(stp->st_mode)) + st = *stp; /* Needed for "symlink/." with --relative. */ + else if (readlink_stat(thisname, &st, linkname) != 0) { int save_errno = errno; /* See if file is excluded before reporting an error. */ if (filter_level != NO_FILTERS @@ -814,7 +796,7 @@ goto skip_filters; if (S_ISDIR(st.st_mode) && !xfer_dirs) { - rprintf(FINFO, "skipping directory %s\n", safe_fname(thisname)); + rprintf(FINFO, "skipping directory %s\n", thisname); return NULL; } @@ -822,8 +804,16 @@ * into a mount-point directory, not to avoid copying a symlinked * file if -L (or similar) was specified. */ if (one_file_system && st.st_dev != filesystem_dev - && S_ISDIR(st.st_mode)) + && S_ISDIR(st.st_mode)) { + if (one_file_system > 1) { + if (verbose > 2) { + rprintf(FINFO, "skipping mount-point dir %s\n", + thisname); + } + return NULL; + } flags |= FLAG_MOUNT_POINT; + } if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) return NULL; @@ -836,11 +826,11 @@ return NULL; } -skip_filters: + skip_filters: if (verbose > 2) { rprintf(FINFO, "[%s] make_file(%s,*,%d)\n", - who_am_i(), safe_fname(thisname), filter_level); + who_am_i(), thisname, filter_level); } if ((basename = strrchr(thisname, '/')) != NULL) { @@ -864,16 +854,16 @@ linkname_len = 0; #endif - sum_len = always_checksum && S_ISREG(st.st_mode) ? MD4_SUM_LENGTH : 0; + sum_len = always_checksum && am_sender && S_ISREG(st.st_mode) + ? MD4_SUM_LENGTH : 0; alloc_len = file_struct_len + dirname_len + basename_len + linkname_len + sum_len; - if (flist) { - bp = pool_alloc(flist->file_pool, alloc_len, - "receive_file_entry"); - } else { + if (flist) + bp = pool_alloc(flist->file_pool, alloc_len, "make_file"); + else { if (!(bp = new_array(char, alloc_len))) - out_of_memory("receive_file_entry"); + out_of_memory("make_file"); } file = (struct file_struct *)bp; @@ -921,7 +911,8 @@ bp += basename_len; #ifdef HAVE_STRUCT_STAT_ST_RDEV - if (preserve_devices && IS_DEVICE(st.st_mode)) + if ((preserve_devices && IS_DEVICE(st.st_mode)) + || (preserve_specials && IS_SPECIAL(st.st_mode))) file->u.rdev = st.st_rdev; #endif @@ -946,7 +937,7 @@ if (keep_dirlinks && linkname_len && flist) { STRUCT_STAT st2; int save_mode = file->mode; - file->mode = S_IFDIR; /* find a directory w/our name */ + file->mode = S_IFDIR; /* Find a directory with our name. */ if (flist_find(the_file_list, file) >= 0 && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) { file->modtime = st2.st_mtime; @@ -965,34 +956,39 @@ return file; } - static struct file_struct *send_file_name(int f, struct file_list *flist, - char *fname, unsigned short base_flags) + char *fname, STRUCT_STAT *stp, + unsigned short flags) { struct file_struct *file; - file = make_file(fname, flist, f == -2 ? SERVER_FILTERS : ALL_FILTERS); + file = make_file(fname, flist, stp, flags, + f == -2 ? SERVER_FILTERS : ALL_FILTERS); if (!file) return NULL; + if (chmod_modes && !S_ISLNK(file->mode)) + file->mode = tweak_mode(file->mode, chmod_modes); + maybe_emit_filelist_progress(flist->count + flist_count_offset); flist_expand(flist); if (file->basename[0]) { flist->files[flist->count++] = file; - send_file_entry(file, f, base_flags); + send_file_entry(file, f); } return file; } static void send_if_directory(int f, struct file_list *flist, - struct file_struct *file) + struct file_struct *file, + char *fbuf, unsigned int ol) { - char fbuf[MAXPATHLEN]; + char is_dot_dir = fbuf[ol-1] == '.' && (ol == 1 || fbuf[ol-2] == '/'); if (S_ISDIR(file->mode) - && !(file->flags & FLAG_MOUNT_POINT) && f_name_to(file, fbuf)) { + && !(file->flags & FLAG_MOUNT_POINT) && f_name(file, fbuf)) { void *save_filters; unsigned int len = strlen(fbuf); if (len > 1 && fbuf[len-1] == '/') @@ -1006,10 +1002,12 @@ save_filters = push_local_filters(fbuf, len); send_directory(f, flist, fbuf, len); pop_local_filters(save_filters); + fbuf[ol] = '\0'; + if (is_dot_dir) + fbuf[ol-1] = '.'; } } - /* This function is normally called by the sender, but the receiving side also * calls it from get_dirlist() with f set to -1 so that we just construct the * file list in memory without sending it over the wire. Also, get_dirlist() @@ -1041,14 +1039,15 @@ if (dname[0] == '.' && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) continue; - if (strlcpy(p, dname, remainder) < remainder) - send_file_name(f, flist, fbuf, 0); - else { + if (strlcpy(p, dname, remainder) >= remainder) { io_error |= IOERR_GENERAL; rprintf(FINFO, "cannot send long-named file %s\n", full_fname(fbuf)); + continue; } + + send_file_name(f, flist, fbuf, NULL, 0); } fbuf[len] = '\0'; @@ -1063,14 +1062,13 @@ if (recurse) { int i, end = flist->count - 1; for (i = start; i <= end; i++) - send_if_directory(f, flist, flist->files[i]); + send_if_directory(f, flist, flist->files[i], fbuf, len); } } - struct file_list *send_file_list(int f, int argc, char *argv[]) { - int l; + int len; STRUCT_STAT st; char *p, *dir, olddir[sizeof curr_dir]; char lastpath[MAXPATHLEN] = ""; @@ -1098,58 +1096,59 @@ } while (1) { - struct file_struct *file; - char fname2[MAXPATHLEN]; - char *fname = fname2; + char fbuf[MAXPATHLEN]; + char *fn; int is_dot_dir; if (use_ff_fd) { - if (read_filesfrom_line(filesfrom_fd, fname) == 0) + if (read_filesfrom_line(filesfrom_fd, fbuf) == 0) break; - sanitize_path(fname, fname, "", 0); + sanitize_path(fbuf, fbuf, "", 0); } else { if (argc-- == 0) break; - strlcpy(fname, *argv++, MAXPATHLEN); + strlcpy(fbuf, *argv++, MAXPATHLEN); if (sanitize_paths) - sanitize_path(fname, fname, "", 0); + sanitize_path(fbuf, fbuf, "", 0); } - l = strlen(fname); - if (!l || fname[l - 1] == '/') { - if (l == 2 && fname[0] == '.') { + len = strlen(fbuf); + if (relative_paths) { + /* We clean up fbuf below. */ + is_dot_dir = 0; + } else if (!len || fbuf[len - 1] == '/') { + if (len == 2 && fbuf[0] == '.') { /* Turn "./" into just "." rather than "./." */ - fname[1] = '\0'; + fbuf[1] = '\0'; } else { - if (l + 1 >= MAXPATHLEN) + if (len + 1 >= MAXPATHLEN) overflow_exit("send_file_list"); - fname[l++] = '.'; - fname[l] = '\0'; + fbuf[len++] = '.'; + fbuf[len] = '\0'; } is_dot_dir = 1; - } else if (l > 1 && fname[l-1] == '.' && fname[l-2] == '.' - && (l == 2 || fname[l-3] == '/')) { - if (l + 2 >= MAXPATHLEN) + } else if (len > 1 && fbuf[len-1] == '.' && fbuf[len-2] == '.' + && (len == 2 || fbuf[len-3] == '/')) { + if (len + 2 >= MAXPATHLEN) overflow_exit("send_file_list"); - fname[l++] = '/'; - fname[l++] = '.'; - fname[l] = '\0'; + fbuf[len++] = '/'; + fbuf[len++] = '.'; + fbuf[len] = '\0'; is_dot_dir = 1; } else { - is_dot_dir = fname[l-1] == '.' - && (l == 1 || fname[l-2] == '/'); + is_dot_dir = fbuf[len-1] == '.' + && (len == 1 || fbuf[len-2] == '/'); } - if (link_stat(fname, &st, keep_dirlinks) != 0) { + if (link_stat(fbuf, &st, copy_dirlinks) != 0) { io_error |= IOERR_GENERAL; rsyserr(FERROR, errno, "link_stat %s failed", - full_fname(fname)); + full_fname(fbuf)); continue; } if (S_ISDIR(st.st_mode) && !xfer_dirs) { - rprintf(FINFO, "skipping directory %s\n", - safe_fname(fname)); + rprintf(FINFO, "skipping directory %s\n", fbuf); continue; } @@ -1157,54 +1156,70 @@ olddir[0] = '\0'; if (!relative_paths) { - p = strrchr(fname, '/'); + p = strrchr(fbuf, '/'); if (p) { - *p = 0; - if (p == fname) + *p = '\0'; + if (p == fbuf) dir = "/"; else - dir = fname; - fname = p + 1; - } - } else if (implied_dirs && (p=strrchr(fname,'/')) && p != fname) { - /* this ensures we send the intermediate directories, - thus getting their permissions right */ - char *lp = lastpath, *fn = fname, *slash = fname; - *p = 0; - /* Skip any initial directories in our path that we - * have in common with lastpath. */ - while (*fn && *lp == *fn) { - if (*fn == '/') - slash = fn; - lp++, fn++; + dir = fbuf; + len -= p - fbuf + 1; + fn = p + 1; + } else + fn = fbuf; + } else { + if ((p = strstr(fbuf, "/./")) != NULL) { + *p = '\0'; + if (p == fbuf) + dir = "/"; + else + dir = fbuf; + len -= p - fbuf + 3; + fn = p + 3; + } else + fn = fbuf; + /* Get rid of trailing "/" and "/.". */ + while (len) { + if (fn[len - 1] == '/') { + is_dot_dir = 1; + if (!--len && !dir) { + len++; + break; + } + } + else if (len >= 2 && fn[len - 1] == '.' + && fn[len - 2] == '/') { + is_dot_dir = 1; + if (!(len -= 2) && !dir) { + len++; + break; + } + } else + break; } - *p = '/'; - if (fn != p || (*lp && *lp != '/')) { - int save_copy_links = copy_links; - int save_xfer_dirs = xfer_dirs; - copy_links = copy_unsafe_links; - xfer_dirs = 1; - while ((slash = strchr(slash+1, '/')) != 0) { - *slash = 0; - send_file_name(f, flist, fname, 0); - *slash = '/'; + fn[len] = '\0'; + /* Reject a ".." dir in the active part of the path. */ + for (p = fn; (p = strstr(p, "..")) != NULL; p += 2) { + if ((p[2] == '/' || p[2] == '\0') + && (p == fn || p[-1] == '/')) { + rprintf(FERROR, + "found \"..\" dir in relative path: %s\n", + fbuf); + exit_cleanup(RERR_SYNTAX); } - copy_links = save_copy_links; - xfer_dirs = save_xfer_dirs; - *p = 0; - strlcpy(lastpath, fname, sizeof lastpath); - *p = '/'; } } - if (!*fname) - fname = "."; + if (!*fn) { + len = 1; + fn = "."; + } if (dir && *dir) { static char *lastdir; static int lastdir_len; - strcpy(olddir, curr_dir); /* can't overflow */ + strlcpy(olddir, curr_dir, sizeof olddir); if (!push_dir(dir)) { io_error |= IOERR_GENERAL; @@ -1222,20 +1237,56 @@ } } + if (fn != fbuf) + memmove(fbuf, fn, len + 1); + + if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) { + /* Send the implied directories at the start of the + * source spec, so we get their permissions right. */ + char *lp = lastpath, *slash = fbuf; + *p = '\0'; + /* Skip any initial directories in our path that we + * have in common with lastpath. */ + for (fn = fbuf; *fn && *lp == *fn; lp++, fn++) { + if (*fn == '/') + slash = fn; + } + *p = '/'; + if (fn != p || (*lp && *lp != '/')) { + int save_copy_links = copy_links; + int save_xfer_dirs = xfer_dirs; + copy_links |= copy_unsafe_links; + xfer_dirs = 1; + while ((slash = strchr(slash+1, '/')) != 0) { + *slash = '\0'; + send_file_name(f, flist, fbuf, NULL, 0); + *slash = '/'; + } + copy_links = save_copy_links; + xfer_dirs = save_xfer_dirs; + *p = '\0'; + strlcpy(lastpath, fbuf, sizeof lastpath); + *p = '/'; + } + } + if (one_file_system) filesystem_dev = st.st_dev; - if ((file = send_file_name(f, flist, fname, XMIT_TOP_DIR))) { - if (recurse || (xfer_dirs && is_dot_dir)) - send_if_directory(f, flist, file); - } + if (recurse || (xfer_dirs && is_dot_dir)) { + struct file_struct *file; + file = send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR); + if (file) + send_if_directory(f, flist, file, fbuf, len); + } else + send_file_name(f, flist, fbuf, &st, 0); if (olddir[0]) { flist_dir = NULL; flist_dir_len = 0; if (!pop_dir(olddir)) { rsyserr(FERROR, errno, "pop_dir %s failed", - full_fname(dir)); + full_fname(olddir)); exit_cleanup(RERR_FILESELECT); } } @@ -1248,7 +1299,7 @@ stats.flist_buildtime = 1; start_tv = end_tv; - send_file_entry(NULL, f, 0); + send_file_entry(NULL, f); if (show_filelist_p()) finish_filelist_progress(flist); @@ -1268,8 +1319,6 @@ * without causing a compatibility problem with older versions. */ clean_flist(flist, 0, 0); - /* Now send the uid/gid list. This was introduced in - * protocol version 15 */ send_uid_list(f); /* send the io_error flag */ @@ -1288,7 +1337,6 @@ return flist; } - struct file_list *recv_file_list(int f) { struct file_list *flist; @@ -1308,7 +1356,6 @@ if (!flist->files) goto oom; - while ((flags = read_byte(f)) != 0) { struct file_struct *file; @@ -1318,7 +1365,7 @@ flags |= read_byte(f) << 8; file = receive_file_entry(flist, flags, f); - if (S_ISREG(file->mode)) + if (S_ISREG(file->mode) || S_ISLNK(file->mode)) stats.total_size += file->length; flist->files[flist->count++] = file; @@ -1327,7 +1374,7 @@ if (verbose > 2) { rprintf(FINFO, "recv_file_name(%s)\n", - safe_fname(f_name(file))); + f_name(file, NULL)); } } receive_file_entry(NULL, 0, 0); /* Signal that we're done. */ @@ -1341,8 +1388,6 @@ clean_flist(flist, relative_paths, 1); if (f >= 0) { - /* Now send the uid/gid list. This was introduced in - * protocol version 15 */ recv_uid_list(f, flist); /* Recv the io_error flag */ @@ -1369,60 +1414,83 @@ return flist; -oom: + oom: out_of_memory("recv_file_list"); return NULL; /* not reached */ } - static int file_compare(struct file_struct **file1, struct file_struct **file2) { return f_name_cmp(*file1, *file2); } - /* Search for an identically-named item in the file list. Note that the * items must agree in their directory-ness, or no match is returned. */ int flist_find(struct file_list *flist, struct file_struct *f) { int low = flist->low, high = flist->high; - int ret, mid, mid_up; + int diff, mid, mid_up; while (low <= high) { mid = (low + high) / 2; - for (mid_up = mid; !flist->files[mid_up]->basename; mid_up++) {} - if (mid_up <= high) - ret = f_name_cmp(flist->files[mid_up], f); - else - ret = 1; - if (ret == 0) { + if (flist->files[mid]->basename) + mid_up = mid; + else { + /* Scan for the next non-empty entry using the cached + * distance values. If the value isn't fully up-to- + * date, update it. */ + mid_up = mid + flist->files[mid]->dir.depth; + if (!flist->files[mid_up]->basename) { + do { + mid_up += flist->files[mid_up]->dir.depth; + } while (!flist->files[mid_up]->basename); + flist->files[mid]->dir.depth = mid_up - mid; + } + if (mid_up > high) { + /* If there's nothing left above us, set high to + * a non-empty entry below us and continue. */ + high = mid - flist->files[mid]->length; + if (!flist->files[high]->basename) { + do { + high -= flist->files[high]->length; + } while (!flist->files[high]->basename); + flist->files[mid]->length = mid - high; + } + continue; + } + } + diff = f_name_cmp(flist->files[mid_up], f); + if (diff == 0) { if (protocol_version < 29 && S_ISDIR(flist->files[mid_up]->mode) != S_ISDIR(f->mode)) return -1; return mid_up; } - if (ret > 0) - high = mid - 1; - else + if (diff < 0) low = mid_up + 1; + else + high = mid - 1; } return -1; } - /* * Free up any resources a file_struct has allocated * and clear the file. */ -void clear_file(int i, struct file_list *flist) +void clear_file(struct file_struct *file, struct file_list *flist) { - if (flist->hlink_pool && flist->files[i]->link_u.idev) - pool_free(flist->hlink_pool, 0, flist->files[i]->link_u.idev); - memset(flist->files[i], 0, file_struct_len); + if (flist->hlink_pool && file->link_u.idev) + pool_free(flist->hlink_pool, 0, file->link_u.idev); + memset(file, 0, file_struct_len); + /* In an empty entry, dir.depth is an offset to the next non-empty + * entry. Likewise for length in the opposite direction. We assume + * that we're alone for now since flist_find() will adjust the counts + * it runs into that aren't up-to-date. */ + file->length = file->dir.depth = 1; } - /* * allocate a new file list */ @@ -1462,13 +1530,13 @@ free(flist); } - /* * This routine ensures we don't have any duplicate names in our file list. * duplicate names can cause corruption because of the pipelining */ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) { + char fbuf[MAXPATHLEN]; int i, prev_i = 0; if (!flist) @@ -1478,10 +1546,8 @@ return; } - sorting_flist = flist; qsort(flist->files, flist->count, sizeof flist->files[0], (int (*)())file_compare); - sorting_flist = NULL; for (i = no_dups? 0 : flist->count; i < flist->count; i++) { if (flist->files[i]->basename) { @@ -1524,14 +1590,14 @@ if (verbose > 1 && !am_server) { rprintf(FINFO, "removing duplicate name %s from file list (%d)\n", - safe_fname(f_name(file)), drop); + f_name(file, fbuf), drop); } - /* Make sure that if we unduplicate '.', that we don't - * lose track of a user-specified top directory. */ + /* Make sure we don't lose track of a user-specified + * top directory. */ flist->files[keep]->flags |= flist->files[drop]->flags & (FLAG_TOP_DIR|FLAG_DEL_HERE); - clear_file(drop, flist); + clear_file(flist->files[drop], flist); if (keep == i) { if (flist->low == drop) { @@ -1555,18 +1621,79 @@ if (!file->dirname) continue; - if (*file->dirname == '/') { - char *s = file->dirname + 1; - while (*s == '/') s++; - memmove(file->dirname, s, strlen(s) + 1); - } - + while (*file->dirname == '/') + file->dirname++; if (!*file->dirname) file->dirname = NULL; } } -} + if (prune_empty_dirs && no_dups) { + int j, prev_depth = 0; + + prev_i = 0; /* It's OK that this isn't really true. */ + + for (i = flist->low; i <= flist->high; i++) { + struct file_struct *fp, *file = flist->files[i]; + + /* This temporarily abuses the dir.depth value for a + * directory that is in a chain that might get pruned. + * We restore the old value if it gets a reprieve. */ + if (S_ISDIR(file->mode) && file->dir.depth) { + /* Dump empty dirs when coming back down. */ + for (j = prev_depth; j >= file->dir.depth; j--) { + fp = flist->files[prev_i]; + if (fp->dir.depth >= 0) + break; + prev_i = -fp->dir.depth-1; + clear_file(fp, flist); + } + prev_depth = file->dir.depth; + if (is_excluded(f_name(file, fbuf), 1, + ALL_FILTERS)) { + /* Keep dirs through this dir. */ + for (j = prev_depth-1; ; j--) { + fp = flist->files[prev_i]; + if (fp->dir.depth >= 0) + break; + prev_i = -fp->dir.depth-1; + fp->dir.depth = j; + } + } else + file->dir.depth = -prev_i-1; + prev_i = i; + } else { + /* Keep dirs through this non-dir. */ + for (j = prev_depth; ; j--) { + fp = flist->files[prev_i]; + if (fp->dir.depth >= 0) + break; + prev_i = -fp->dir.depth-1; + fp->dir.depth = j; + } + } + } + /* Dump empty all remaining empty dirs. */ + while (1) { + struct file_struct *fp = flist->files[prev_i]; + if (fp->dir.depth >= 0) + break; + prev_i = -fp->dir.depth-1; + clear_file(fp, flist); + } + + for (i = flist->low; i <= flist->high; i++) { + if (flist->files[i]->basename) + break; + } + flist->low = i; + for (i = flist->high; i >= flist->low; i--) { + if (flist->files[i]->basename) + break; + } + flist->high = i; + } +} static void output_flist(struct file_list *flist) { @@ -1589,14 +1716,13 @@ sprintf(depthbuf, "%d", file->dir.depth); rprintf(FINFO, "[%s] i=%d %s %s%s%s%s mode=0%o len=%.0f%s%s flags=%x\n", who, i, am_sender ? NS(file->dir.root) : depthbuf, - file->dirname ? safe_fname(file->dirname) : "", + file->dirname ? file->dirname : "", file->dirname ? "/" : "", NS(file->basename), S_ISDIR(file->mode) ? "/" : "", (int)file->mode, (double)file->length, uidbuf, gidbuf, file->flags); } } - enum fnc_state { s_DIR, s_SLASH, s_BASE, s_TRAILING }; enum fnc_type { t_PATH, t_ITEM }; @@ -1743,16 +1869,24 @@ return dif; } - /* Return a copy of the full filename of a flist entry, using the indicated - * buffer. No size-checking is done because we checked the size when creating - * the file_struct entry. + * buffer or one of 5 static buffers if fbuf is NULL. No size-checking is + * done because we checked the size when creating the file_struct entry. */ -char *f_name_to(struct file_struct *f, char *fbuf) +char *f_name(struct file_struct *f, char *fbuf) { if (!f || !f->basename) return NULL; + if (!fbuf) { + static char names[5][MAXPATHLEN]; + static unsigned int n; + + n = (n + 1) % (sizeof names / sizeof names[0]); + + fbuf = names[n]; + } + if (f->dirname) { int len = strlen(f->dirname); memcpy(fbuf, f->dirname, len); @@ -1760,22 +1894,10 @@ strcpy(fbuf + len + 1, f->basename); } else strcpy(fbuf, f->basename); - return fbuf; -} - -/* Like f_name_to(), but we rotate through 5 static buffers of our own. */ -char *f_name(struct file_struct *f) -{ - static char names[5][MAXPATHLEN]; - static unsigned int n; - - n = (n + 1) % (sizeof names / sizeof names[0]); - - return f_name_to(f, names[n]); + return fbuf; } - /* Do a non-recursive scan of the named directory, possibly ignoring all * exclude rules except for the daemon's. If "dlen" is >=0, it is the length * of the dirname string, and also indicates that "dirname" is a MAXPATHLEN @@ -1787,6 +1909,7 @@ struct file_list *dirlist; char dirbuf[MAXPATHLEN]; int save_recurse = recurse; + int save_xfer_dirs = xfer_dirs; if (dlen < 0) { dlen = strlcpy(dirbuf, dirname, MAXPATHLEN); @@ -1798,7 +1921,9 @@ dirlist = flist_new(WITHOUT_HLINK, "get_dirlist"); recurse = 0; + xfer_dirs = 1; send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen); + xfer_dirs = save_xfer_dirs; recurse = save_recurse; if (do_progress) flist_count_offset += dirlist->count; diff -urN --exclude=patches rsync-2.6.6/generator.c rsync-2.6.7/generator.c --- rsync-2.6.6/generator.c 2005-07-28 12:06:03.000000000 -0700 +++ rsync-2.6.7/generator.c 2006-03-10 07:57:03.000000000 -0800 @@ -27,7 +27,6 @@ extern int dry_run; extern int do_xfers; extern int log_format_has_i; -extern int log_format_has_o_or_i; extern int daemon_log_format_has_i; extern int am_root; extern int am_server; @@ -35,15 +34,18 @@ extern int do_progress; extern int recurse; extern int relative_paths; +extern int implied_dirs; extern int keep_dirlinks; extern int preserve_links; extern int preserve_devices; +extern int preserve_specials; extern int preserve_hard_links; extern int preserve_perms; extern int preserve_uid; extern int preserve_gid; extern int preserve_times; extern int omit_dir_times; +extern int delete_mode; extern int delete_before; extern int delete_during; extern int delete_after; @@ -52,13 +54,16 @@ extern int remove_sent_files; extern int delay_updates; extern int update_only; -extern int opt_ignore_existing; +extern int ignore_existing; +extern int ignore_non_existing; extern int inplace; +extern int append_mode; extern int make_backups; extern int csum_length; extern int ignore_times; extern int size_only; extern OFF_T max_size; +extern OFF_T min_size; extern int io_error; extern int allowed_lull; extern int sock_f_out; @@ -66,6 +71,7 @@ extern int protocol_version; extern int fuzzy_basis; extern int always_checksum; +extern int checksum_len; extern char *partial_dir; extern char *basis_dir[]; extern int compare_dest; @@ -74,8 +80,6 @@ extern int whole_file; extern int list_only; extern int read_batch; -extern int only_existing; -extern int orig_umask; extern int safe_symlinks; extern long block_size; /* "long" because popt can't set an int32. */ extern int max_delete; @@ -90,6 +94,12 @@ extern struct filter_list_struct server_filter_list; static int deletion_count = 0; /* used to implement --max-delete */ +static int can_link_symlinks = 1; /* start out optimistic */ +static int can_link_devices = 1; + +/* For calling delete_file() */ +#define DEL_FORCE_RECURSE (1<<1) /* recurse even w/o --force */ +#define DEL_TERSE (1<<3) static int is_backup_file(char *fn) @@ -100,14 +110,18 @@ /* Delete a file or directory. If DEL_FORCE_RECURSE is set in the flags, or if - * force_delete is set, this will delete recursively as long as DEL_NO_RECURSE - * is not set in the flags. */ + * force_delete is set, this will delete recursively. + * + * Note that fname must point to a MAXPATHLEN buffer if the mode indicates it's + * a directory! (The buffer is used for recursion, but returned unchanged.) + */ static int delete_item(char *fname, int mode, int flags) { struct file_list *dirlist; - char buf[MAXPATHLEN]; int j, dlen, zap_dir, ok; + unsigned remainder; void *save_filters; + char *p; if (!S_ISDIR(mode)) { if (max_delete && ++deletion_count > max_delete) @@ -130,8 +144,7 @@ return -1; } - zap_dir = (flags & DEL_FORCE_RECURSE || (force_delete && recurse)) - && !(flags & DEL_NO_RECURSE); + zap_dir = flags & DEL_FORCE_RECURSE || force_delete; if ((max_delete && ++deletion_count > max_delete) || (dry_run && zap_dir)) { ok = 0; @@ -158,21 +171,29 @@ flags |= DEL_FORCE_RECURSE; /* mark subdir dels as not "in the way" */ deletion_count--; - dlen = strlcpy(buf, fname, MAXPATHLEN); - save_filters = push_local_filters(buf, dlen); + dlen = strlen(fname); + save_filters = push_local_filters(fname, dlen); + + dirlist = get_dirlist(fname, dlen, 0); + + p = fname + dlen; + if (dlen != 1 || *fname != '/') + *p++ = '/'; + remainder = MAXPATHLEN - (p - fname); - dirlist = get_dirlist(buf, dlen, 0); for (j = dirlist->count; j--; ) { struct file_struct *fp = dirlist->files[j]; if (fp->flags & FLAG_MOUNT_POINT) continue; - f_name_to(fp, buf); - delete_item(buf, fp->mode, flags & ~DEL_TERSE); + strlcpy(p, fp->basename, remainder); + delete_item(fname, fp->mode, flags & ~DEL_TERSE); } flist_free(dirlist); + fname[dlen] = '\0'; + pop_local_filters(save_filters); if (max_delete && ++deletion_count > max_delete) @@ -197,14 +218,13 @@ * call will append names onto the end, but the old dir value will be restored * on exit). */ static void delete_in_dir(struct file_list *flist, char *fbuf, - struct file_struct *file) + struct file_struct *file, STRUCT_STAT *stp) { static int min_depth = MAXPATHLEN, cur_depth = -1; static void *filt_array[MAXPATHLEN/2+1]; static int already_warned = 0; struct file_list *dirlist; char delbuf[MAXPATHLEN]; - STRUCT_STAT st; int dlen, i; if (!flist) { @@ -216,7 +236,7 @@ } if (verbose > 2) - rprintf(FINFO, "delete_in_dir(%s)\n", safe_fname(fbuf)); + rprintf(FINFO, "delete_in_dir(%s)\n", fbuf); if (allowed_lull) maybe_send_keepalive(); @@ -241,13 +261,10 @@ dlen = strlen(fbuf); filt_array[cur_depth] = push_local_filters(fbuf, dlen); - if (link_stat(fbuf, &st, keep_dirlinks) < 0) - return; - if (one_file_system) { if (file->flags & FLAG_TOP_DIR) - filesystem_dev = st.st_dev; - else if (filesystem_dev != st.st_dev) + filesystem_dev = stp->st_dev; + else if (filesystem_dev != stp->st_dev) return; } @@ -260,9 +277,8 @@ if (!fp->basename || fp->flags & FLAG_MOUNT_POINT) continue; if (flist_find(flist, fp) < 0) { - int mode = fp->mode; - f_name_to(fp, delbuf); - delete_item(delbuf, mode, DEL_FORCE_RECURSE); + f_name(fp, delbuf); + delete_item(delbuf, fp->mode, DEL_FORCE_RECURSE); } } @@ -274,6 +290,7 @@ static void do_delete_pass(struct file_list *flist) { char fbuf[MAXPATHLEN]; + STRUCT_STAT st; int j; if (dry_run > 1 /* destination doesn't exist yet */ @@ -286,17 +303,23 @@ if (!(file->flags & FLAG_DEL_HERE)) continue; - f_name_to(file, fbuf); + f_name(file, fbuf); if (verbose > 1 && file->flags & FLAG_TOP_DIR) - rprintf(FINFO, "deleting in %s\n", safe_fname(fbuf)); + rprintf(FINFO, "deleting in %s\n", fbuf); - delete_in_dir(flist, fbuf, file); + if (link_stat(fbuf, &st, keep_dirlinks) < 0 + || !S_ISDIR(st.st_mode)) + continue; + + delete_in_dir(flist, fbuf, file, &st); } + delete_in_dir(NULL, NULL, NULL, NULL); + if (do_progress && !am_server) rprintf(FINFO, " \r"); } -static int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st) +int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st) { if (preserve_perms && (st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) @@ -311,37 +334,33 @@ return 1; } - void itemize(struct file_struct *file, int ndx, int statret, STRUCT_STAT *st, int32 iflags, uchar fnamecmp_type, char *xname) { - if (statret == 0) { + if (statret >= 0) { /* A from-dest-dir statret can == 1! */ + int keep_time = !preserve_times ? 0 + : S_ISDIR(file->mode) ? !omit_dir_times + : !S_ISLNK(file->mode); + if (S_ISREG(file->mode) && file->length != st->st_size) iflags |= ITEM_REPORT_SIZE; - if (!(iflags & ITEM_NO_DEST_AND_NO_UPDATE)) { - int keep_time = !preserve_times ? 0 - : S_ISDIR(file->mode) ? !omit_dir_times - : !S_ISLNK(file->mode); - - if ((iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !keep_time - && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname)) - || (keep_time && cmp_modtime(file->modtime, st->st_mtime) != 0)) - iflags |= ITEM_REPORT_TIME; - if (preserve_perms - && (file->mode & CHMOD_BITS) != (st->st_mode & CHMOD_BITS)) - iflags |= ITEM_REPORT_PERMS; - if (preserve_uid && am_root && file->uid != st->st_uid) - iflags |= ITEM_REPORT_OWNER; - if (preserve_gid && file->gid != GID_NONE - && st->st_gid != file->gid) - iflags |= ITEM_REPORT_GROUP; - } + if ((iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !keep_time + && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname)) + || (keep_time && cmp_time(file->modtime, st->st_mtime) != 0)) + iflags |= ITEM_REPORT_TIME; + if ((file->mode & CHMOD_BITS) != (st->st_mode & CHMOD_BITS)) + iflags |= ITEM_REPORT_PERMS; + if (preserve_uid && am_root && file->uid != st->st_uid) + iflags |= ITEM_REPORT_OWNER; + if (preserve_gid && file->gid != GID_NONE + && st->st_gid != file->gid) + iflags |= ITEM_REPORT_GROUP; } else iflags |= ITEM_IS_NEW; iflags &= 0xffff; if ((iflags & SIGNIFICANT_ITEM_FLAGS || verbose > 1 - || (xname && *xname)) && !read_batch) { + || log_format_has_i > 1 || (xname && *xname)) && !read_batch) { if (protocol_version >= 29) { if (ndx >= 0) write_int(sock_f_out, ndx); @@ -357,7 +376,7 @@ /* Perform our quick-check heuristic for determining if a file is unchanged. */ -static int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st) +int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st) { if (st->st_size != file->length) return 0; @@ -367,8 +386,7 @@ if (always_checksum && S_ISREG(st->st_mode)) { char sum[MD4_SUM_LENGTH]; file_checksum(fn, sum, st->st_size); - return memcmp(sum, file->u.sum, protocol_version < 21 ? 2 - : MD4_SUM_LENGTH) == 0; + return memcmp(sum, file->u.sum, checksum_len) == 0; } if (size_only) @@ -377,7 +395,7 @@ if (ignore_times) return 0; - return cmp_modtime(st->st_mtime, file->modtime) == 0; + return cmp_time(st->st_mtime, file->modtime) == 0; } @@ -445,8 +463,8 @@ sum->flength = len; sum->blength = blength; sum->s2length = s2length; - sum->count = (len + (blength - 1)) / blength; - sum->remainder = (len % blength); + sum->remainder = len % blength; + sum->count = len / blength + (sum->remainder != 0); if (sum->count && verbose > 2) { rprintf(FINFO, @@ -470,35 +488,42 @@ OFF_T offset = 0; sum_sizes_sqroot(&sum, len); + write_sum_head(f_out, &sum); + + if (append_mode > 0 && f_copy < 0) + return; 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++) { int32 n1 = (int32)MIN(len, (OFF_T)sum.blength); char *map = map_ptr(mapbuf, offset, n1); - uint32 sum1 = get_checksum1(map, n1); char sum2[SUM_LENGTH]; + uint32 sum1; - if (f_copy >= 0) + len -= n1; + offset += n1; + + if (f_copy >= 0) { full_write(f_copy, map, n1); + if (append_mode > 0) + continue; + } + sum1 = get_checksum1(map, n1); get_checksum2(map, n1, sum2); if (verbose > 3) { rprintf(FINFO, "chunk[%.0f] offset=%.0f len=%ld sum1=%08lx\n", - (double)i, (double)offset, (long)n1, + (double)i, (double)offset - n1, (long)n1, (unsigned long)sum1); } write_int(f_out, sum1); write_buf(f_out, sum2, sum.s2length); - len -= n1; - offset += n1; } if (mapbuf) @@ -530,7 +555,7 @@ name = fp->basename; if (fp->length == file->length - && cmp_modtime(fp->modtime, file->modtime) == 0) { + && cmp_time(fp->modtime, file->modtime) == 0) { if (verbose > 4) { rprintf(FINFO, "fuzzy size/modtime match for %s\n", @@ -576,6 +601,157 @@ } } +/* This is only called for regular files. We return -2 if we've finished + * handling the file, -1 if no dest-linking occurred, or a non-negative + * value if we found an alternate basis file. */ +static int try_dests_reg(struct file_struct *file, char *fname, int ndx, + char *cmpbuf, STRUCT_STAT *stp, int itemizing, + int maybe_ATTRS_REPORT, enum logcode code) +{ + int best_match = -1; + int match_level = 0; + int j = 0; + + do { + pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname); + if (link_stat(cmpbuf, stp, 0) < 0 || !S_ISREG(stp->st_mode)) + continue; + switch (match_level) { + case 0: + best_match = j; + match_level = 1; + /* FALL THROUGH */ + case 1: + if (!unchanged_file(cmpbuf, file, stp)) + continue; + best_match = j; + match_level = 2; + /* FALL THROUGH */ + case 2: + if (!unchanged_attrs(file, stp)) + continue; + if ((always_checksum || ignore_times) + && cmp_time(stp->st_mtime, file->modtime)) + continue; + best_match = j; + match_level = 3; + break; + } + break; + } while (basis_dir[++j] != NULL); + + if (!match_level) + return -1; + + if (j != best_match) { + j = best_match; + pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname); + if (link_stat(cmpbuf, stp, 0) < 0) + match_level = 0; + } + +#ifdef HAVE_LINK + if (match_level == 3 && !copy_dest) { + if (link_dest) { + if (hard_link_one(file, ndx, fname, 0, stp, + cmpbuf, 1, + itemizing && verbose > 1, + code) < 0) + goto try_a_copy; + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); + } else if (itemizing) + itemize(file, ndx, 0, stp, 0, 0, NULL); + if (verbose > 1 && maybe_ATTRS_REPORT) { + code = daemon_log_format_has_i || dry_run + ? FCLIENT : FINFO; + rprintf(code, "%s is uptodate\n", fname); + } + return -2; + } +#endif + + if (match_level >= 2) { + try_a_copy: /* Copy the file locally. */ + if (copy_file(cmpbuf, fname, file->mode) < 0) { + if (verbose) { + rsyserr(FINFO, errno, "copy_file %s => %s", + full_fname(cmpbuf), fname); + } + return -1; + } + if (itemizing) + itemize(file, ndx, 0, stp, ITEM_LOCAL_CHANGE, 0, NULL); + set_file_attrs(fname, file, NULL, 0); + if (maybe_ATTRS_REPORT + && ((!itemizing && verbose && match_level == 2) + || (verbose > 1 && match_level == 3))) { + code = daemon_log_format_has_i || dry_run + ? FCLIENT : FINFO; + rprintf(code, "%s%s\n", fname, + match_level == 3 ? " is uptodate" : ""); + } + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); + return -2; + } + + return FNAMECMP_BASIS_DIR_LOW + j; +} + +/* This is only called for non-regular files. We return -2 if we've finished + * handling the file, or -1 if no dest-linking occurred. */ +static int try_dests_non(struct file_struct *file, char *fname, int ndx, + int itemizing, int *possible_ptr, + int maybe_ATTRS_REPORT, enum logcode code) +{ + char fnamebuf[MAXPATHLEN], lnk[MAXPATHLEN]; + STRUCT_STAT st; + int len, i = 0; + + do { + pathjoin(fnamebuf, MAXPATHLEN, basis_dir[i], fname); + if (link_stat(fnamebuf, &st, 0) < 0 || S_ISDIR(st.st_mode) + || !unchanged_attrs(file, &st)) + continue; + if (S_ISLNK(file->mode)) { +#ifdef SUPPORT_LINKS + if ((len = readlink(fnamebuf, lnk, MAXPATHLEN-1)) <= 0) + continue; + lnk[len] = '\0'; + if (strcmp(lnk, file->u.link) != 0) +#endif + continue; + } else { + if (!IS_DEVICE(st.st_mode) || st.st_rdev != file->u.rdev) + continue; + } + if (link_dest) { + if (do_link(fnamebuf, fname) < 0) { + /* TODO improve this to be based on errno? */ + *possible_ptr = 0; + break; + } + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); + } + if (itemizing && log_format_has_i && verbose > 1) { + int changes = compare_dest ? 0 : ITEM_LOCAL_CHANGE + + (link_dest ? ITEM_XNAME_FOLLOWS : 0); + char *lp = link_dest ? "" : NULL; + itemize(file, ndx, 0, &st, changes, 0, lp); + } + if (verbose > 1 && maybe_ATTRS_REPORT) { + code = daemon_log_format_has_i || dry_run + ? FCLIENT : FINFO; + rprintf(code, "%s is uptodate\n", fname); + } + return -2; + } while (basis_dir[++i] != NULL); + + return -1; +} + static int phase = 0; /* Acts on the_file_list->file's ndx'th item, whose name is fname. If a dir, @@ -584,15 +760,18 @@ * regular files that have changed, we try to find a basis file and then * start sending checksums. * + * When fname is non-null, it must point to a MAXPATHLEN buffer! + * * Note that f_out is set to -1 when doing final directory-permission and * modification-time repair. */ static void recv_generator(char *fname, struct file_struct *file, int ndx, - int itemizing, int maybe_PERMS_REPORT, + int itemizing, int maybe_ATTRS_REPORT, enum logcode code, int f_out) { static int missing_below = -1, excluded_below = -1; - static char *fuzzy_dirname = ""; + static char *parent_dirname = ""; static struct file_list *fuzzy_dirlist = NULL; + static int need_fuzzy_dirlist = 0; struct file_struct *fuzzy_file = NULL; int fd = -1, f_copy = -1; STRUCT_STAT st, real_st, partial_st; @@ -601,6 +780,7 @@ char *fnamecmp, *partialptr, *backupptr = NULL; char fnamecmpbuf[MAXPATHLEN]; uchar fnamecmp_type; + int del_opts = DEL_TERSE | (delete_mode ? DEL_FORCE_RECURSE : 0); if (list_only) return; @@ -609,19 +789,17 @@ if (fuzzy_dirlist) { flist_free(fuzzy_dirlist); fuzzy_dirlist = NULL; - fuzzy_dirname = ""; } if (missing_below >= 0) { dry_run--; missing_below = -1; } + parent_dirname = ""; return; } - if (verbose > 2) { - rprintf(FINFO, "recv_generator(%s,%d)\n", - safe_fname(fname), ndx); - } + if (verbose > 2) + rprintf(FINFO, "recv_generator(%s,%d)\n", fname, ndx); if (server_filter_list.head) { if (excluded_below >= 0) { @@ -633,11 +811,11 @@ S_ISDIR(file->mode)) < 0) { if (S_ISDIR(file->mode)) excluded_below = file->dir.depth; - skipping: + skipping: if (verbose) { rprintf(FINFO, "skipping server-excluded file \"%s\"\n", - safe_fname(fname)); + fname); } return; } @@ -651,15 +829,27 @@ statret = -1; stat_errno = ENOENT; } else { - if (fuzzy_basis && S_ISREG(file->mode)) { - char *dn = file->dirname ? file->dirname : "."; - if (fuzzy_dirname != dn - && strcmp(fuzzy_dirname, dn) != 0) { - if (fuzzy_dirlist) - flist_free(fuzzy_dirlist); - fuzzy_dirlist = get_dirlist(dn, -1, 1); + char *dn = file->dirname ? file->dirname : "."; + if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) { + if (relative_paths && !implied_dirs + && do_stat(dn, &st) < 0 + && create_directory_path(fname) < 0) { + rsyserr(FERROR, errno, + "recv_generator: mkdir %s failed", + full_fname(dn)); } - fuzzy_dirname = dn; + if (fuzzy_dirlist) { + flist_free(fuzzy_dirlist); + fuzzy_dirlist = NULL; + } + if (fuzzy_basis) + need_fuzzy_dirlist = 1; + } + parent_dirname = dn; + + if (need_fuzzy_dirlist && S_ISREG(file->mode)) { + fuzzy_dirlist = get_dirlist(dn, -1, 1); + need_fuzzy_dirlist = 0; } statret = link_stat(fname, &st, @@ -667,23 +857,21 @@ stat_errno = errno; } - if (only_existing && statret == -1 && stat_errno == ENOENT) { - /* we only want to update existing files */ + if (ignore_non_existing && statret == -1 && stat_errno == ENOENT) { if (verbose > 1) { rprintf(FINFO, "not creating new %s \"%s\"\n", S_ISDIR(file->mode) ? "directory" : "file", - safe_fname(fname)); + fname); } return; } - 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 */ - file->mode = (file->mode & ~CHMOD_BITS) - | (st.st_mode & CHMOD_BITS); + /* If we're not preserving permissions, change the file-list's + * mode based on the local permissions and some heuristics. */ + if (!preserve_perms) { + int exists = statret == 0 + && S_ISDIR(st.st_mode) == S_ISDIR(file->mode); + file->mode = dest_mode(file->mode, st.st_mode, exists); } if (S_ISDIR(file->mode)) { @@ -693,7 +881,7 @@ * we need to delete it. If it doesn't exist, then * (perhaps recursively) create it. */ if (statret == 0 && !S_ISDIR(st.st_mode)) { - if (delete_item(fname, st.st_mode, DEL_TERSE) < 0) + if (delete_item(fname, st.st_mode, del_opts) < 0) return; statret = -1; } @@ -705,34 +893,38 @@ itemize(file, ndx, statret, &st, statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL); } - if (statret != 0 && do_mkdir(fname,file->mode) != 0 && errno != EEXIST) { + if (statret != 0 && do_mkdir(fname,file->mode) < 0 && errno != EEXIST) { if (!relative_paths || errno != ENOENT - || create_directory_path(fname, orig_umask) < 0 + || create_directory_path(fname) < 0 || (do_mkdir(fname, file->mode) < 0 && errno != EEXIST)) { rsyserr(FERROR, errno, "recv_generator: mkdir %s failed", full_fname(fname)); } } - if (set_perms(fname, file, statret ? NULL : &st, 0) + if (set_file_attrs(fname, file, statret ? NULL : &st, 0) && verbose && code && f_out != -1) - rprintf(code, "%s/\n", safe_fname(fname)); + rprintf(code, "%s/\n", fname); if (delete_during && f_out != -1 && !phase && dry_run < 2 && (file->flags & FLAG_DEL_HERE)) - delete_in_dir(the_file_list, fname, file); + delete_in_dir(the_file_list, fname, file, &st); return; } + if (preserve_hard_links && file->link_u.links + && hard_link_check(file, ndx, fname, statret, &st, + itemizing, code, HL_CHECK_MASTER)) + return; + if (preserve_links && S_ISLNK(file->mode)) { #ifdef SUPPORT_LINKS if (safe_symlinks && unsafe_symlink(file->u.link, fname)) { if (verbose) { if (the_file_list->count == 1) - fname = f_name(file); + fname = f_name(file, NULL); rprintf(FINFO, "ignoring unsafe symlink %s -> \"%s\"\n", - full_fname(fname), - safe_fname(file->u.link)); + full_fname(fname), file->u.link); } return; } @@ -751,109 +943,151 @@ itemize(file, ndx, 0, &st, 0, 0, NULL); } - set_perms(fname, file, &st, - maybe_PERMS_REPORT); + set_file_attrs(fname, file, &st, + maybe_ATTRS_REPORT); + if (preserve_hard_links + && file->link_u.links) { + hard_link_cluster(file, ndx, + itemizing, + code); + } return; } } /* Not the right symlink (or not a symlink), so * delete it. */ - if (delete_item(fname, st.st_mode, DEL_TERSE) < 0) + if (delete_item(fname, st.st_mode, del_opts) < 0) return; if (!S_ISLNK(st.st_mode)) statret = -1; + } else if (basis_dir[0] != NULL && can_link_symlinks) { + if (try_dests_non(file, fname, ndx, itemizing, + &can_link_symlinks, + maybe_ATTRS_REPORT, code) == -2) { + if (!copy_dest) + return; + itemizing = code = 0; + } } + if (preserve_hard_links && file->link_u.links + && hard_link_check(file, ndx, fname, -1, &st, + itemizing, code, HL_SKIP)) + return; if (do_symlink(file->u.link,fname) != 0) { rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed", - full_fname(fname), safe_fname(file->u.link)); + full_fname(fname), file->u.link); } else { - set_perms(fname,file,NULL,0); + set_file_attrs(fname, file, NULL, 0); if (itemizing) { itemize(file, ndx, statret, &st, ITEM_LOCAL_CHANGE, 0, NULL); } if (code && verbose) { - rprintf(code, "%s -> %s\n", safe_fname(fname), - safe_fname(file->u.link)); + rprintf(code, "%s -> %s\n", fname, + file->u.link); } if (remove_sent_files && !dry_run) { char numbuf[4]; SIVAL(numbuf, 0, ndx); send_msg(MSG_SUCCESS, numbuf, 4); } + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); } #endif return; } - if (am_root && preserve_devices && IS_DEVICE(file->mode)) { - if (statret != 0 || - st.st_mode != file->mode || - st.st_rdev != file->u.rdev) { - if (delete_item(fname, st.st_mode, DEL_TERSE) < 0) + if ((am_root && preserve_devices && IS_DEVICE(file->mode)) + || (preserve_specials && IS_SPECIAL(file->mode))) { + if (statret != 0 + && (basis_dir[0] != NULL && can_link_devices)) { + if (try_dests_non(file, fname, ndx, itemizing, + &can_link_devices, + maybe_ATTRS_REPORT, code) == -2) { + if (!copy_dest) + return; + itemizing = code = 0; + } + } + if (statret != 0 + || (st.st_mode & ~CHMOD_BITS) != (file->mode & ~CHMOD_BITS) + || st.st_rdev != file->u.rdev) { + if (statret == 0 + && delete_item(fname, st.st_mode, del_opts) < 0) + return; + if (preserve_hard_links && file->link_u.links + && hard_link_check(file, ndx, fname, -1, &st, + itemizing, code, HL_SKIP)) return; - if (!IS_DEVICE(st.st_mode)) + if ((IS_DEVICE(file->mode) && !IS_DEVICE(st.st_mode)) + || (IS_SPECIAL(file->mode) && !IS_SPECIAL(st.st_mode))) statret = -1; if (verbose > 2) { rprintf(FINFO,"mknod(%s,0%o,0x%x)\n", - safe_fname(fname), + fname, (int)file->mode, (int)file->u.rdev); } - if (do_mknod(fname,file->mode,file->u.rdev) != 0) { + if (do_mknod(fname,file->mode,file->u.rdev) < 0) { rsyserr(FERROR, errno, "mknod %s failed", full_fname(fname)); } else { - set_perms(fname,file,NULL,0); + set_file_attrs(fname, file, NULL, 0); if (itemizing) { itemize(file, ndx, statret, &st, ITEM_LOCAL_CHANGE, 0, NULL); } - if (code && verbose) { - rprintf(code, "%s\n", - safe_fname(fname)); + if (code && verbose) + rprintf(code, "%s\n", fname); + if (preserve_hard_links && file->link_u.links) { + hard_link_cluster(file, ndx, + itemizing, code); } } } else { if (itemizing) itemize(file, ndx, statret, &st, 0, 0, NULL); - set_perms(fname, file, &st, maybe_PERMS_REPORT); + set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT); + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); } return; } - if (preserve_hard_links - && hard_link_check(file, ndx, fname, statret, &st, - itemizing, code, HL_CHECK_MASTER)) - return; - if (!S_ISREG(file->mode)) { if (the_file_list->count == 1) - fname = f_name(file); - rprintf(FINFO, "skipping non-regular file \"%s\"\n", - safe_fname(fname)); + fname = f_name(file, NULL); + rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname); return; } if (max_size && file->length > max_size) { if (verbose > 1) { if (the_file_list->count == 1) - fname = f_name(file); - rprintf(FINFO, "%s is over max-size\n", - safe_fname(fname)); + fname = f_name(file, NULL); + rprintf(FINFO, "%s is over max-size\n", fname); + } + return; + } + if (min_size && file->length < min_size) { + if (verbose > 1) { + if (the_file_list->count == 1) + fname = f_name(file, NULL); + rprintf(FINFO, "%s is under min-size\n", fname); } return; } - if (opt_ignore_existing && statret == 0) { + if (ignore_existing && statret == 0) { if (verbose > 1) - rprintf(FINFO, "%s exists\n", safe_fname(fname)); + rprintf(FINFO, "%s exists\n", fname); return; } if (update_only && statret == 0 - && cmp_modtime(st.st_mtime, file->modtime) > 0) { + && cmp_time(st.st_mtime, file->modtime) > 0) { if (verbose > 1) - rprintf(FINFO, "%s is newer\n", safe_fname(fname)); + rprintf(FINFO, "%s is newer\n", fname); return; } @@ -861,107 +1095,21 @@ fnamecmp_type = FNAMECMP_FNAME; if (statret == 0 && !S_ISREG(st.st_mode)) { - if (delete_item(fname, st.st_mode, DEL_TERSE) != 0) + if (delete_item(fname, st.st_mode, del_opts) != 0) return; statret = -1; stat_errno = ENOENT; } if (statret != 0 && basis_dir[0] != NULL) { - int best_match = -1; - int match_level = 0; - int i = 0; - do { - pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, - basis_dir[i], fname); - if (link_stat(fnamecmpbuf, &st, 0) < 0 - || !S_ISREG(st.st_mode)) - continue; - switch (match_level) { - case 0: - best_match = i; - match_level = 1; - /* FALL THROUGH */ - case 1: - if (!unchanged_file(fnamecmpbuf, file, &st)) - continue; - best_match = i; - match_level = 2; - if (copy_dest) - break; - /* FALL THROUGH */ - case 2: - if (!unchanged_attrs(file, &st)) - continue; - best_match = i; - match_level = 3; - break; - } - break; - } while (basis_dir[++i] != NULL); - if (match_level) { + int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &st, + itemizing, maybe_ATTRS_REPORT, code); + if (j == -2) + return; + if (j != -1) { + fnamecmp = fnamecmpbuf; + fnamecmp_type = j; statret = 0; - if (i != best_match) { - i = best_match; - pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, - basis_dir[i], fname); - if (link_stat(fnamecmpbuf, &st, 0) < 0) { - match_level = 0; - statret = -1; - stat_errno = errno; - } - } -#ifdef HAVE_LINK - if (link_dest && match_level == 3) { - if (hard_link_one(file, ndx, fname, -1, &st, - fnamecmpbuf, 1, - itemizing && verbose > 1, - code) == 0) { - if (preserve_hard_links - && file->link_u.links) { - hard_link_cluster(file, ndx, - itemizing, - code); - } - return; - } - match_level = 2; - } -#endif - if (match_level == 2) { - /* Copy the file locally. */ - if (copy_file(fnamecmpbuf, fname, file->mode) < 0) { - if (verbose) { - rsyserr(FINFO, errno, - "copy_file %s => %s", - full_fname(fnamecmpbuf), - safe_fname(fname)); - } - match_level = 0; - statret = -1; - } else { - if (itemizing) { - itemize(file, ndx, 0, &st, - ITEM_LOCAL_CHANGE, 0, - NULL); - } else if (verbose && code) { - rprintf(code, "%s\n", - safe_fname(fname)); - } - set_perms(fname, file, NULL, - maybe_PERMS_REPORT); - if (preserve_hard_links - && file->link_u.links) { - hard_link_cluster(file, ndx, - itemizing, - code); - } - return; - } - } else if (compare_dest || match_level == 1) { - fnamecmp = fnamecmpbuf; - fnamecmp_type = i; - } } } @@ -976,14 +1124,14 @@ } else partialptr = NULL; - if (statret != 0 && fuzzy_basis && dry_run <= 1) { + if (statret != 0 && fuzzy_dirlist && dry_run <= 1) { int j = find_fuzzy(file, fuzzy_dirlist); if (j >= 0) { fuzzy_file = fuzzy_dirlist->files[j]; - f_name_to(fuzzy_file, fnamecmpbuf); + f_name(fuzzy_file, fnamecmpbuf); if (verbose > 2) { rprintf(FINFO, "fuzzy basis selected for %s: %s\n", - safe_fname(fname), safe_fname(fnamecmpbuf)); + fname, fnamecmpbuf); } st.st_size = fuzzy_file->length; statret = 0; @@ -993,42 +1141,40 @@ } if (statret != 0) { - if (preserve_hard_links + if (preserve_hard_links && file->link_u.links && hard_link_check(file, ndx, fname, statret, &st, itemizing, code, HL_SKIP)) return; if (stat_errno == ENOENT) goto notify_others; - if (verbose > 1) { - rsyserr(FERROR, stat_errno, - "recv_generator: failed to stat %s", - full_fname(fname)); - } + rsyserr(FERROR, stat_errno, "recv_generator: failed to stat %s", + full_fname(fname)); return; } - if (!compare_dest && fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH) + if (append_mode && st.st_size > file->length) + return; + + if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH) ; else if (fnamecmp_type == FNAMECMP_FUZZY) ; else if (unchanged_file(fnamecmp, file, &st)) { - if (fnamecmp_type == FNAMECMP_FNAME) { - if (itemizing) { - itemize(file, ndx, real_ret, &real_st, - 0, 0, NULL); - } - set_perms(fname, file, &st, maybe_PERMS_REPORT); - if (preserve_hard_links && file->link_u.links) - hard_link_cluster(file, ndx, itemizing, code); - return; + if (partialptr) { + do_unlink(partialptr); + handle_partial_dir(partialptr, PDIR_DELETE); + } + if (itemizing) { + itemize(file, ndx, real_ret, &real_st, + 0, 0, NULL); } - /* Only --compare-dest gets here. */ - itemize(file, ndx, real_ret, &real_st, - ITEM_NO_DEST_AND_NO_UPDATE, 0, NULL); + set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT); + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); return; } -prepare_to_open: + prepare_to_open: if (partialptr) { st = partial_st; fnamecmp = partialptr; @@ -1039,7 +1185,7 @@ if (!do_xfers || read_batch || whole_file) goto notify_others; - if (fuzzy_basis) { + if (fuzzy_dirlist) { int j = flist_find(fuzzy_dirlist, file); if (j >= 0) /* don't use changing file as future fuzzy basis */ fuzzy_dirlist->files[j]->flags |= FLAG_NO_FUZZY; @@ -1051,9 +1197,9 @@ if (fd == -1) { rsyserr(FERROR, errno, "failed to open %s, continuing", full_fname(fnamecmp)); - pretend_missing: + pretend_missing: /* pretend the file didn't exist */ - if (preserve_hard_links + if (preserve_hard_links && file->link_u.links && hard_link_check(file, ndx, fname, statret, &st, itemizing, code, HL_SKIP)) return; @@ -1066,7 +1212,7 @@ close(fd); return; } - if (!(back_file = make_file(fname, NULL, NO_FILTERS))) { + if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) { close(fd); goto pretend_missing; } @@ -1090,13 +1236,15 @@ if (verbose > 3) { rprintf(FINFO, "gen mapped %s of size %.0f\n", - safe_fname(fnamecmp), (double)st.st_size); + fnamecmp, (double)st.st_size); } if (verbose > 2) rprintf(FINFO, "generating and sending sums for %d\n", ndx); -notify_others: + notify_others: + if (remove_sent_files && !delay_updates && !phase) + increment_active_files(ndx, itemizing, code); write_int(f_out, ndx); if (itemizing) { int iflags = ITEM_TRANSFER; @@ -1127,10 +1275,10 @@ if (f_copy >= 0) { close(f_copy); - set_perms(backupptr, back_file, NULL, 0); + set_file_attrs(backupptr, back_file, NULL, 0); if (verbose > 1) { rprintf(FINFO, "backed up %s to %s\n", - safe_fname(fname), safe_fname(backupptr)); + fname, backupptr); } free(back_file); } @@ -1138,36 +1286,36 @@ close(fd); } - void generate_files(int f_out, struct file_list *flist, char *local_name) { int i; char fbuf[MAXPATHLEN]; - int itemizing, maybe_PERMS_REPORT; + int itemizing, maybe_ATTRS_REPORT; enum logcode code; int lull_mod = allowed_lull * 5; int need_retouch_dir_times = preserve_times && !omit_dir_times; int need_retouch_dir_perms = 0; - int save_only_existing = only_existing; - int save_opt_ignore_existing = opt_ignore_existing; + int save_ignore_existing = ignore_existing; + int save_ignore_non_existing = ignore_non_existing; int save_do_progress = do_progress; int save_make_backups = make_backups; + int dir_tweaking = !(list_only || local_name || dry_run); if (protocol_version >= 29) { itemizing = 1; - maybe_PERMS_REPORT = log_format_has_i ? 0 : PERMS_REPORT; + maybe_ATTRS_REPORT = log_format_has_i ? 0 : ATTRS_REPORT; code = daemon_log_format_has_i ? 0 : FLOG; } else if (am_daemon) { itemizing = daemon_log_format_has_i && do_xfers; - maybe_PERMS_REPORT = PERMS_REPORT; + maybe_ATTRS_REPORT = ATTRS_REPORT; code = itemizing || !do_xfers ? FCLIENT : FINFO; } else if (!am_server) { itemizing = log_format_has_i; - maybe_PERMS_REPORT = log_format_has_i ? 0 : PERMS_REPORT; + maybe_ATTRS_REPORT = log_format_has_i ? 0 : ATTRS_REPORT; code = itemizing ? 0 : FINFO; } else { itemizing = 0; - maybe_PERMS_REPORT = PERMS_REPORT; + maybe_ATTRS_REPORT = ATTRS_REPORT; code = FINFO; } @@ -1180,7 +1328,7 @@ do_delete_pass(flist); do_progress = 0; - if (whole_file < 0) + if (append_mode || whole_file < 0) whole_file = 0; if (verbose >= 2) { rprintf(FINFO, "delta-transmission %s\n", @@ -1201,24 +1349,29 @@ if (!file->basename) continue; - recv_generator(local_name ? local_name : f_name_to(file, fbuf), - file, i, itemizing, maybe_PERMS_REPORT, code, - f_out); + if (local_name) + strlcpy(fbuf, local_name, sizeof fbuf); + else + f_name(file, fbuf); + recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT, + code, f_out); /* We need to ensure that any dirs we create have writeable * permissions during the time we are putting files within * them. This is then fixed after the transfer is done. */ +#ifdef HAVE_CHMOD if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR) - && !list_only) { - int mode = file->mode | S_IWUSR; /* user write */ + && dir_tweaking) { + mode_t mode = file->mode | S_IWUSR; /* user write */ char *fname = local_name ? local_name : fbuf; - if (do_chmod(fname, mode & CHMOD_BITS) < 0) { + if (do_chmod(fname, mode) < 0) { rsyserr(FERROR, errno, "failed to modify permissions on %s", full_fname(fname)); } need_retouch_dir_perms = 1; } +#endif if (preserve_hard_links) check_for_finished_hlinks(itemizing, code); @@ -1230,13 +1383,15 @@ } recv_generator(NULL, NULL, 0, 0, 0, code, -1); if (delete_during) - delete_in_dir(NULL, NULL, NULL); + delete_in_dir(NULL, NULL, NULL, NULL); phase++; csum_length = SUM_LENGTH; - only_existing = max_size = opt_ignore_existing = 0; + max_size = min_size = ignore_existing = ignore_non_existing = 0; update_only = always_checksum = size_only = 0; ignore_times = 1; + if (append_mode) /* resend w/o append mode */ + append_mode = -1; /* ... but only longer files */ make_backups = 0; /* avoid a duplicate backup for inplace processing */ if (verbose > 2) @@ -1248,14 +1403,17 @@ * to catch initial checksum errors */ while ((i = get_redo_num(itemizing, code)) != -1) { struct file_struct *file = flist->files[i]; - recv_generator(local_name ? local_name : f_name_to(file, fbuf), - file, i, itemizing, maybe_PERMS_REPORT, code, - f_out); + if (local_name) + strlcpy(fbuf, local_name, sizeof fbuf); + else + f_name(file, fbuf); + recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT, + code, f_out); } phase++; - only_existing = save_only_existing; - opt_ignore_existing = save_opt_ignore_existing; + ignore_non_existing = save_ignore_non_existing; + ignore_existing = save_ignore_existing; make_backups = save_make_backups; if (verbose > 2) @@ -1283,20 +1441,20 @@ if (delete_after && !local_name && flist->count > 0) do_delete_pass(flist); - if ((need_retouch_dir_perms || need_retouch_dir_times) - && !list_only && !local_name && !dry_run) { + if ((need_retouch_dir_perms || need_retouch_dir_times) && dir_tweaking) { int j = 0; /* Now we need to fix any directory permissions that were * modified during the transfer and/or re-set any tweaked * modified-time values. */ for (i = 0; i < flist->count; i++) { struct file_struct *file = flist->files[i]; + if (!file->basename || !S_ISDIR(file->mode)) continue; if (!need_retouch_dir_times && file->mode & S_IWUSR) continue; - recv_generator(f_name(file), file, i, itemizing, - maybe_PERMS_REPORT, code, -1); + recv_generator(f_name(file, NULL), file, i, itemizing, + maybe_ATTRS_REPORT, code, -1); if (allowed_lull && !(++j % lull_mod)) maybe_send_keepalive(); else if (!(j % 200)) diff -urN --exclude=patches rsync-2.6.6/hlink.c rsync-2.6.7/hlink.c --- rsync-2.6.6/hlink.c 2005-06-09 14:56:11.000000000 -0700 +++ rsync-2.6.7/hlink.c 2006-02-24 08:43:44.000000000 -0800 @@ -20,10 +20,11 @@ #include "rsync.h" -extern int dry_run; extern int verbose; +extern int link_dest; extern int make_backups; extern int log_format_has_i; +extern char *basis_dir[]; extern struct file_list *the_file_list; #ifdef SUPPORT_HARD_LINKS @@ -173,8 +174,6 @@ { #ifdef SUPPORT_HARD_LINKS int head; - if (!file->link_u.links) - return 0; if (skip && !(file->flags & FLAG_HLINK_EOL)) head = hlink_list[file->F_HLINDEX] = file->F_NEXT; else @@ -183,16 +182,41 @@ struct file_struct *head_file = FPTR(head); if (!log_format_has_i && verbose > 1) { rprintf(FINFO, "\"%s\" is a hard link\n", - safe_fname(f_name(file))); + f_name(file, NULL)); } if (head_file->F_HLINDEX == FINISHED_LINK) { - STRUCT_STAT st2; - char *toname = f_name(head_file); + STRUCT_STAT st2, st3; + char *toname = f_name(head_file, NULL); if (link_stat(toname, &st2, 0) < 0) { rsyserr(FERROR, errno, "stat %s failed", full_fname(toname)); return -1; } + if (statret < 0 && basis_dir[0] != NULL) { + char cmpbuf[MAXPATHLEN]; + int j = 0; + do { + pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname); + if (link_stat(cmpbuf, &st3, 0) < 0) + continue; + if (link_dest) { + if (st2.st_dev != st3.st_dev + || st2.st_ino != st3.st_ino) + continue; + statret = 1; + st = &st3; + if (verbose < 2 || !log_format_has_i) + itemizing = code = 0; + break; + } + if (!unchanged_file(cmpbuf, file, &st3)) + continue; + statret = 1; + st = &st3; + if (unchanged_attrs(file, &st3)) + break; + } while (basis_dir[++j] != NULL); + } maybe_hard_link(file, ndx, fname, statret, st, toname, &st2, itemizing, code); file->F_HLINDEX = FINISHED_LINK; @@ -217,7 +241,7 @@ } else code = FERROR; rsyserr(code, errno, "link %s => %s failed", - full_fname(fname), safe_fname(toname)); + full_fname(fname), toname); return -1; } @@ -226,10 +250,8 @@ ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0, terse ? "" : toname); } - if (code && verbose && !terse) { - rprintf(code, "%s => %s\n", - safe_fname(fname), safe_fname(toname)); - } + if (code && verbose && !terse) + rprintf(code, "%s => %s\n", fname, toname); return 0; } #endif @@ -245,7 +267,7 @@ int statret, ndx = master; file->F_HLINDEX = FINISHED_LINK; - if (link_stat(f_name_to(file, hlink1), &st1, 0) < 0) + if (link_stat(f_name(file, hlink1), &st1, 0) < 0) return; if (!(file->flags & FLAG_HLINK_TOL)) { while (!(file->flags & FLAG_HLINK_EOL)) { @@ -258,7 +280,7 @@ file = FPTR(ndx); if (file->F_HLINDEX != SKIPPED_LINK) continue; - hlink2 = f_name(file); + hlink2 = f_name(file, NULL); statret = link_stat(hlink2, &st2, 0); maybe_hard_link(file, ndx, hlink2, statret, &st2, hlink1, &st1, itemizing, code); diff -urN --exclude=patches rsync-2.6.6/io.c rsync-2.6.7/io.c --- rsync-2.6.6/io.c 2005-05-19 01:52:13.000000000 -0700 +++ rsync-2.6.7/io.c 2006-02-26 19:12:26.000000000 -0800 @@ -41,7 +41,6 @@ extern int bwlimit; extern size_t bwlimit_writemax; -extern int verbose; extern int io_timeout; extern int allowed_lull; extern int am_server; @@ -105,6 +104,8 @@ static int io_filesfrom_buflen; static size_t contiguous_write_len = 0; static int select_timeout = SELECT_TIMEOUT; +static int active_filecnt = 0; +static OFF_T active_bytecnt = 0; static void read_loop(int fd, char *buf, size_t len); @@ -119,14 +120,17 @@ static struct flist_ndx_list redo_list, hlink_list; -struct msg_list { - struct msg_list *next; +struct msg_list_item { + struct msg_list_item *next; char *buf; int len; }; -static struct msg_list *msg_list_head; -static struct msg_list *msg_list_tail; +struct msg_list { + struct msg_list_item *head, *tail; +}; + +static struct msg_list msg_list; static void flist_ndx_push(struct flist_ndx_list *lp, int ndx) { @@ -224,9 +228,9 @@ /* Add a message to the pending MSG_* list. */ static void msg_list_add(int code, char *buf, int len) { - struct msg_list *ml; + struct msg_list_item *ml; - if (!(ml = new(struct msg_list))) + if (!(ml = new(struct msg_list_item))) out_of_memory("msg_list_add"); ml->next = NULL; if (!(ml->buf = new_array(char, len+4))) @@ -234,21 +238,11 @@ SIVAL(ml->buf, 0, ((code+MPLEX_BASE)<<24) | len); memcpy(ml->buf+4, buf, len); ml->len = len+4; - if (msg_list_tail) - msg_list_tail->next = ml; + if (msg_list.tail) + msg_list.tail->next = ml; else - msg_list_head = ml; - msg_list_tail = ml; -} - -void send_msg(enum msgcode code, char *buf, int len) -{ - if (msg_fd_out < 0) { - io_multiplex_write(code, buf, len); - return; - } - msg_list_add(code, buf, len); - msg_list_push(NORMAL_FLUSH); + msg_list.head = ml; + msg_list.tail = ml; } /* Read a message from the MSG_* fd and handle it. This is called either @@ -286,6 +280,8 @@ exit_cleanup(RERR_STREAMIO); } read_loop(fd, buf, 4); + if (remove_sent_files) + decrement_active_files(IVAL(buf,0)); flist_ndx_push(&redo_list, IVAL(buf,0)); break; case MSG_DELETED: @@ -302,11 +298,20 @@ exit_cleanup(RERR_STREAMIO); } read_loop(fd, buf, len); - if (remove_sent_files) + if (remove_sent_files) { + decrement_active_files(IVAL(buf,0)); io_multiplex_write(MSG_SUCCESS, buf, len); + } if (preserve_hard_links) flist_ndx_push(&hlink_list, IVAL(buf,0)); break; + case MSG_SOCKERR: + if (!am_generator) { + rprintf(FERROR, "invalid message %d:%d\n", tag, len); + exit_cleanup(RERR_STREAMIO); + } + close_multiplexing_out(); + /* FALL THROUGH */ case MSG_INFO: case MSG_ERROR: case MSG_LOG: @@ -327,10 +332,32 @@ msg_fd_in = fd; } +/* This is used by the generator to limit how many file transfers can + * be active at once when --remove-sent-files is specified. Without + * this, sender-side deletions were mostly happening at the end. */ +void increment_active_files(int ndx, int itemizing, enum logcode code) +{ + /* TODO: tune these limits? */ + while (active_filecnt >= (active_bytecnt >= 128*1024 ? 10 : 50)) { + if (hlink_list.head) + check_for_finished_hlinks(itemizing, code); + read_msg_fd(); + } + + active_filecnt++; + active_bytecnt += the_file_list->files[ndx]->length; +} + +void decrement_active_files(int ndx) +{ + active_filecnt--; + active_bytecnt -= the_file_list->files[ndx]->length; +} + /* 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 active in the receiver. */ -int msg_list_push(int flush_it_all) +static int msg_list_flush(int flush_it_all) { static int written = 0; struct timeval tv; @@ -339,8 +366,8 @@ if (msg_fd_out < 0) return -1; - while (msg_list_head) { - struct msg_list *ml = msg_list_head; + while (msg_list.head) { + struct msg_list_item *ml = msg_list.head; int n = write(msg_fd_out, ml->buf + written, ml->len - written); if (n < 0) { if (errno == EINTR) @@ -357,9 +384,9 @@ check_timeout(); } else if ((written += n) == ml->len) { free(ml->buf); - msg_list_head = ml->next; - if (!msg_list_head) - msg_list_tail = NULL; + msg_list.head = ml->next; + if (!msg_list.head) + msg_list.tail = NULL; free(ml); written = 0; } @@ -367,6 +394,16 @@ return 1; } +void send_msg(enum msgcode code, char *buf, int len) +{ + if (msg_fd_out < 0) { + io_multiplex_write(code, buf, len); + return; + } + msg_list_add(code, buf, len); + msg_list_flush(NORMAL_FLUSH); +} + int get_redo_num(int itemizing, enum logcode code) { while (1) { @@ -434,7 +471,6 @@ exit_cleanup(RERR_STREAMIO); } - /** * 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. @@ -448,11 +484,11 @@ */ static int read_timeout(int fd, char *buf, size_t len) { - int n, ret = 0; + int n, cnt = 0; io_flush(NORMAL_FLUSH); - while (ret == 0) { + while (cnt == 0) { /* until we manage to read *something* */ fd_set r_fds, w_fds; struct timeval tv; @@ -462,7 +498,7 @@ FD_ZERO(&r_fds); FD_ZERO(&w_fds); FD_SET(fd, &r_fds); - if (msg_list_head) { + if (msg_list.head) { FD_SET(msg_fd_out, &w_fds); if (msg_fd_out > maxfd) maxfd = msg_fd_out; @@ -499,8 +535,8 @@ continue; } - if (msg_list_head && FD_ISSET(msg_fd_out, &w_fds)) - msg_list_push(NORMAL_FLUSH); + if (msg_list.head && FD_ISSET(msg_fd_out, &w_fds)) + msg_list_flush(NORMAL_FLUSH); if (io_filesfrom_f_out >= 0) { if (io_filesfrom_buflen) { @@ -578,21 +614,23 @@ continue; /* Don't write errors on a dead socket. */ - if (fd == sock_f_in) + if (fd == sock_f_in) { close_multiplexing_out(); - rsyserr(FERROR, errno, "read error"); + rsyserr(FSOCKERR, errno, "read error"); + } else + rsyserr(FERROR, errno, "read error"); exit_cleanup(RERR_STREAMIO); } buf += n; len -= n; - ret += n; + cnt += n; if (fd == sock_f_in && io_timeout) last_io_in = time(NULL); } - return ret; + return cnt; } /** @@ -642,7 +680,6 @@ return s - fname; } - static char *iobuf_out; static int iobuf_out_cnt; @@ -655,7 +692,6 @@ iobuf_out_cnt = 0; } - static char *iobuf_in; static size_t iobuf_in_siz; @@ -668,7 +704,6 @@ out_of_memory("io_start_buffering_in"); } - void io_end_buffering(void) { io_flush(NORMAL_FLUSH); @@ -678,14 +713,12 @@ } } - void maybe_flush_socket(void) { if (iobuf_out && iobuf_out_cnt && time(NULL) - last_io_out >= 5) io_flush(NORMAL_FLUSH); } - void maybe_send_keepalive(void) { if (time(NULL) - last_io_out >= allowed_lull) { @@ -700,7 +733,6 @@ } } - /** * Continue trying to read len bytes - don't return until len has been * read. @@ -715,7 +747,6 @@ } } - /** * Read from the file descriptor handling multiplexing - return number * of bytes read. @@ -727,12 +758,8 @@ static size_t remaining; static size_t iobuf_in_ndx; size_t msg_bytes; - int tag, ret = 0; -#if MAXPATHLEN < 4096 - char line[4096+1024]; -#else - char line[MAXPATHLEN+1024]; -#endif + int tag, cnt = 0; + char line[BIGPATHBUFLEN]; if (!iobuf_in || fd != sock_f_in) return read_timeout(fd, buf, len); @@ -742,13 +769,13 @@ iobuf_in_ndx = 0; } - while (ret == 0) { + while (cnt == 0) { if (remaining) { len = MIN(len, remaining); memcpy(buf, iobuf_in + iobuf_in_ndx, len); iobuf_in_ndx += len; remaining -= len; - ret = len; + cnt = len; break; } @@ -774,12 +801,13 @@ if (msg_bytes >= sizeof line) goto overflow; read_loop(fd, line, msg_bytes); - line[msg_bytes] = '\0'; /* A directory name was sent with the trailing null */ if (msg_bytes > 0 && !line[msg_bytes-1]) log_delete(line, S_IFDIR); - else + else { + line[msg_bytes] = '\0'; log_delete(line, S_IFREG); + } break; case MSG_SUCCESS: if (msg_bytes != 4) { @@ -812,11 +840,9 @@ if (remaining == 0) io_flush(NORMAL_FLUSH); - return ret; + return cnt; } - - /** * Do a buffered read from @p fd. Don't return until all @p n bytes * have been read. If all @p n can't be read then exit with an @@ -824,12 +850,12 @@ **/ static void readfd(int fd, char *buffer, size_t N) { - int ret; + int cnt; size_t total = 0; while (total < N) { - ret = readfd_unbuffered(fd, buffer + total, N-total); - total += ret; + cnt = readfd_unbuffered(fd, buffer + total, N-total); + total += cnt; } if (fd == write_batch_monitor_in) { @@ -841,7 +867,6 @@ stats.total_read += total; } - int read_shortint(int f) { uchar b[2]; @@ -849,37 +874,36 @@ return (b[1] << 8) + b[0]; } - int32 read_int(int f) { char b[4]; - int32 ret; + int32 num; readfd(f,b,4); - ret = IVAL(b,0); - if (ret == (int32)0xffffffff) + num = IVAL(b,0); + if (num == (int32)0xffffffff) return -1; - return ret; + return num; } int64 read_longint(int f) { - int64 ret; + int64 num; char b[8]; - ret = read_int(f); + num = read_int(f); - if ((int32)ret != (int32)0xffffffff) - return ret; + if ((int32)num != (int32)0xffffffff) + return num; #if SIZEOF_INT64 < 8 rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n"); exit_cleanup(RERR_UNSUPPORTED); #else readfd(f,b,8); - ret = IVAL(b,0) | (((int64)IVAL(b,4))<<32); + num = IVAL(b,0) | (((int64)IVAL(b,4))<<32); #endif - return ret; + return num; } void read_buf(int f,char *buf,size_t len) @@ -924,6 +948,11 @@ void read_sum_head(int f, struct sum_struct *sum) { sum->count = read_int(f); + if (sum->count < 0) { + rprintf(FERROR, "Invalid checksum count %ld [%s]\n", + (long)sum->count, who_am_i()); + exit_cleanup(RERR_PROTOCOL); + } sum->blength = read_int(f); if (sum->blength < 0 || sum->blength > MAX_BLOCK_SIZE) { rprintf(FERROR, "Invalid block length %ld [%s]\n", @@ -961,7 +990,6 @@ write_int(f, sum->remainder); } - /** * Sleep after writing to limit I/O bandwidth usage. * @@ -987,7 +1015,7 @@ #define ONE_SEC 1000000L /* # of microseconds in a second */ - if (!bwlimit) + if (!bwlimit_writemax) return; total_written += bytes_written; @@ -1017,9 +1045,8 @@ total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_SEC/1024); } - /* Write len bytes to the file descriptor fd, looping as necessary to get - * the job done and also (in certain circumstnces) reading any data on + * the job done and also (in certain circumstances) reading any data on * msg_fd_in to avoid deadlock. * * This function underlies the multiplexing system. The body of the @@ -1028,7 +1055,7 @@ { size_t n, total = 0; fd_set w_fds, r_fds; - int maxfd, count, ret, using_r_fds; + int maxfd, count, cnt, using_r_fds; struct timeval tv; no_flush++; @@ -1068,12 +1095,12 @@ continue; n = len - total; - if (bwlimit && n > bwlimit_writemax) + if (bwlimit_writemax && n > bwlimit_writemax) n = bwlimit_writemax; - ret = write(fd, buf + total, n); + cnt = write(fd, buf + total, n); - if (ret <= 0) { - if (ret < 0) { + if (cnt <= 0) { + if (cnt < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK || errno == EAGAIN) { @@ -1099,26 +1126,25 @@ exit_cleanup(RERR_STREAMIO); } - total += ret; + total += cnt; if (fd == sock_f_out) { if (io_timeout || am_generator) last_io_out = time(NULL); - sleep_for_bwlimit(ret); + sleep_for_bwlimit(cnt); } } no_flush--; } - /** * Write an message to a multiplexed stream. If this fails then rsync * exits. **/ static void mplex_write(enum msgcode code, char *buf, size_t len) { - char buffer[4096]; + char buffer[1024]; size_t n = len; SIVAL(buffer, 0, ((MPLEX_BASE + (int)code)<<24) + len); @@ -1131,9 +1157,10 @@ contiguous_write_len = len + 4; if (n > sizeof buffer - 4) - n = sizeof buffer - 4; + n = 0; + else + memcpy(buffer + 4, buf, n); - memcpy(&buffer[4], buf, n); writefd_unbuffered(sock_f_out, buffer, n+4); len -= n; @@ -1146,10 +1173,9 @@ contiguous_write_len = 0; } - void io_flush(int flush_it_all) { - msg_list_push(flush_it_all); + msg_list_flush(flush_it_all); if (!iobuf_out_cnt || no_flush) return; @@ -1161,7 +1187,6 @@ iobuf_out_cnt = 0; } - static void writefd(int fd,char *buf,size_t len) { if (fd == msg_fd_out) { @@ -1196,7 +1221,6 @@ } } - void write_shortint(int f, int x) { uchar b[2]; @@ -1205,7 +1229,6 @@ writefd(f, (char *)b, 2); } - void write_int(int f,int32 x) { char b[4]; @@ -1213,7 +1236,6 @@ writefd(f,b,4); } - void write_int_named(int f, int32 x, const char *phase) { io_write_phase = phase; @@ -1221,7 +1243,6 @@ io_write_phase = phase_unknown; } - /* * Note: int64 may actually be a 32-bit type if ./configure couldn't find any * 64-bit types on this platform. @@ -1283,7 +1304,6 @@ writefd(f, str, len); } - /** * Read a line of up to @p maxlen characters into @p buf (not counting * the trailing null). Strips the (required) trailing newline and all @@ -1309,11 +1329,10 @@ return maxlen > 0; } - void io_printf(int fd, const char *format, ...) { va_list ap; - char buf[1024]; + char buf[BIGPATHBUFLEN]; int len; va_start(ap, format); @@ -1323,10 +1342,14 @@ if (len < 0) exit_cleanup(RERR_STREAMIO); + if (len > (int)sizeof buf) { + rprintf(FERROR, "io_printf() was too long for the buffer.\n"); + exit_cleanup(RERR_STREAMIO); + } + write_sbuf(fd, buf); } - /** Setup for multiplexing a MSG_* stream with the data stream. */ void io_start_multiplex_out(void) { diff -urN --exclude=patches rsync-2.6.6/lib/permstring.c rsync-2.6.7/lib/permstring.c --- rsync-2.6.6/lib/permstring.c 2003-01-10 12:08:43.000000000 -0800 +++ rsync-2.6.7/lib/permstring.c 2006-01-29 16:39:59.000000000 -0800 @@ -26,8 +26,7 @@ * * @param buf buffer of at least 11 characters **/ -void permstring(char *perms, - int mode) +void permstring(char *perms, mode_t mode) { static const char *perm_map = "rwxrwxrwx"; int i; diff -urN --exclude=patches rsync-2.6.6/lib/permstring.h rsync-2.6.7/lib/permstring.h --- rsync-2.6.6/lib/permstring.h 2001-11-26 22:43:17.000000000 -0800 +++ rsync-2.6.7/lib/permstring.h 2006-01-29 16:39:59.000000000 -0800 @@ -1,3 +1,3 @@ #define PERMSTRING_SIZE 11 -void permstring(char *perms, int mode); +void permstring(char *perms, mode_t mode); diff -urN --exclude=patches rsync-2.6.6/lib/pool_alloc.c rsync-2.6.7/lib/pool_alloc.c --- rsync-2.6.6/lib/pool_alloc.c 2005-01-20 14:37:38.000000000 -0800 +++ rsync-2.6.7/lib/pool_alloc.c 2005-11-14 13:24:30.000000000 -0800 @@ -56,8 +56,7 @@ pool->size = size /* round extent size to min alignment reqs */ ? (size + MINALIGN - 1) & ~(MINALIGN - 1) : POOL_DEF_EXTENT; - if (pool->flags & POOL_INTERN) - { + if (pool->flags & POOL_INTERN) { pool->size -= sizeof (struct pool_extent); flags |= POOL_APPEND; } @@ -77,15 +76,13 @@ if (!pool) return; - if (pool->live) - { + if (pool->live) { cur = pool->live; free(cur->start); if (!(pool->flags & POOL_APPEND)) free(cur); } - for (cur = pool->free; cur; cur = next) - { + for (cur = pool->free; cur; cur = next) { next = cur->next; free(cur->start); if (!(pool->flags & POOL_APPEND)) @@ -109,16 +106,14 @@ if (len > pool->size) goto bomb; - if (!pool->live || len > pool->live->free) - { + if (!pool->live || len > pool->live->free) { void *start; size_t free; size_t bound; size_t sqew; size_t asize; - if (pool->live) - { + if (pool->live) { pool->live->next = pool->free; pool->free = pool->live; } @@ -137,16 +132,11 @@ memset(start, 0, pool->size); if (pool->flags & POOL_APPEND) - { pool->live = PTR_ADD(start, free); - } else if (!(pool->live = (struct pool_extent *) malloc(sizeof (struct pool_extent)))) - { goto bomb; - } if (pool->flags & POOL_QALIGN && pool->quantum > 1 - && (sqew = (size_t)PTR_ADD(start, free) % pool->quantum)) - { + && (sqew = (size_t)PTR_ADD(start, free) % pool->quantum)) { bound += sqew; free -= sqew; } @@ -186,8 +176,7 @@ else if (pool->quantum > 1 && len % pool->quantum) len += pool->quantum - len % pool->quantum; - if (!addr && pool->live) - { + if (!addr && pool->live) { pool->live->next = pool->free; pool->free = pool->live; pool->live = NULL; @@ -197,35 +186,28 @@ pool->b_freed += len; cur = pool->live; - if (cur - && addr >= cur->start - && addr < PTR_ADD(cur->start, pool->size)) - { - if (addr == PTR_ADD(cur->start, cur->free)) - { + if (cur && addr >= cur->start + && addr < PTR_ADD(cur->start, pool->size)) { + if (addr == PTR_ADD(cur->start, cur->free)) { if (pool->flags & POOL_CLEAR) memset(addr, 0, len); pool->b_freed += len; - } else { + } else cur->bound += len; - } - if (cur->free + cur->bound >= pool->size) - { + if (cur->free + cur->bound >= pool->size) { size_t sqew; cur->free = pool->size; cur->bound = 0; if (pool->flags & POOL_QALIGN && pool->quantum > 1 - && (sqew = (size_t)PTR_ADD(cur->start, cur->free) % pool->quantum)) - { + && (sqew = (size_t)PTR_ADD(cur->start, cur->free) % pool->quantum)) { cur->bound += sqew; cur->free -= sqew; } } return; } - for (prev = NULL, cur = pool->free; cur; prev = cur, cur = cur->next) - { + for (prev = NULL, cur = pool->free; cur; prev = cur, cur = cur->next) { if (addr >= cur->start && addr < PTR_ADD(cur->start, pool->size)) break; @@ -233,16 +215,14 @@ if (!cur) return; - if (prev) - { + if (prev) { prev->next = cur->next; cur->next = pool->free; pool->free = cur; } cur->bound += len; - if (cur->free + cur->bound >= pool->size) - { + if (cur->free + cur->bound >= pool->size) { pool->free = cur->next; free(cur->start); @@ -254,11 +234,11 @@ } #define FDPRINT(label, value) \ - snprintf(buf, BUFSIZ, label, value), \ - write(fd, buf, strlen(buf)); + snprintf(buf, sizeof buf, label, value), \ + write(fd, buf, strlen(buf)) #define FDEXTSTAT(ext) \ - snprintf(buf, BUFSIZ, " %12ld %5ld\n", \ + snprintf(buf, sizeof buf, " %12ld %5ld\n", \ (long) ext->free, \ (long) ext->bound), \ write(fd, buf, strlen(buf)) @@ -291,14 +271,10 @@ write(fd, "\n", 1); if (pool->live) - { FDEXTSTAT(pool->live); - } strcpy(buf, " FREE BOUND\n"); write(fd, buf, strlen(buf)); for (cur = pool->free; cur; cur = cur->next) - { FDEXTSTAT(cur); - } } diff -urN --exclude=patches rsync-2.6.6/lib/snprintf.c rsync-2.6.7/lib/snprintf.c --- rsync-2.6.6/lib/snprintf.c 2005-02-21 09:02:53.000000000 -0800 +++ rsync-2.6.7/lib/snprintf.c 2005-10-11 13:21:03.000000000 -0700 @@ -146,7 +146,7 @@ #endif #ifndef VA_COPY -#ifdef HAVE_VA_COPY +#if defined HAVE_VA_COPY || defined va_copy #define VA_COPY(dest, src) va_copy(dest, src) #else #ifdef HAVE___VA_COPY diff -urN --exclude=patches rsync-2.6.6/lib/wildmatch.c rsync-2.6.7/lib/wildmatch.c --- rsync-2.6.6/lib/wildmatch.c 2005-01-28 13:01:21.000000000 -0800 +++ rsync-2.6.7/lib/wildmatch.c 2006-01-02 09:46:15.000000000 -0800 @@ -57,173 +57,312 @@ int wildmatch_iteration_count; #endif -static int domatch(const uchar *p, const uchar *text) +static int force_lower_case = 0; + +/* Match pattern "p" against the a virtually-joined string consisting + * of "text" and any strings in array "a". */ +static int dowild(const uchar *p, const uchar *text, const uchar*const *a) { - int matched, special; - uchar ch, prev; + uchar p_ch; #ifdef WILD_TEST_ITERATIONS wildmatch_iteration_count++; #endif - for ( ; (ch = *p) != '\0'; text++, p++) { - if (*text == '\0' && ch != '*') - return FALSE; - switch (ch) { + for ( ; (p_ch = *p) != '\0'; text++, p++) { + int matched, special; + uchar t_ch, prev_ch; + while ((t_ch = *text) == '\0') { + if (*a == NULL) { + if (p_ch != '*') + return ABORT_ALL; + break; + } + text = *a++; + } + if (force_lower_case && ISUPPER(t_ch)) + t_ch = tolower(t_ch); + switch (p_ch) { case '\\': /* Literal match with following character. Note that the test * in "default" handles the p[1] == '\0' failure case. */ - ch = *++p; + p_ch = *++p; /* FALLTHROUGH */ default: - if (*text != ch) + if (t_ch != p_ch) return FALSE; continue; case '?': /* Match anything but '/'. */ - if (*text == '/') + if (t_ch == '/') return FALSE; continue; case '*': if (*++p == '*') { while (*++p == '*') {} special = TRUE; - } - else + } else special = FALSE; if (*p == '\0') { /* Trailing "**" matches everything. Trailing "*" matches * only if there are no more slash characters. */ - return special? TRUE : strchr((char*)text, '/') == NULL; + if (!special) { + do { + if (strchr((char*)text, '/') != NULL) + return FALSE; + } while ((text = *a++) != NULL); + } + return TRUE; } - for ( ; *text; text++) { - if ((matched = domatch(p, text)) != FALSE) { + while (1) { + if (t_ch == '\0') { + if ((text = *a++) == NULL) + break; + t_ch = *text; + continue; + } + if ((matched = dowild(p, text, a)) != FALSE) { if (!special || matched != ABORT_TO_STARSTAR) return matched; - } - else if (!special && *text == '/') + } else if (!special && t_ch == '/') return ABORT_TO_STARSTAR; + t_ch = *++text; } return ABORT_ALL; case '[': - ch = *++p; + p_ch = *++p; #ifdef NEGATE_CLASS2 - if (ch == NEGATE_CLASS2) - ch = NEGATE_CLASS; + if (p_ch == NEGATE_CLASS2) + p_ch = NEGATE_CLASS; #endif /* Assign literal TRUE/FALSE because of "matched" comparison. */ - special = ch == NEGATE_CLASS? TRUE : FALSE; + special = p_ch == NEGATE_CLASS? TRUE : FALSE; if (special) { /* Inverted character class. */ - ch = *++p; + p_ch = *++p; } - prev = 0; + prev_ch = 0; matched = FALSE; do { - if (!ch) + if (!p_ch) return ABORT_ALL; - if (ch == '\\') { - ch = *++p; - if (!ch) + if (p_ch == '\\') { + p_ch = *++p; + if (!p_ch) return ABORT_ALL; - if (*text == ch) + if (t_ch == p_ch) matched = TRUE; - } - else if (ch == '-' && prev && p[1] && p[1] != ']') { - ch = *++p; - if (ch == '\\') { - ch = *++p; - if (!ch) + } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') { + p_ch = *++p; + if (p_ch == '\\') { + p_ch = *++p; + if (!p_ch) return ABORT_ALL; } - if (*text <= ch && *text >= prev) + if (t_ch <= p_ch && t_ch >= prev_ch) matched = TRUE; - ch = 0; /* This makes "prev" get set to 0. */ - } - else if (ch == '[' && p[1] == ':') { + p_ch = 0; /* This makes "prev_ch" get set to 0. */ + } else if (p_ch == '[' && p[1] == ':') { const uchar *s; int i; - for (s = p += 2; (ch = *p) && ch != ']'; p++) {} - if (!ch) + for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} + if (!p_ch) return ABORT_ALL; i = p - s - 1; if (i < 0 || p[-1] != ':') { /* Didn't find ":]", so treat like a normal set. */ p = s - 2; - ch = '['; - if (*text == ch) + p_ch = '['; + if (t_ch == p_ch) matched = TRUE; continue; } if (CC_EQ(s,i, "alnum")) { - if (ISALNUM(*text)) + if (ISALNUM(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "alpha")) { - if (ISALPHA(*text)) + } else if (CC_EQ(s,i, "alpha")) { + if (ISALPHA(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "blank")) { - if (ISBLANK(*text)) + } else if (CC_EQ(s,i, "blank")) { + if (ISBLANK(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "cntrl")) { - if (ISCNTRL(*text)) + } else if (CC_EQ(s,i, "cntrl")) { + if (ISCNTRL(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "digit")) { - if (ISDIGIT(*text)) + } else if (CC_EQ(s,i, "digit")) { + if (ISDIGIT(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "graph")) { - if (ISGRAPH(*text)) + } else if (CC_EQ(s,i, "graph")) { + if (ISGRAPH(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "lower")) { - if (ISLOWER(*text)) + } else if (CC_EQ(s,i, "lower")) { + if (ISLOWER(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "print")) { - if (ISPRINT(*text)) + } else if (CC_EQ(s,i, "print")) { + if (ISPRINT(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "punct")) { - if (ISPUNCT(*text)) + } else if (CC_EQ(s,i, "punct")) { + if (ISPUNCT(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "space")) { - if (ISSPACE(*text)) + } else if (CC_EQ(s,i, "space")) { + if (ISSPACE(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "upper")) { - if (ISUPPER(*text)) + } else if (CC_EQ(s,i, "upper")) { + if (ISUPPER(t_ch)) matched = TRUE; - } - else if (CC_EQ(s,i, "xdigit")) { - if (ISXDIGIT(*text)) + } else if (CC_EQ(s,i, "xdigit")) { + if (ISXDIGIT(t_ch)) matched = TRUE; - } - else /* malformed [:class:] string */ + } else /* malformed [:class:] string */ return ABORT_ALL; - ch = 0; /* This makes "prev" get set to 0. */ - } - else if (*text == ch) + p_ch = 0; /* This makes "prev_ch" get set to 0. */ + } else if (t_ch == p_ch) matched = TRUE; - } while (prev = ch, (ch = *++p) != ']'); - if (matched == special || *text == '/') + } while (prev_ch = p_ch, (p_ch = *++p) != ']'); + if (matched == special || t_ch == '/') return FALSE; continue; } } - return *text == '\0'; + do { + if (*text) + return FALSE; + } while ((text = *a++) != NULL); + + return TRUE; +} + +/* Match literal string "s" against the a virtually-joined string consisting + * of "text" and any strings in array "a". */ +static int doliteral(const uchar *s, const uchar *text, const uchar*const *a) +{ + for ( ; *s != '\0'; text++, s++) { + while (*text == '\0') { + if ((text = *a++) == NULL) + return FALSE; + } + if (*text != *s) + return FALSE; + } + + do { + if (*text) + return FALSE; + } while ((text = *a++) != NULL); + + return TRUE; +} + +/* Return the last "count" path elements from the concatenated string. + * We return a string pointer to the start of the string, and update the + * array pointer-pointer to point to any remaining string elements. */ +static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count) +{ + const uchar*const *a = *a_ptr; + const uchar*const *first_a = a; + + while (*a) + a++; + + while (a != first_a) { + const uchar *s = *--a; + s += strlen((char*)s); + while (--s >= *a) { + if (*s == '/' && !--count) { + *a_ptr = a+1; + return s+1; + } + } + } + + if (count == 1) { + *a_ptr = a+1; + return *a; + } + + return NULL; +} + +/* Match the "pattern" against the "text" string. */ +int wildmatch(const char *pattern, const char *text) +{ + static const uchar *nomore[1]; /* A NULL pointer. */ +#ifdef WILD_TEST_ITERATIONS + wildmatch_iteration_count = 0; +#endif + return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; } -/* Find the pattern (p) in the text string (t). */ -int wildmatch(const char *p, const char *t) +/* Match the "pattern" against the forced-to-lower-case "text" string. */ +int iwildmatch(const char *pattern, const char *text) { + static const uchar *nomore[1]; /* A NULL pointer. */ + int ret; #ifdef WILD_TEST_ITERATIONS wildmatch_iteration_count = 0; #endif - return domatch((const uchar*)p, (const uchar*)t) == TRUE; + force_lower_case = 1; + ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; + force_lower_case = 0; + return ret; +} + +/* Match pattern "p" against the a virtually-joined string consisting + * of all the pointers in array "texts" (which has a NULL pointer at the + * end). The int "where" can be 0 (normal matching), > 0 (match only + * the trailing N slash-separated filename components of "texts"), or < 0 + * (match the "pattern" at the start or after any slash in "texts"). */ +int wildmatch_array(const char *pattern, const char*const *texts, int where) +{ + const uchar *p = (const uchar*)pattern; + const uchar*const *a = (const uchar*const*)texts; + const uchar *text; + int matched; + +#ifdef WILD_TEST_ITERATIONS + wildmatch_iteration_count = 0; +#endif + + if (where > 0) + text = trailing_N_elements(&a, where); + else + text = *a++; + if (!text) + return FALSE; + + if ((matched = dowild(p, text, a)) != TRUE && where < 0 + && matched != ABORT_ALL) { + while (1) { + if (*text == '\0') { + if ((text = (uchar*)*a++) == NULL) + return FALSE; + continue; + } + if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE + && matched != ABORT_TO_STARSTAR) + break; + } + } + return matched == TRUE; +} + +/* Match literal string "s" against the a virtually-joined string consisting + * of all the pointers in array "texts" (which has a NULL pointer at the + * end). The int "where" can be 0 (normal matching), or > 0 (match + * only the trailing N slash-separated filename components of "texts"). */ +int litmatch_array(const char *string, const char*const *texts, int where) +{ + const uchar *s = (const uchar*)string; + const uchar*const *a = (const uchar* const*)texts; + const uchar *text; + + if (where > 0) + text = trailing_N_elements(&a, where); + else + text = *a++; + if (!text) + return FALSE; + + return doliteral(s, text, a) == TRUE; } diff -urN --exclude=patches rsync-2.6.6/lib/wildmatch.h rsync-2.6.7/lib/wildmatch.h --- rsync-2.6.6/lib/wildmatch.h 2003-07-04 17:03:36.000000000 -0700 +++ rsync-2.6.7/lib/wildmatch.h 2006-01-02 09:46:12.000000000 -0800 @@ -1,3 +1,6 @@ /* wildmatch.h */ -int wildmatch(const char *p, const char *text); +int wildmatch(const char *pattern, const char *text); +int iwildmatch(const char *pattern, const char *text); +int wildmatch_array(const char *pattern, const char*const *texts, int where); +int litmatch_array(const char *string, const char*const *texts, int where); diff -urN --exclude=patches rsync-2.6.6/loadparm.c rsync-2.6.7/loadparm.c --- rsync-2.6.6/loadparm.c 2005-06-10 09:57:43.000000000 -0700 +++ rsync-2.6.7/loadparm.c 2006-01-30 13:47:02.000000000 -0800 @@ -98,87 +98,107 @@ */ typedef struct { - char *motd_file; + char *bind_address; char *log_file; + char *motd_file; char *pid_file; char *socket_options; - char *bind_address; - int syslog_facility; + int rsync_port; + int syslog_facility; } global; static global Globals; /* - * This structure describes a single service. + * This structure describes a single service. Their order must match the + * initializers below, which you can accomplish by keeping each sub-section + * sorted. (e.g. in vim, just visually select each subsection and use !sort.) */ typedef struct { - char *name; - char *path; + char *auth_users; char *comment; - char *lock_file; - BOOL read_only; - BOOL write_only; - BOOL list; - BOOL use_chroot; - BOOL transfer_logging; - BOOL ignore_errors; - char *uid; + char *dont_compress; + char *exclude; + char *exclude_from; + char *filter; char *gid; char *hosts_allow; char *hosts_deny; - char *auth_users; - char *secrets_file; - BOOL strict_modes; - char *filter; - char *exclude; - char *exclude_from; char *include; char *include_from; + char *incoming_chmod; + char *lock_file; char *log_format; + char *name; + char *outgoing_chmod; + char *path; + char *postxfer_exec; + char *prexfer_exec; char *refuse_options; - char *dont_compress; - int timeout; + char *secrets_file; + char *temp_dir; + char *uid; + int max_connections; int max_verbosity; + int timeout; + + BOOL ignore_errors; BOOL ignore_nonreadable; + BOOL list; + BOOL read_only; + BOOL strict_modes; + BOOL transfer_logging; + BOOL use_chroot; + BOOL write_only; } service; -/* This is a default service used to prime a services structure */ +/* This is a default service used to prime a services structure. In order + * to make these easy to keep sorted in the same way as the variables + * above, use the variable name in the leading comment, including a + * trailing ';' (to avoid a sorting problem with trailing digits). */ static service sDefault = { - NULL, /* name */ - NULL, /* path */ - NULL, /* comment */ - DEFAULT_LOCK_FILE, /* lock file */ - True, /* read only */ - False, /* write only */ - True, /* list */ - True, /* use chroot */ - False, /* transfer logging */ - False, /* ignore errors */ - NOBODY_USER,/* uid */ - NOBODY_GROUP,/* gid */ - NULL, /* hosts allow */ - NULL, /* hosts deny */ - NULL, /* auth users */ - NULL, /* secrets file */ - True, /* strict modes */ - NULL, /* filter */ - NULL, /* exclude */ - NULL, /* exclude from */ - NULL, /* include */ - NULL, /* include from */ - "%o %h [%a] %m (%u) %f %l", /* log format */ - NULL, /* refuse options */ - "*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz", /* dont compress */ - 0, /* timeout */ - 0, /* max connections */ - 1, /* max verbosity */ - False /* ignore nonreadable */ + /* auth_users; */ NULL, + /* comment; */ NULL, + /* dont_compress; */ "*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz", + /* exclude; */ NULL, + /* exclude_from; */ NULL, + /* filter; */ NULL, + /* gid; */ NOBODY_GROUP, + /* hosts_allow; */ NULL, + /* hosts_deny; */ NULL, + /* include; */ NULL, + /* include_from; */ NULL, + /* incoming_chmod; */ NULL, + /* lock_file; */ DEFAULT_LOCK_FILE, + /* log_format; */ "%o %h [%a] %m (%u) %f %l", + /* name; */ NULL, + /* outgoing_chmod; */ NULL, + /* path; */ NULL, + /* postxfer_exec; */ NULL, + /* prexfer_exec; */ NULL, + /* refuse_options; */ NULL, + /* secrets_file; */ NULL, + /* temp_dir; */ NULL, + /* uid; */ NOBODY_USER, + + /* max_connections; */ 0, + /* max_verbosity; */ 1, + /* timeout; */ 0, + + /* ignore_errors; */ False, + /* ignore_nonreadable; */ False, + /* list; */ True, + /* read_only; */ True, + /* strict_modes; */ True, + /* transfer_logging; */ False, + /* use_chroot; */ True, + /* write_only; */ False, }; @@ -261,44 +281,51 @@ /* note that we do not initialise the defaults union - it is not allowed in ANSI C */ static struct parm_struct parm_table[] = { - {"motd file", P_STRING, P_GLOBAL, &Globals.motd_file, NULL, 0}, - {"syslog facility", P_ENUM, P_GLOBAL, &Globals.syslog_facility, enum_facilities,0}, - {"socket options", P_STRING, P_GLOBAL, &Globals.socket_options,NULL, 0}, - {"log file", P_STRING, P_GLOBAL, &Globals.log_file, NULL, 0}, - {"pid file", P_STRING, P_GLOBAL, &Globals.pid_file, NULL, 0}, - {"port", P_INTEGER, P_GLOBAL, &Globals.rsync_port, NULL, 0}, - {"address", P_STRING, P_GLOBAL, &Globals.bind_address, NULL, 0}, - - {"timeout", P_INTEGER, P_LOCAL, &sDefault.timeout, NULL, 0}, - {"max connections", P_INTEGER, P_LOCAL, &sDefault.max_connections,NULL, 0}, - {"max verbosity", P_INTEGER, P_LOCAL, &sDefault.max_verbosity,NULL, 0}, - {"name", P_STRING, P_LOCAL, &sDefault.name, NULL, 0}, - {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL, 0}, - {"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}, - {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL, 0}, - {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL, 0}, - {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL, 0}, - {"hosts deny", P_STRING, P_LOCAL, &sDefault.hosts_deny, NULL, 0}, - {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL, 0}, - {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file,NULL, 0}, - {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes,NULL, 0}, - {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL, 0}, - {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL, 0}, - {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from,NULL, 0}, - {"include", P_STRING, P_LOCAL, &sDefault.include, NULL, 0}, - {"include from", P_STRING, P_LOCAL, &sDefault.include_from,NULL, 0}, - {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging,NULL,0}, - {"ignore errors", P_BOOL, P_LOCAL, &sDefault.ignore_errors,NULL,0}, - {"log format", P_STRING, P_LOCAL, &sDefault.log_format, NULL, 0}, - {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options,NULL, 0}, - {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress,NULL, 0}, - {NULL, P_BOOL, P_NONE, NULL, NULL, 0} + {"address", P_STRING, P_GLOBAL,&Globals.bind_address, NULL,0}, + {"log file", P_STRING, P_GLOBAL,&Globals.log_file, NULL,0}, + {"motd file", P_STRING, P_GLOBAL,&Globals.motd_file, NULL,0}, + {"pid file", P_STRING, P_GLOBAL,&Globals.pid_file, NULL,0}, + {"port", P_INTEGER,P_GLOBAL,&Globals.rsync_port, NULL,0}, + {"socket options", P_STRING, P_GLOBAL,&Globals.socket_options, NULL,0}, + {"syslog facility", P_ENUM, P_GLOBAL,&Globals.syslog_facility,enum_facilities,0}, + + {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL,0}, + {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL,0}, + {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0}, + {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0}, + {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0}, + {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0}, + {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0}, + {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0}, + {"hosts deny", P_STRING, P_LOCAL, &sDefault.hosts_deny, NULL,0}, + {"ignore errors", P_BOOL, P_LOCAL, &sDefault.ignore_errors, NULL,0}, + {"ignore nonreadable",P_BOOL, P_LOCAL, &sDefault.ignore_nonreadable,NULL,0}, + {"include from", P_STRING, P_LOCAL, &sDefault.include_from, NULL,0}, + {"include", P_STRING, P_LOCAL, &sDefault.include, NULL,0}, + {"incoming chmod", P_STRING, P_LOCAL, &sDefault.incoming_chmod, NULL,0}, + {"list", P_BOOL, P_LOCAL, &sDefault.list, NULL,0}, + {"lock file", P_STRING, P_LOCAL, &sDefault.lock_file, NULL,0}, + {"log format", P_STRING, P_LOCAL, &sDefault.log_format, NULL,0}, + {"max connections", P_INTEGER,P_LOCAL, &sDefault.max_connections, NULL,0}, + {"max verbosity", P_INTEGER,P_LOCAL, &sDefault.max_verbosity, NULL,0}, + {"name", P_STRING, P_LOCAL, &sDefault.name, NULL,0}, + {"outgoing chmod", P_STRING, P_LOCAL, &sDefault.outgoing_chmod, NULL,0}, + {"path", P_PATH, P_LOCAL, &sDefault.path, NULL,0}, +#ifdef HAVE_PUTENV + {"post-xfer exec", P_STRING, P_LOCAL, &sDefault.postxfer_exec, NULL,0}, + {"pre-xfer exec", P_STRING, P_LOCAL, &sDefault.prexfer_exec, NULL,0}, +#endif + {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL,0}, + {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options, NULL,0}, + {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file, NULL,0}, + {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0}, + {"temp dir", P_PATH, P_LOCAL, &sDefault.temp_dir, NULL,0}, + {"timeout", P_INTEGER,P_LOCAL, &sDefault.timeout, NULL,0}, + {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging, NULL,0}, + {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0}, + {"use chroot", P_BOOL, P_LOCAL, &sDefault.use_chroot, NULL,0}, + {"write only", P_BOOL, P_LOCAL, &sDefault.write_only, NULL,0}, + {NULL, P_BOOL, P_NONE, NULL, NULL,0} }; @@ -345,43 +372,51 @@ int fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);} -FN_GLOBAL_STRING(lp_motd_file, &Globals.motd_file) +FN_GLOBAL_STRING(lp_bind_address, &Globals.bind_address) FN_GLOBAL_STRING(lp_log_file, &Globals.log_file) +FN_GLOBAL_STRING(lp_motd_file, &Globals.motd_file) FN_GLOBAL_STRING(lp_pid_file, &Globals.pid_file) FN_GLOBAL_STRING(lp_socket_options, &Globals.socket_options) -FN_GLOBAL_INTEGER(lp_syslog_facility, &Globals.syslog_facility) + FN_GLOBAL_INTEGER(lp_rsync_port, &Globals.rsync_port) -FN_GLOBAL_STRING(lp_bind_address, &Globals.bind_address) +FN_GLOBAL_INTEGER(lp_syslog_facility, &Globals.syslog_facility) -FN_LOCAL_STRING(lp_name, name) +FN_LOCAL_STRING(lp_auth_users, auth_users) FN_LOCAL_STRING(lp_comment, comment) -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) -FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors) -FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable) -FN_LOCAL_STRING(lp_uid, uid) +FN_LOCAL_STRING(lp_dont_compress, dont_compress) +FN_LOCAL_STRING(lp_exclude, exclude) +FN_LOCAL_STRING(lp_exclude_from, exclude_from) +FN_LOCAL_STRING(lp_filter, filter) FN_LOCAL_STRING(lp_gid, gid) FN_LOCAL_STRING(lp_hosts_allow, hosts_allow) FN_LOCAL_STRING(lp_hosts_deny, hosts_deny) -FN_LOCAL_STRING(lp_auth_users, auth_users) -FN_LOCAL_STRING(lp_secrets_file, secrets_file) -FN_LOCAL_BOOL(lp_strict_modes, strict_modes) -FN_LOCAL_STRING(lp_filter, filter) -FN_LOCAL_STRING(lp_exclude, exclude) -FN_LOCAL_STRING(lp_exclude_from, exclude_from) FN_LOCAL_STRING(lp_include, include) FN_LOCAL_STRING(lp_include_from, include_from) +FN_LOCAL_STRING(lp_incoming_chmod, incoming_chmod) +FN_LOCAL_STRING(lp_lock_file, lock_file) FN_LOCAL_STRING(lp_log_format, log_format) +FN_LOCAL_STRING(lp_name, name) +FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod) +FN_LOCAL_STRING(lp_path, path) +FN_LOCAL_STRING(lp_postxfer_exec, postxfer_exec) +FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec) FN_LOCAL_STRING(lp_refuse_options, refuse_options) -FN_LOCAL_STRING(lp_dont_compress, dont_compress) -FN_LOCAL_INTEGER(lp_timeout, timeout) +FN_LOCAL_STRING(lp_secrets_file, secrets_file) +FN_LOCAL_STRING(lp_temp_dir, temp_dir) +FN_LOCAL_STRING(lp_uid, uid) + FN_LOCAL_INTEGER(lp_max_connections, max_connections) FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity) +FN_LOCAL_INTEGER(lp_timeout, timeout) + +FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors) +FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable) +FN_LOCAL_BOOL(lp_list, list) +FN_LOCAL_BOOL(lp_read_only, read_only) +FN_LOCAL_BOOL(lp_strict_modes, strict_modes) +FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging) +FN_LOCAL_BOOL(lp_use_chroot, use_chroot) +FN_LOCAL_BOOL(lp_write_only, write_only) /* local prototypes */ static int strwicmp(char *psz1, char *psz2); diff -urN --exclude=patches rsync-2.6.6/log.c rsync-2.6.7/log.c --- rsync-2.6.6/log.c 2005-06-09 15:27:22.000000000 -0700 +++ rsync-2.6.7/log.c 2006-02-23 17:56:26.000000000 -0800 @@ -26,6 +26,9 @@ , Oct 2000. */ #include "rsync.h" +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H +#include +#endif extern int verbose; extern int dry_run; @@ -36,12 +39,18 @@ extern int quiet; extern int module_id; extern int msg_fd_out; +extern int allow_8bit_chars; extern int protocol_version; extern int preserve_times; +extern int log_format_has_i; extern int log_format_has_o_or_i; extern int daemon_log_format_has_o_or_i; +extern mode_t orig_umask; extern char *auth_user; extern char *log_format; +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H +extern iconv_t ic_chck; +#endif static int log_initialised; static int logfile_was_closed; @@ -65,8 +74,11 @@ { RERR_STREAMIO , "error in rsync protocol data stream" }, { RERR_MESSAGEIO , "errors with program diagnostics" }, { RERR_IPC , "error in IPC code" }, - { RERR_SIGNAL , "received SIGUSR1 or SIGINT" }, - { RERR_WAITCHILD , "some error returned by waitpid()" }, + { RERR_CRASHED , "sibling process crashed" }, + { RERR_TERMINATED , "sibling process terminated abnormally" }, + { RERR_SIGNAL1 , "received SIGUSR1" }, + { RERR_SIGNAL , "received SIGINT, SIGTERM, or SIGHUP" }, + { RERR_WAITCHILD , "waitpid() failed" }, { RERR_MALLOC , "error allocating core memory buffers" }, { RERR_PARTIAL , "some files could not be transferred" }, { RERR_VANISHED , "some files vanished before they could be transferred" }, @@ -80,7 +92,6 @@ }; - /* * Map from rsync error code to name, or return NULL. */ @@ -94,7 +105,6 @@ return NULL; } - static void logit(int priority, char *buf) { if (logfile_was_closed) @@ -134,8 +144,7 @@ static void logfile_open(void) { - extern int orig_umask; - int old_umask = umask(022 | orig_umask); + mode_t old_umask = umask(022 | orig_umask); logfile = fopen(logfname, "a"); umask(old_umask); if (!logfile) { @@ -187,20 +196,41 @@ } } +static void filtered_fwrite(FILE *f, const char *buf, int len, int use_isprint) +{ + const char *s, *end = buf + len; + for (s = buf; s < end; s++) { + if ((s < end - 4 + && *s == '\\' && s[1] == '#' + && isdigit(*(uchar*)(s+2)) + && isdigit(*(uchar*)(s+3)) + && isdigit(*(uchar*)(s+4))) + || (*s != '\t' + && ((use_isprint && !isprint(*(uchar*)s)) + || *(uchar*)s < ' '))) { + if (s != buf && fwrite(buf, s - buf, 1, f) != 1) + exit_cleanup(RERR_MESSAGEIO); + fprintf(f, "\\#%03o", *(uchar*)s); + buf = s + 1; + } + } + if (buf != end && fwrite(buf, end - buf, 1, f) != 1) + exit_cleanup(RERR_MESSAGEIO); +} + /* this is the underlying (unformatted) rsync debugging function. Call - * it with FINFO, FERROR or FLOG */ + * it with FINFO, FERROR or FLOG. Note: recursion can happen with + * certain fatal conditions. */ void rwrite(enum logcode code, char *buf, int len) { + int trailing_CR_or_NL; FILE *f = NULL; - /* recursion can happen with certain fatal conditions */ - - if (quiet && code == FINFO) - return; if (len < 0) exit_cleanup(RERR_MESSAGEIO); - buf[len] = 0; + if (quiet && code == FINFO) + return; if (am_server && msg_fd_out >= 0) { /* Pass the message to our sibling. */ @@ -208,6 +238,9 @@ return; } + if (code == FSOCKERR) /* This gets simplified for a non-sibling. */ + code = FERROR; + if (code == FCLIENT) code = FINFO; else if (am_daemon) { @@ -239,31 +272,67 @@ } } - if (code == FERROR) { + switch (code) { + case FERROR: log_got_error = 1; f = stderr; - } - - if (code == FINFO) + goto pre_scan; + case FINFO: f = am_server ? stderr : stdout; - - if (!f) + pre_scan: + while (len > 1 && *buf == '\n') { + fputc(*buf, f); + buf++; + len--; + } + break; + case FNAME: + f = am_server ? stderr : stdout; + break; + default: exit_cleanup(RERR_MESSAGEIO); + } - if (fwrite(buf, len, 1, f) != 1) - exit_cleanup(RERR_MESSAGEIO); + trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r') + ? buf[--len] : 0; + +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H + if (ic_chck != (iconv_t)-1) { + char convbuf[1024]; + char *in_buf = buf, *out_buf = convbuf; + size_t in_cnt = len, out_cnt = sizeof convbuf - 1; + + iconv(ic_chck, NULL, 0, NULL, 0); + while (iconv(ic_chck, &in_buf,&in_cnt, + &out_buf,&out_cnt) == (size_t)-1) { + if (out_buf != convbuf) { + filtered_fwrite(f, convbuf, out_buf - convbuf, 0); + out_buf = convbuf; + out_cnt = sizeof convbuf - 1; + } + if (errno == E2BIG) + continue; + fprintf(f, "\\#%03o", *(uchar*)in_buf++); + in_cnt--; + } + if (out_buf != convbuf) + filtered_fwrite(f, convbuf, out_buf - convbuf, 0); + } else +#endif + filtered_fwrite(f, buf, len, !allow_8bit_chars); - if (buf[len-1] == '\r' || buf[len-1] == '\n') + if (trailing_CR_or_NL) { + fputc(trailing_CR_or_NL, f); fflush(f); + } } - /* This is the rsync debugging function. Call it with FINFO, FERROR or * FLOG. */ void rprintf(enum logcode code, const char *format, ...) { va_list ap; - char buf[MAXPATHLEN+512]; + char buf[BIGPATHBUFLEN]; size_t len; va_start(ap, format); @@ -274,7 +343,7 @@ * 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[] = "[...]"; + static const char ellipsis[] = "[...]"; /* Reset length, and zero-terminate the end of our buffer */ len = sizeof buf - 1; @@ -290,7 +359,7 @@ * If the input format string has a trailing newline, * we copy it into that extra null; if it doesn't, well, * all we lose is one byte. */ - strncpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis); + memcpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis); if (format[strlen(format)-1] == '\n') { buf[len-1] = '\n'; } @@ -299,7 +368,6 @@ rwrite(code, buf, len); } - /* This is like rprintf, but it also tries to print some * representation of the error code. Normally errcode = errno. * @@ -311,7 +379,7 @@ void rsyserr(enum logcode code, int errcode, const char *format, ...) { va_list ap; - char buf[MAXPATHLEN+512]; + char buf[BIGPATHBUFLEN]; size_t len; strcpy(buf, RSYNC_NAME ": "); @@ -331,8 +399,6 @@ rwrite(code, buf, len); } - - void rflush(enum logcode code) { FILE *f = NULL; @@ -360,8 +426,6 @@ fflush(f); } - - /* a generic logging routine for send/recv, with parameter * substitiution */ static void log_formatted(enum logcode code, char *format, char *op, @@ -399,51 +463,83 @@ n = NULL; switch (*p) { - case 'h': if (am_daemon) n = client_name(0); break; - case 'a': if (am_daemon) n = client_addr(0); break; + case 'h': + if (am_daemon) + n = client_name(0); + break; + case 'a': + if (am_daemon) + n = client_addr(0); + break; case 'l': strlcat(fmt, ".0f", sizeof fmt); snprintf(buf2, sizeof buf2, fmt, (double)file->length); n = buf2; break; + case 'U': + strlcat(fmt, "ld", sizeof fmt); + snprintf(buf2, sizeof buf2, fmt, + (long)file->uid); + n = buf2; + break; + case 'G': + if (file->gid == GID_NONE) + n = "DEFAULT"; + else { + strlcat(fmt, "ld", sizeof fmt); + snprintf(buf2, sizeof buf2, fmt, + (long)file->gid); + n = buf2; + } + break; case 'p': strlcat(fmt, "ld", sizeof fmt); snprintf(buf2, sizeof buf2, fmt, (long)getpid()); n = buf2; break; - case 'o': n = op; break; + case 'M': + n = timestring(file->modtime); + { + char *cp = n; + while ((cp = strchr(cp, ' ')) != NULL) + *cp = '-'; + } + break; + case 'B': + n = buf2 + MAXPATHLEN - PERMSTRING_SIZE; + permstring(n - 1, file->mode); /* skip the type char */ + break; + case 'o': + n = op; + break; case 'f': - n = safe_fname(f_name(file)); + n = f_name(file, NULL); if (am_sender && file->dir.root) { pathjoin(buf2, sizeof buf2, file->dir.root, n); - /* The buffer from safe_fname() has more - * room than MAXPATHLEN, so this is safe. */ + clean_fname(buf2, 0); if (fmt[1]) - strcpy(n, buf2); + strlcpy(n, buf2, MAXPATHLEN); else n = buf2; - } - clean_fname(n, 0); + } else + clean_fname(n, 0); if (*n == '/') n++; break; case 'n': - n = safe_fname(f_name(file)); - if (S_ISDIR(file->mode)) { - /* The buffer from safe_fname() has more - * room than MAXPATHLEN, so this is safe. */ - strcat(n, "/"); - } + n = f_name(file, NULL); + if (S_ISDIR(file->mode)) + strlcat(n, "/", MAXPATHLEN); break; case 'L': if (hlink && *hlink) { - n = safe_fname(hlink); + n = hlink; strcpy(buf2, " => "); } else if (S_ISLNK(file->mode) && file->u.link) { - n = safe_fname(file->u.link); + n = file->u.link; strcpy(buf2, " -> "); } else { n = ""; @@ -455,10 +551,18 @@ snprintf(buf2 + 4, sizeof buf2 - 4, fmt, n); n = buf2; break; - case 'm': n = lp_name(module_id); break; - case 't': n = timestring(time(NULL)); break; - case 'P': n = lp_path(module_id); break; - case 'u': n = auth_user; break; + case 'm': + n = lp_name(module_id); + break; + case 't': + n = timestring(time(NULL)); + break; + case 'P': + n = lp_path(module_id); + break; + case 'u': + n = auth_user; + break; case 'b': if (am_sender) { b = stats.total_written - @@ -494,17 +598,17 @@ : !(iflags & ITEM_TRANSFER) ? '.' : !local_server && *op == 's' ? '<' : '>'; n[1] = S_ISDIR(file->mode) ? 'd' + : IS_SPECIAL(file->mode) ? 'S' : IS_DEVICE(file->mode) ? 'D' : S_ISLNK(file->mode) ? 'L' : 'f'; n[2] = !(iflags & ITEM_REPORT_CHECKSUM) ? '.' : 'c'; n[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's'; n[4] = !(iflags & ITEM_REPORT_TIME) ? '.' - : !preserve_times || IS_DEVICE(file->mode) - || S_ISLNK(file->mode) ? 'T' : 't'; + : !preserve_times || S_ISLNK(file->mode) ? 'T' : 't'; n[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p'; n[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o'; n[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g'; - n[8] = !(iflags & ITEM_REPORT_XATTRS) ? '.' : 'a'; + n[8] = '.'; n[9] = '\0'; if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) { @@ -512,7 +616,8 @@ int i; for (i = 2; n[i]; i++) n[i] = ch; - } else if (n[0] == '.' || n[0] == 'h') { + } else if (n[0] == '.' || n[0] == 'h' + || (n[0] == 'c' && n[1] == 'f')) { int i; for (i = 2; n[i]; i++) { if (n[i] != '.') @@ -594,7 +699,7 @@ log_formatted(FLOG, lp_log_format(module_id), s_or_r, file, initial_stats, iflags, hlink); } else if (log_format && !am_server) { - log_formatted(FINFO, log_format, s_or_r, + log_formatted(FNAME, log_format, s_or_r, file, initial_stats, iflags, hlink); } } @@ -603,9 +708,9 @@ char *buf) { int significant_flags = iflags & SIGNIFICANT_ITEM_FLAGS; - int see_item = itemizing && (significant_flags || *buf || verbose > 1); - int local_change = iflags & ITEM_LOCAL_CHANGE - && (!(iflags & ITEM_XNAME_FOLLOWS) || significant_flags); + int see_item = itemizing && (significant_flags || *buf + || log_format_has_i > 1 || (verbose > 1 && log_format_has_i)); + int local_change = iflags & ITEM_LOCAL_CHANGE && significant_flags; if (am_server) { if (am_daemon && !dry_run && see_item) log_item(file, &stats, iflags, buf); @@ -642,7 +747,6 @@ log_formatted(FLOG, fmt, "del.", &file, &stats, ITEM_DELETED, NULL); } - /* * Called when the transfer is interrupted for some reason. * @@ -665,11 +769,11 @@ /* VANISHED is not an error, only a warning */ if (code == RERR_VANISHED) { - rprintf(FINFO, "rsync warning: %s (code %d) at %s(%d)\n", - name, code, file, line); + rprintf(FINFO, "rsync warning: %s (code %d) at %s(%d) [%s]\n", + name, code, file, line, who_am_i()); } else { - rprintf(FERROR, "rsync error: %s (code %d) at %s(%d)\n", - name, code, file, line); + rprintf(FERROR, "rsync error: %s (code %d) at %s(%d) [%s]\n", + name, code, file, line, who_am_i()); } } } diff -urN --exclude=patches rsync-2.6.6/main.c rsync-2.6.7/main.c --- rsync-2.6.6/main.c 2005-05-12 00:43:14.000000000 -0700 +++ rsync-2.6.7/main.c 2006-03-01 19:53:42.000000000 -0800 @@ -40,17 +40,14 @@ extern int do_stats; extern int log_got_error; extern int module_id; -extern int orig_umask; extern int copy_links; +extern int copy_dirlinks; extern int keep_dirlinks; extern int preserve_hard_links; extern int protocol_version; extern int recurse; -extern int fuzzy_basis; extern int relative_paths; extern int rsync_port; -extern int inplace; -extern int make_backups; extern int whole_file; extern int read_batch; extern int write_batch; @@ -60,22 +57,30 @@ extern pid_t cleanup_child_pid; extern struct stats stats; extern char *filesfrom_host; -extern char *partial_dir; -extern char *basis_dir[]; extern char *rsync_path; extern char *shell_cmd; extern char *batch_name; int local_server = 0; +mode_t orig_umask = 0; struct file_list *the_file_list; /* There's probably never more than at most 2 outstanding child processes, * but set it higher, just in case. */ -#define MAXCHILDPROCS 5 +#define MAXCHILDPROCS 7 + +#ifdef HAVE_SIGACTION +# ifdef HAVE_SIGPROCMASK +# define SIGACTMASK(n,h) SIGACTION(n,h), sigaddset(&sigmask,(n)) +# else +# define SIGACTMASK(n,h) SIGACTION(n,h) +# endif +static struct sigaction sigact; +#endif struct pid_status { pid_t pid; - int status; + int status; } pid_stat_table[MAXCHILDPROCS]; static time_t starttime, endtime; @@ -83,38 +88,58 @@ static void show_malloc_stats(void); -/**************************************************************************** -wait for a process to exit, calling io_flush while waiting -****************************************************************************/ -void wait_process(pid_t pid, int *status) +/* Works like waitpid(), but if we already harvested the child pid in our + * remember_children(), we succeed instead of returning an error. */ +pid_t wait_process(pid_t pid, int *status_ptr, int flags) { - pid_t waited_pid; - int cnt; - - while ((waited_pid = waitpid(pid, status, WNOHANG)) == 0) { - msleep(20); - io_flush(FULL_FLUSH); - } + pid_t waited_pid = waitpid(pid, status_ptr, flags); if (waited_pid == -1 && errno == ECHILD) { - /* status of requested child no longer available. - * check to see if it was processed by the sigchld_handler. - */ - for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) { + /* Status of requested child no longer available: check to + * see if it was processed by remember_children(). */ + int cnt; + for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) { if (pid == pid_stat_table[cnt].pid) { - *status = pid_stat_table[cnt].status; + *status_ptr = pid_stat_table[cnt].status; pid_stat_table[cnt].pid = 0; - break; + return pid; } } } + return waited_pid; +} + +/* Wait for a process to exit, calling io_flush while waiting. */ +static void wait_process_with_flush(pid_t pid, int *exit_code_ptr) +{ + pid_t waited_pid; + int status; + + while ((waited_pid = wait_process(pid, &status, WNOHANG)) == 0) { + msleep(20); + io_flush(FULL_FLUSH); + } + /* TODO: If the child exited on a signal, then log an * appropriate error message. Perhaps we should also accept a * message describing the purpose of the child. Also indicate - * this to the caller so that thhey know something went - * wrong. */ - *status = WEXITSTATUS(*status); + * this to the caller so that they know something went wrong. */ + if (waited_pid < 0) { + rsyserr(FERROR, errno, "waitpid"); + *exit_code_ptr = RERR_WAITCHILD; + } else if (!WIFEXITED(status)) { +#ifdef WCOREDUMP + if (WCOREDUMP(status)) + *exit_code_ptr = RERR_CRASHED; + else +#endif + if (WIFSIGNALED(status)) + *exit_code_ptr = RERR_TERMINATED; + else + *exit_code_ptr = RERR_WAITCHILD; + } else + *exit_code_ptr = WEXITSTATUS(status); } /* This function gets called from all 3 processes. We want the client side @@ -194,14 +219,14 @@ rprintf(FINFO,"\nNumber of files: %d\n", stats.num_files); rprintf(FINFO,"Number of files transferred: %d\n", stats.num_transferred_files); - rprintf(FINFO,"Total file size: %.0f bytes\n", - (double)stats.total_size); - rprintf(FINFO,"Total transferred file size: %.0f bytes\n", - (double)stats.total_transferred_size); - rprintf(FINFO,"Literal data: %.0f bytes\n", - (double)stats.literal_data); - rprintf(FINFO,"Matched data: %.0f bytes\n", - (double)stats.matched_data); + rprintf(FINFO,"Total file size: %s bytes\n", + human_num(stats.total_size)); + rprintf(FINFO,"Total transferred file size: %s bytes\n", + human_num(stats.total_transferred_size)); + rprintf(FINFO,"Literal data: %s bytes\n", + human_num(stats.literal_data)); + rprintf(FINFO,"Matched data: %s bytes\n", + human_num(stats.matched_data)); rprintf(FINFO,"File list size: %d\n", stats.flist_size); if (stats.flist_buildtime) { rprintf(FINFO, @@ -211,19 +236,19 @@ "File list transfer time: %.3f seconds\n", (double)stats.flist_xfertime / 1000); } - rprintf(FINFO,"Total bytes sent: %.0f\n", - (double)total_written); - rprintf(FINFO,"Total bytes received: %.0f\n", - (double)total_read); + rprintf(FINFO,"Total bytes sent: %s\n", + human_num(total_written)); + rprintf(FINFO,"Total bytes received: %s\n", + human_num(total_read)); } if (verbose || do_stats) { rprintf(FINFO, - "\nsent %.0f bytes received %.0f bytes %.2f bytes/sec\n", - (double)total_written, (double)total_read, - (total_written + total_read)/(0.5 + (endtime - starttime))); - rprintf(FINFO, "total size is %.0f speedup is %.2f\n", - (double)stats.total_size, + "\nsent %s bytes received %s bytes %s bytes/sec\n", + human_num(total_written), human_num(total_read), + human_dnum((total_written + total_read)/(0.5 + (endtime - starttime)), 2)); + rprintf(FINFO, "total size is %s speedup is %.2f\n", + human_num(stats.total_size), (double)stats.total_size / (total_written+total_read)); } @@ -278,10 +303,11 @@ int i, argc = 0; char *args[MAX_ARGS]; pid_t ret; - char *tok, *dir = NULL; + char *dir = NULL; int dash_l_set = 0; if (!read_batch && !local_server) { + char *t, *f, in_quote = '\0'; char *rsh_env = getenv(RSYNC_RSH_ENV); if (!cmd) cmd = rsh_env; @@ -291,13 +317,39 @@ if (!cmd) goto oom; - for (tok = strtok(cmd, " "); tok; tok = strtok(NULL, " ")) { + for (t = f = cmd; *f; f++) { + if (*f == ' ') + continue; /* Comparison leaves rooms for server_options(). */ if (argc >= MAX_ARGS - MAX_SERVER_ARGS) { rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n"); exit_cleanup(RERR_SYNTAX); } - args[argc++] = tok; + args[argc++] = t; + while (*f != ' ' || in_quote) { + if (!*f) { + if (in_quote) { + rprintf(FERROR, + "Missing trailing-%c in remote-shell command.\n", + in_quote); + exit_cleanup(RERR_SYNTAX); + } + f--; + break; + } + if (*f == '\'' || *f == '"') { + if (!in_quote) { + in_quote = *f++; + continue; + } + if (*f == in_quote && *++f != in_quote) { + in_quote = '\0'; + continue; + } + } + *t++ = *f++; + } + *t++ = '\0'; } /* check to see if we've already been given '-l user' in @@ -350,10 +402,9 @@ args[argc] = NULL; if (verbose > 3) { - rprintf(FINFO,"cmd="); for (i = 0; i < argc; i++) - rprintf(FINFO, "%s ", safe_fname(args[i])); - rprintf(FINFO,"\n"); + rprintf(FINFO, "cmd[%d]=%s ", i, args[i]); + rprintf(FINFO, "\n"); } if (read_batch) { @@ -380,62 +431,122 @@ return ret; -oom: + oom: out_of_memory("do_cmd"); return 0; /* not reached */ } - -static char *get_local_name(struct file_list *flist,char *name) +/* The receiving side operates in one of two modes: + * + * 1. it receives any number of files into a destination directory, + * placing them according to their names in the file-list. + * + * 2. it receives a single file and saves it using the name in the + * destination path instead of its file-list name. This requires a + * "local name" for writing out the destination file. + * + * So, our task is to figure out what mode/local-name we need and return + * either a NULL for mode 1, or the local-name for mode 2. We also + * change directory if there are any path components in dest_path. */ +static char *get_local_name(struct file_list *flist, char *dest_path) { STRUCT_STAT st; - int e; + char *cp; - if (verbose > 2) - rprintf(FINFO,"get_local_name count=%d %s\n", - flist->count, NS(name)); + if (verbose > 2) { + rprintf(FINFO, "get_local_name count=%d %s\n", + flist->count, NS(dest_path)); + } - if (!name) + if (!dest_path || list_only) return NULL; - if (do_stat(name,&st) == 0) { + /* If the destination path refers to an existing directory, enter + * it and use mode 1. If there is something other than a directory + * at the destination path, we must be transferring one file + * (anything at the destination will be overwritten). */ + if (do_stat(dest_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { - if (!push_dir(name)) { + if (!push_dir(dest_path)) { rsyserr(FERROR, errno, "push_dir#1 %s failed", - full_fname(name)); + full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } return NULL; } if (flist->count > 1) { - rprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n"); + rprintf(FERROR, + "ERROR: destination must be a directory when" + " copying more than 1 file\n"); + exit_cleanup(RERR_FILESELECT); + } + /* Caution: flist->count could be 0! */ + if (flist->count == 1 && S_ISDIR(flist->files[0]->mode)) { + rprintf(FERROR, + "ERROR: cannot overwrite non-directory" + " with a directory\n"); exit_cleanup(RERR_FILESELECT); } - return name; + } else if (errno != ENOENT) { + rsyserr(FERROR, errno, "cannot stat destination %s", + full_fname(dest_path)); + exit_cleanup(RERR_FILESELECT); } - if (flist->count <= 1 && ((e = strlen(name)) <= 1 || name[e-1] != '/')) - return name; + cp = strrchr(dest_path, '/'); - if (do_mkdir(name,0777 & ~orig_umask) != 0) { - rsyserr(FERROR, errno, "mkdir %s failed", full_fname(name)); - exit_cleanup(RERR_FILEIO); - } - if (verbose > 0) - rprintf(FINFO, "created directory %s\n", safe_fname(name)); + /* If the destination path ends in a slash or we are transferring + * multiple files, create a directory at the destination path, + * enter the new directory, and use mode 1. */ + if (flist->count > 1 || (cp && !cp[1])) { + /* Lop off the final slash (if any). */ + if (cp && !cp[1]) + *cp = '\0'; + + if (mkdir_defmode(dest_path) != 0) { + rsyserr(FERROR, errno, "mkdir %s failed", + full_fname(dest_path)); + exit_cleanup(RERR_FILEIO); + } + + if (verbose) + rprintf(FINFO, "created directory %s\n", dest_path); + + if (dry_run) { + /* Indicate that the destination directory doesn't + * really exist and return mode 1. */ + dry_run++; + return NULL; + } + + if (!push_dir(dest_path)) { + rsyserr(FERROR, errno, "push_dir#2 %s failed", + full_fname(dest_path)); + exit_cleanup(RERR_FILESELECT); + } - if (dry_run) { - dry_run++; return NULL; } - if (!push_dir(name)) { - rsyserr(FERROR, errno, "push_dir#2 %s failed", - full_fname(name)); + /* Otherwise, we are writing a single file, possibly on top of an + * existing non-directory. Change to the item's parent directory + * (if it has a path component), return the basename of the + * destination file as the local name, and use mode 2. */ + if (!cp) + return dest_path; + + if (cp == dest_path) + dest_path = "/"; + + *cp = '\0'; + if (!push_dir(dest_path)) { + rsyserr(FERROR, errno, "push_dir#3 %s failed", + full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } + *cp = '/'; - return NULL; + return cp + 1; } @@ -463,9 +574,8 @@ } -static void do_server_sender(int f_in, int f_out, int argc,char *argv[]) +static void do_server_sender(int f_in, int f_out, int argc, char *argv[]) { - int i; struct file_list *flist; char *dir = argv[0]; @@ -494,14 +604,6 @@ argc--; argv++; - if (strcmp(dir,".")) { - int l = strlen(dir); - if (strcmp(dir,"/") == 0) - l = 0; - for (i = 0; i < argc; i++) - argv[i] += l+1; - } - if (argc == 0 && (recurse || list_only)) { argc = 1; argv--; @@ -530,12 +632,12 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) { int pid; - int status = 0; + int exit_code = 0; int error_pipe[2]; /* The receiving side mustn't obey this, or an existing symlink that * points to an identical file won't be replaced by the referent. */ - copy_links = 0; + copy_links = copy_dirlinks = 0; if (preserve_hard_links) init_hard_links(); @@ -616,14 +718,14 @@ set_msg_fd_in(-1); kill(pid, SIGUSR2); - wait_process(pid, &status); - return status; + wait_process_with_flush(pid, &exit_code); + return exit_code; } static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) { - int status; + int exit_code; struct file_list *flist; char *local_name = NULL; char *dir = NULL; @@ -646,7 +748,6 @@ return; } - if (argc > 0) { dir = argv[0]; argc--; @@ -679,17 +780,11 @@ } the_file_list = flist; - if (argc > 0) { - if (strcmp(dir,".")) { - argv[0] += strlen(dir); - if (argv[0][0] == '/') - argv[0]++; - } + if (argc > 0) local_name = get_local_name(flist,argv[0]); - } - status = do_recv(f_in,f_out,flist,local_name); - exit_cleanup(status); + exit_code = do_recv(f_in,f_out,flist,local_name); + exit_cleanup(exit_code); } @@ -707,6 +802,9 @@ io_set_sock_fds(f_in, f_out); setup_protocol(f_out, f_in); +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H + setup_iconv(); +#endif if (protocol_version >= 23) io_start_multiplex_out(); @@ -732,7 +830,7 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) { struct file_list *flist = NULL; - int status = 0, status2 = 0; + int exit_code = 0, exit_code2 = 0; char *local_name = NULL; cleanup_child_pid = pid; @@ -743,6 +841,9 @@ io_set_sock_fds(f_in, f_out); setup_protocol(f_out,f_in); +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H + setup_iconv(); +#endif if (protocol_version >= 23 && !read_batch) io_start_multiplex_in(); @@ -784,11 +885,11 @@ if (verbose > 3) rprintf(FINFO,"client_run waiting on %d\n", (int) pid); io_flush(FULL_FLUSH); - wait_process(pid, &status); + wait_process_with_flush(pid, &exit_code); } output_summary(); io_flush(FULL_FLUSH); - exit_cleanup(status); + exit_cleanup(exit_code); } if (need_messages_from_generator && !read_batch) @@ -812,7 +913,7 @@ if (flist && flist->count > 0) { local_name = get_local_name(flist, argv[0]); - status2 = do_recv(f_in, f_out, flist, local_name); + exit_code2 = do_recv(f_in, f_out, flist, local_name); } else { handle_stats(-1); output_summary(); @@ -822,10 +923,10 @@ if (verbose > 3) rprintf(FINFO,"client_run2 waiting on %d\n", (int) pid); io_flush(FULL_FLUSH); - wait_process(pid, &status); + wait_process_with_flush(pid, &exit_code); } - return MAX(status, status2); + return MAX(exit_code, exit_code2); } static int copy_argv (char *argv[]) @@ -869,9 +970,16 @@ return rc; if (!read_batch) { /* for read_batch, NO source is specified */ - argc--; shell_path = check_for_hostspec(argv[0], &shell_machine, &rsync_port); if (shell_path) { /* source is remote */ + char *dummy1; + int dummy2; + if (--argc + && check_for_hostspec(argv[argc], &dummy1, &dummy2)) { + rprintf(FERROR, + "The source and destination cannot both be remote.\n"); + exit_cleanup(RERR_SYNTAX); + } argv++; if (filesfrom_host && *filesfrom_host && strcmp(filesfrom_host, shell_machine) != 0) { @@ -892,12 +1000,14 @@ } else { /* source is local, check dest arg */ am_sender = 1; - if (argc < 1) { /* destination required */ - usage(FERROR); - exit_cleanup(RERR_SYNTAX); + if (argc > 1) + p = argv[--argc]; + else { + p = "."; + list_only = 1; } - shell_path = check_for_hostspec(argv[argc], &shell_machine, &rsync_port); + shell_path = check_for_hostspec(p, &shell_machine, &rsync_port); if (shell_path && filesfrom_host && *filesfrom_host && strcmp(filesfrom_host, shell_machine) != 0) { rprintf(FERROR, @@ -912,7 +1022,7 @@ exit_cleanup(RERR_SYNTAX); } shell_machine = NULL; - shell_path = argv[argc]; + shell_path = p; } else if (rsync_port) { if (!shell_cmd) { return start_socket_client(shell_machine, @@ -942,10 +1052,10 @@ if (verbose > 3) { rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n", - shell_cmd ? safe_fname(shell_cmd) : "", - shell_machine ? safe_fname(shell_machine) : "", - shell_user ? safe_fname(shell_user) : "", - shell_path ? safe_fname(shell_path) : ""); + shell_cmd ? shell_cmd : "", + shell_machine ? shell_machine : "", + shell_user ? shell_user : "", + shell_path ? shell_path : ""); } /* for remote source, only single dest arg can remain ... */ @@ -982,7 +1092,7 @@ static RETSIGTYPE sigusr1_handler(UNUSED(int val)) { - exit_cleanup(RERR_SIGNAL); + exit_cleanup(RERR_SIGNAL1); } static RETSIGTYPE sigusr2_handler(UNUSED(int val)) @@ -995,7 +1105,7 @@ _exit(0); } -static RETSIGTYPE sigchld_handler(UNUSED(int val)) +RETSIGTYPE remember_children(UNUSED(int val)) { #ifdef WNOHANG int cnt, status; @@ -1004,8 +1114,7 @@ * get him to explain why he put it in, so rather than taking it * out we're instead saving the child exit statuses for later use. * The waitpid() loop presumably eliminates all possibility of leaving - * zombie children, maybe that's why he did it. - */ + * zombie children, maybe that's why he did it. */ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { /* save the child's exit status */ for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) { @@ -1017,6 +1126,9 @@ } } #endif +#ifndef HAVE_SIGACTION + signal(SIGCHLD, remember_children); +#endif } @@ -1076,16 +1188,23 @@ int ret; int orig_argc = argc; char **orig_argv = argv; - - signal(SIGUSR1, sigusr1_handler); - signal(SIGUSR2, sigusr2_handler); - signal(SIGCHLD, sigchld_handler); +#ifdef HAVE_SIGACTION +# ifdef HAVE_SIGPROCMASK + sigset_t sigmask; + + sigemptyset(&sigmask); +# endif + sigact.sa_flags = SA_NOCLDSTOP; +#endif + SIGACTMASK(SIGUSR1, sigusr1_handler); + SIGACTMASK(SIGUSR2, sigusr2_handler); + SIGACTMASK(SIGCHLD, remember_children); #ifdef MAINTAINER_MODE - signal(SIGSEGV, rsync_panic_handler); - signal(SIGFPE, rsync_panic_handler); - signal(SIGABRT, rsync_panic_handler); - signal(SIGBUS, rsync_panic_handler); -#endif /* def MAINTAINER_MODE */ + SIGACTMASK(SIGSEGV, rsync_panic_handler); + SIGACTMASK(SIGFPE, rsync_panic_handler); + SIGACTMASK(SIGABRT, rsync_panic_handler); + SIGACTMASK(SIGBUS, rsync_panic_handler); +#endif starttime = time(NULL); am_root = (MY_UID() == 0); @@ -1099,7 +1218,11 @@ /* we set a 0 umask so that correct file permissions can be * carried across */ - orig_umask = (int)umask(0); + orig_umask = umask(0); + +#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE + setlocale(LC_CTYPE, ""); +#endif if (!parse_arguments(&argc, (const char ***) &argv, 1)) { /* FIXME: We ought to call the same error-handling @@ -1108,16 +1231,18 @@ exit_cleanup(RERR_SYNTAX); } - signal(SIGINT,SIGNAL_CAST sig_int); - signal(SIGHUP,SIGNAL_CAST sig_int); - signal(SIGTERM,SIGNAL_CAST sig_int); + SIGACTMASK(SIGINT, sig_int); + SIGACTMASK(SIGHUP, sig_int); + SIGACTMASK(SIGTERM, sig_int); +#if defined HAVE_SIGACTION && HAVE_SIGPROCMASK + sigprocmask(SIG_UNBLOCK, &sigmask, NULL); +#endif /* Ignore SIGPIPE; we consistently check error codes and will * see the EPIPE. */ - signal(SIGPIPE, SIG_IGN); - -#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE - setlocale(LC_CTYPE, ""); + SIGACTION(SIGPIPE, SIG_IGN); +#ifdef SIGXFSZ + SIGACTION(SIGXFSZ, SIG_IGN); #endif /* Initialize push_dir here because on some old systems getcwd diff -urN --exclude=patches rsync-2.6.6/match.c rsync-2.6.7/match.c --- rsync-2.6.6/match.c 2005-03-05 09:51:23.000000000 -0800 +++ rsync-2.6.7/match.c 2006-02-28 13:48:15.000000000 -0800 @@ -20,69 +20,47 @@ #include "rsync.h" extern int verbose; -extern int am_server; extern int do_progress; extern int checksum_seed; +extern int append_mode; int updating_basis_file; -typedef unsigned short tag; - -#define TABLESIZE (1<<16) -#define NULL_TAG (-1) - static int false_alarms; -static int tag_hits; +static int hash_hits; static int matches; static int64 data_transfer; static int total_false_alarms; -static int total_tag_hits; +static int total_hash_hits; static int total_matches; extern struct stats stats; -struct target { - tag t; - int32 i; -}; - -static struct target *targets; - -static int32 *tag_table; +#define TABLESIZE (1<<16) -#define gettag2(s1,s2) (((s1) + (s2)) & 0xFFFF) -#define gettag(sum) gettag2((sum)&0xFFFF,(sum)>>16) - -static int compare_targets(struct target *t1,struct target *t2) -{ - return (int)t1->t - (int)t2->t; -} +static int32 *hash_table; +#define SUM2HASH2(s1,s2) (((s1) + (s2)) & 0xFFFF) +#define SUM2HASH(sum) SUM2HASH2((sum)&0xFFFF,(sum)>>16) static void build_hash_table(struct sum_struct *s) { int32 i; - if (!tag_table) - tag_table = new_array(int32, TABLESIZE); + if (!hash_table) { + hash_table = new_array(int32, TABLESIZE); + if (!hash_table) + out_of_memory("build_hash_table"); + } - targets = new_array(struct target, s->count); - if (!tag_table || !targets) - out_of_memory("build_hash_table"); + memset(hash_table, 0xFF, TABLESIZE * sizeof hash_table[0]); for (i = 0; i < s->count; i++) { - targets[i].i = i; - targets[i].t = gettag(s->sums[i].sum1); + uint32 t = SUM2HASH(s->sums[i].sum1); + s->sums[i].chain = hash_table[t]; + hash_table[t] = i; } - - qsort(targets,s->count,sizeof(targets[0]),(int (*)())compare_targets); - - for (i = 0; i < TABLESIZE; i++) - tag_table[i] = NULL_TAG; - - for (i = s->count; i-- > 0; ) - tag_table[targets[i].t] = i; } @@ -127,7 +105,6 @@ sum_update(map_ptr(buf, last_match + j, n1), n1); } - if (i >= 0) last_match = offset + s->sums[i].len; else @@ -177,20 +154,22 @@ } do { - tag t = gettag2(s1,s2); int done_csum2 = 0; - int32 j = tag_table[t]; - - if (verbose > 4) - rprintf(FINFO,"offset=%.0f sum=%08x\n",(double)offset,sum); + int32 i; - if (j == NULL_TAG) - goto null_tag; + if (verbose > 4) { + rprintf(FINFO, "offset=%.0f sum=%04x%04x\n", + (double)offset, s2 & 0xFFFF, s1 & 0xFFFF); + } + + i = hash_table[SUM2HASH2(s1,s2)]; + if (i < 0) + goto null_hash; sum = (s1 & 0xffff) | (s2 << 16); - tag_hits++; + hash_hits++; do { - int32 l, i = targets[j].i; + int32 l; if (sum != s->sums[i].sum1) continue; @@ -206,9 +185,11 @@ && !(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); + if (verbose > 3) { + rprintf(FINFO, + "potential match at %.0f i=%ld sum=%08x\n", + (double)offset, (long)i, sum); + } if (!done_csum2) { map = (schar *)map_ptr(buf,offset,l); @@ -225,8 +206,8 @@ * one with an identical offset, so we prefer that over * the following want_i optimization. */ if (updating_basis_file) { - do { - int32 i2 = targets[j].i; + int32 i2; + for (i2 = i; i2 >= 0; i2 = s->sums[i2].chain) { if (s->sums[i2].offset != offset) continue; if (i2 != i) { @@ -241,7 +222,7 @@ * 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); + } } /* we've found a match, but now check to see @@ -267,9 +248,9 @@ s2 = sum >> 16; matches++; break; - } while (++j < s->count && targets[j].t == t); + } while ((i = s->sums[i].chain) >= 0); - null_tag: + null_hash: backup = offset - last_match; /* We sometimes read 1 byte prior to last_match... */ if (backup < 0) @@ -324,12 +305,31 @@ last_match = 0; false_alarms = 0; - tag_hits = 0; + hash_hits = 0; matches = 0; data_transfer = 0; sum_init(checksum_seed); + if (append_mode) { + OFF_T j = 0; + for (j = CHUNK_SIZE; j < s->flength; j += CHUNK_SIZE) { + if (buf && do_progress) + show_progress(last_match, buf->file_size); + sum_update(map_ptr(buf, last_match, CHUNK_SIZE), + CHUNK_SIZE); + last_match = j; + } + if (last_match < s->flength) { + int32 len = s->flength - last_match; + if (buf && do_progress) + show_progress(last_match, buf->file_size); + sum_update(map_ptr(buf, last_match, len), len); + last_match = s->flength; + } + s->count = 0; + } + if (len > 0 && s->count > 0) { build_hash_table(s); @@ -343,7 +343,7 @@ } else { OFF_T j; /* by doing this in pieces we avoid too many seeks */ - for (j = CHUNK_SIZE; j < len; j += CHUNK_SIZE) + for (j = last_match + CHUNK_SIZE; j < len; j += CHUNK_SIZE) matched(f, s, buf, j, -2); matched(f, s, buf, len, -1); } @@ -357,16 +357,11 @@ rprintf(FINFO,"sending file_sum\n"); write_buf(f,file_sum,MD4_SUM_LENGTH); - if (targets) { - free(targets); - targets=NULL; - } - if (verbose > 2) - rprintf(FINFO, "false_alarms=%d tag_hits=%d matches=%d\n", - false_alarms, tag_hits, matches); + rprintf(FINFO, "false_alarms=%d hash_hits=%d matches=%d\n", + false_alarms, hash_hits, matches); - total_tag_hits += tag_hits; + total_hash_hits += hash_hits; total_false_alarms += false_alarms; total_matches += matches; stats.literal_data += data_transfer; @@ -378,8 +373,7 @@ return; rprintf(FINFO, - "total: matches=%d tag_hits=%d false_alarms=%d data=%.0f\n", - total_matches,total_tag_hits, - total_false_alarms, + "total: matches=%d hash_hits=%d false_alarms=%d data=%.0f\n", + total_matches, total_hash_hits, total_false_alarms, (double)stats.literal_data); } diff -urN --exclude=patches rsync-2.6.6/mkproto.awk rsync-2.6.7/mkproto.awk --- rsync-2.6.6/mkproto.awk 2005-02-18 12:16:59.000000000 -0800 +++ rsync-2.6.7/mkproto.awk 2006-02-01 18:29:30.000000000 -0800 @@ -58,7 +58,7 @@ next; } -!/^OFF_T|^size_t|^off_t|^pid_t|^unsigned|^mode_t|^DIR|^user|^int|^char|^uint|^uchar|^short|^struct|^BOOL|^void|^time|^const/ { +!/^OFF_T|^size_t|^off_t|^pid_t|^unsigned|^mode_t|^DIR|^user|^int|^char|^uint|^uchar|^short|^struct|^BOOL|^void|^time|^const|^RETSIGTYPE/ { next; } diff -urN --exclude=patches rsync-2.6.6/options.c rsync-2.6.7/options.c --- rsync-2.6.6/options.c 2005-05-19 01:52:17.000000000 -0700 +++ rsync-2.6.7/options.c 2006-02-27 15:35:18.000000000 -0800 @@ -19,7 +19,8 @@ */ #include "rsync.h" -#include "popt.h" +#include +#include "zlib/zlib.h" extern int module_id; extern int sanitize_paths; @@ -38,13 +39,16 @@ **/ int whole_file = -1; -int archive_mode = 0; +int append_mode = 0; int keep_dirlinks = 0; +int copy_dirlinks = 0; int copy_links = 0; int preserve_links = 0; int preserve_hard_links = 0; int preserve_perms = 0; +int preserve_executability = 0; int preserve_devices = 0; +int preserve_specials = 0; int preserve_uid = 0; int preserve_gid = 0; int preserve_times = 0; @@ -54,6 +58,8 @@ int dry_run = 0; int do_xfers = 1; int ignore_times = 0; +int saw_delete_opt = 0; +int saw_delete_excluded_opt = 0; int delete_mode = 0; int delete_during = 0; int delete_before = 0; @@ -64,24 +70,27 @@ int protocol_version = PROTOCOL_VERSION; int sparse_files = 0; int do_compression = 0; +int def_compress_level = Z_DEFAULT_COMPRESSION; int am_root = 0; int am_server = 0; int am_sender = 0; int am_generator = 0; int am_starting_up = 1; -int orig_umask = 0; int relative_paths = -1; int implied_dirs = 1; int numeric_ids = 0; +int allow_8bit_chars = 0; int force_delete = 0; int io_timeout = 0; int allowed_lull = 0; +int prune_empty_dirs = 0; char *files_from = NULL; int filesfrom_fd = -1; char *filesfrom_host = NULL; int eol_nulls = 0; +int human_readable = 0; int recurse = 0; -int xfer_dirs = 0; +int xfer_dirs = -1; int am_daemon = 0; int daemon_over_rsh = 0; int do_stats = 0; @@ -94,11 +103,12 @@ int bwlimit = 0; int fuzzy_basis = 0; size_t bwlimit_writemax = 0; -int only_existing = 0; -int opt_ignore_existing = 0; +int ignore_existing = 0; +int ignore_non_existing = 0; int need_messages_from_generator = 0; int max_delete = 0; OFF_T max_size = 0; +OFF_T min_size = 0; int ignore_errors = 0; int modify_window = 0; int blocking_io = -1; @@ -142,6 +152,7 @@ char *rsync_path = RSYNC_PATH; char *backup_dir = NULL; char backup_dir_buf[MAXPATHLEN]; +char *sockopts = NULL; int rsync_port = 0; int compare_dest = 0; int copy_dest = 0; @@ -160,14 +171,17 @@ #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */ char *batch_name = NULL; +struct chmod_mode_struct *chmod_modes = NULL; + static int daemon_opt; /* sets am_daemon after option error-reporting */ static int F_option_cnt = 0; static int modify_window_set; static int itemize_changes = 0; -static int refused_delete, refused_archive_part; +static int refused_delete, refused_archive_part, refused_compress; static int refused_partial, refused_progress, refused_delete_before; -static char *max_size_arg; -static char partialdir_for_delayupdate[] = ".~tmp~"; +static int refused_inplace; +static char *max_size_arg, *min_size_arg; +static char tmp_partialdir[] = ".~tmp~"; /** Local address to bind. As a character string because it's * interpreted by the IPv6 layer: should be a numeric IP4 or IP6 @@ -206,24 +220,23 @@ rprintf(f, "%s version %s protocol version %d\n", RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION); - rprintf(f, - "Copyright (C) 1996-2005 by Andrew Tridgell and others\n"); + rprintf(f, "Copyright (C) 1996-2006 by Andrew Tridgell, Wayne Davison, and others.\n"); rprintf(f, "\n"); rprintf(f, "Capabilities: %d-bit files, %ssocketpairs, " - "%shard links, %ssymlinks, batchfiles, \n", + "%shard links, %ssymlinks, batchfiles,\n", (int) (sizeof (OFF_T) * 8), got_socketpair, hardlinks, links); /* Note that this field may not have type ino_t. It depends * on the complicated interaction between largefile feature * macros. */ - rprintf(f, " %sinplace, %sIPv6, %d-bit system inums, %d-bit internal inums\n", + rprintf(f, " %sinplace, %sIPv6, " + "%d-bit system inums, %d-bit internal inums\n", have_inplace, ipv6, (int) (sizeof dumstat->st_ino * 8), (int) (sizeof (int64) * 8)); #ifdef MAINTAINER_MODE - rprintf(f, " panic action: \"%s\"\n", - get_panic_action()); + rprintf(f, "Panic Action: \"%s\"\n", get_panic_action()); #endif #if SIZEOF_INT64 < 8 @@ -235,12 +248,9 @@ (int) SIZEOF_INT64, (int) sizeof (int64)); } - rprintf(f, -"\n" -"rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n" -"are welcome to redistribute it under certain conditions. See the GNU\n" -"General Public Licence for details.\n" - ); + rprintf(f,"\nrsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"); + rprintf(f,"are welcome to redistribute it under certain conditions. See the GNU\n"); + rprintf(f,"General Public Licence for details.\n"); } @@ -248,54 +258,60 @@ { print_rsync_version(F); - rprintf(F,"\nrsync is a file transfer program capable of efficient remote update\nvia a fast differencing algorithm.\n\n"); + rprintf(F,"\nrsync is a file transfer program capable of efficient remote update\n"); + rprintf(F,"via a fast differencing algorithm.\n"); - rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n"); + rprintf(F,"\nUsage: rsync [OPTION]... SRC [SRC]... DEST\n"); + rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n"); + rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n"); + rprintf(F," or rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n"); rprintf(F," or rsync [OPTION]... [USER@]HOST:SRC [DEST]\n"); - rprintf(F," or rsync [OPTION]... SRC [SRC]... DEST\n"); rprintf(F," or rsync [OPTION]... [USER@]HOST::SRC [DEST]\n"); - rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n"); rprintf(F," or rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]\n"); - rprintf(F," or rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n"); - rprintf(F,"SRC on single-colon remote HOST will be expanded by remote shell\n"); - rprintf(F,"SRC on server remote HOST may contain shell wildcards or multiple\n"); - rprintf(F," sources separated by space as long as they have same top-level\n"); + rprintf(F,"The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect\n"); + rprintf(F,"to an rsync daemon, and require SRC or DEST to start with a module name.\n"); rprintf(F,"\nOptions\n"); rprintf(F," -v, --verbose increase verbosity\n"); rprintf(F," -q, --quiet suppress non-error messages\n"); rprintf(F," -c, --checksum skip based on checksum, not mod-time & size\n"); rprintf(F," -a, --archive archive mode; same as -rlptgoD (no -H)\n"); + rprintf(F," --no-OPTION turn off an implied OPTION (e.g. --no-D)\n"); rprintf(F," -r, --recursive recurse into directories\n"); rprintf(F," -R, --relative use relative path names\n"); - rprintf(F," --no-relative turn off --relative\n"); - rprintf(F," --no-implied-dirs don't send implied dirs with -R\n"); + rprintf(F," --no-implied-dirs don't send implied dirs with --relative\n"); rprintf(F," -b, --backup make backups (see --suffix & --backup-dir)\n"); rprintf(F," --backup-dir=DIR make backups into hierarchy based in DIR\n"); rprintf(F," --suffix=SUFFIX set backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX); rprintf(F," -u, --update skip files that are newer on the receiver\n"); rprintf(F," --inplace update destination files in-place (SEE MAN PAGE)\n"); + rprintf(F," --append append data onto shorter files\n"); rprintf(F," -d, --dirs transfer directories without recursing\n"); rprintf(F," -l, --links copy symlinks as symlinks\n"); rprintf(F," -L, --copy-links transform symlink into referent file/dir\n"); rprintf(F," --copy-unsafe-links only \"unsafe\" symlinks are transformed\n"); rprintf(F," --safe-links ignore symlinks that point outside the source tree\n"); - rprintf(F," -H, --hard-links preserve hard links\n"); + rprintf(F," -k, --copy-dirlinks transform symlink to a dir into referent dir\n"); rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n"); + rprintf(F," -H, --hard-links preserve hard links\n"); rprintf(F," -p, --perms preserve permissions\n"); - rprintf(F," -o, --owner preserve owner (root only)\n"); + rprintf(F," -E, --executability preserve the file's executability\n"); + rprintf(F," --chmod=CHMOD change destination permissions\n"); + rprintf(F," -o, --owner preserve owner (super-user only)\n"); rprintf(F," -g, --group preserve group\n"); - rprintf(F," -D, --devices preserve devices (root only)\n"); + rprintf(F," --devices preserve device files (super-user only)\n"); + rprintf(F," --specials preserve special files\n"); + rprintf(F," -D same as --devices --specials\n"); rprintf(F," -t, --times preserve times\n"); rprintf(F," -O, --omit-dir-times omit directories when preserving times\n"); + rprintf(F," --super receiver attempts super-user activities\n"); rprintf(F," -S, --sparse handle sparse files efficiently\n"); rprintf(F," -n, --dry-run show what would have been transferred\n"); rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n"); - rprintf(F," --no-whole-file always use incremental rsync algorithm\n"); rprintf(F," -x, --one-file-system don't cross filesystem boundaries\n"); rprintf(F," -B, --block-size=SIZE force a fixed checksum block-size\n"); rprintf(F," -e, --rsh=COMMAND specify the remote shell to use\n"); rprintf(F," --rsync-path=PROGRAM specify the rsync to run on the remote machine\n"); - rprintf(F," --existing only update files that already exist on receiver\n"); + rprintf(F," --existing ignore non-existing files on receiving side\n"); rprintf(F," --ignore-existing ignore files that already exist on receiving side\n"); rprintf(F," --remove-sent-files sent files/symlinks are removed from sending side\n"); rprintf(F," --del an alias for --delete-during\n"); @@ -308,9 +324,11 @@ rprintf(F," --force force deletion of directories even if not empty\n"); rprintf(F," --max-delete=NUM don't delete more than NUM files\n"); rprintf(F," --max-size=SIZE don't transfer any file larger than SIZE\n"); + rprintf(F," --min-size=SIZE don't transfer any file smaller than SIZE\n"); rprintf(F," --partial keep partially transferred files\n"); rprintf(F," --partial-dir=DIR put a partially transferred file into DIR\n"); rprintf(F," --delay-updates put all updated files into place at transfer's end\n"); + rprintf(F," -m, --prune-empty-dirs prune empty directory chains from the file-list\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 don't skip files that match in size and mod-time\n"); @@ -322,6 +340,7 @@ rprintf(F," --copy-dest=DIR ... and include copies of unchanged files\n"); rprintf(F," --link-dest=DIR hardlink to files in DIR when unchanged\n"); rprintf(F," -z, --compress compress file data during the transfer\n"); + rprintf(F," --compress-level=NUM explicitly set compression level\n"); rprintf(F," -C, --cvs-exclude auto-ignore files the same way CVS does\n"); rprintf(F," -f, --filter=RULE add a file-filtering RULE\n"); rprintf(F," -F same as --filter='dir-merge /.rsync-filter'\n"); @@ -334,9 +353,11 @@ rprintf(F," -0, --from0 all *-from/filter files are delimited by 0s\n"); rprintf(F," --address=ADDRESS bind address for outgoing socket to daemon\n"); rprintf(F," --port=PORT specify double-colon alternate port number\n"); + rprintf(F," --sockopts=OPTIONS specify custom TCP options\n"); rprintf(F," --blocking-io use blocking I/O for the remote shell\n"); - rprintf(F," --no-blocking-io turn off blocking I/O when it is the default\n"); rprintf(F," --stats give some file-transfer stats\n"); + rprintf(F," -8, --8-bit-output leave high-bit chars unescaped in output\n"); + rprintf(F," -h, --human-readable output numbers in a human-readable format\n"); rprintf(F," --progress show progress during transfer\n"); rprintf(F," -P same as --partial --progress\n"); rprintf(F," -i, --itemize-changes output a change-summary for all updates\n"); @@ -353,7 +374,7 @@ rprintf(F," -6, --ipv6 prefer IPv6\n"); #endif rprintf(F," --version print version number\n"); - rprintf(F," -h, --help show this help screen\n"); + rprintf(F,"(-h) --help show this help (-h works with no other options)\n"); rprintf(F,"\nUse \"rsync --daemon --help\" to see the daemon-mode command-line options.\n"); rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) man pages for full documentation.\n"); @@ -361,23 +382,82 @@ } enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, - OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, - OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, + OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP, + OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE, - OPT_REFUSED_BASE = 9000}; + OPT_NO_D, + OPT_SERVER, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 0, POPT_ARG_NONE, 0, OPT_HELP, 0, 0 }, {"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 }, + {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 }, + {"no-verbose", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 }, + {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 }, + {"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 }, + {"stats", 0, POPT_ARG_NONE, &do_stats, 0, 0, 0 }, + {"human-readable", 'h', POPT_ARG_NONE, 0, 'h', 0, 0}, + {"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 }, + {"archive", 'a', POPT_ARG_NONE, 0, 'a', 0, 0 }, + {"recursive", 'r', POPT_ARG_VAL, &recurse, 2, 0, 0 }, + {"no-recursive", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 }, + {"no-r", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 }, + {"dirs", 'd', POPT_ARG_VAL, &xfer_dirs, 2, 0, 0 }, + {"no-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 }, + {"no-d", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 }, + {"perms", 'p', POPT_ARG_VAL, &preserve_perms, 1, 0, 0 }, + {"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 }, + {"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 }, + {"executability", 'E', POPT_ARG_NONE, &preserve_executability, 0, 0, 0 }, + {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 }, + {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, + {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, + {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 2, 0, 0 }, + {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 }, + {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 }, + {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 }, + {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 }, + {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 }, + {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 }, + {"group", 'g', POPT_ARG_VAL, &preserve_gid, 1, 0, 0 }, + {"no-group", 0, POPT_ARG_VAL, &preserve_gid, 0, 0, 0 }, + {"no-g", 0, POPT_ARG_VAL, &preserve_gid, 0, 0, 0 }, + {0, 'D', POPT_ARG_NONE, 0, 'D', 0, 0 }, + {"no-D", 0, POPT_ARG_NONE, 0, OPT_NO_D, 0, 0 }, + {"devices", 0, POPT_ARG_VAL, &preserve_devices, 1, 0, 0 }, + {"no-devices", 0, POPT_ARG_VAL, &preserve_devices, 0, 0, 0 }, + {"specials", 0, POPT_ARG_VAL, &preserve_specials, 1, 0, 0 }, + {"no-specials", 0, POPT_ARG_VAL, &preserve_specials, 0, 0, 0 }, + {"links", 'l', POPT_ARG_VAL, &preserve_links, 1, 0, 0 }, + {"no-links", 0, POPT_ARG_VAL, &preserve_links, 0, 0, 0 }, + {"no-l", 0, POPT_ARG_VAL, &preserve_links, 0, 0, 0 }, + {"copy-links", 'L', POPT_ARG_NONE, ©_links, 0, 0, 0 }, + {"copy-unsafe-links",0, POPT_ARG_NONE, ©_unsafe_links, 0, 0, 0 }, + {"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 }, + {"copy-dirlinks", 'k', POPT_ARG_NONE, ©_dirlinks, 0, 0, 0 }, + {"keep-dirlinks", 'K', POPT_ARG_NONE, &keep_dirlinks, 0, 0, 0 }, + {"hard-links", 'H', POPT_ARG_VAL, &preserve_hard_links, 1, 0, 0 }, + {"no-hard-links", 0, POPT_ARG_VAL, &preserve_hard_links, 0, 0, 0 }, + {"no-H", 0, POPT_ARG_VAL, &preserve_hard_links, 0, 0, 0 }, + {"relative", 'R', POPT_ARG_VAL, &relative_paths, 1, 0, 0 }, + {"no-relative", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 }, + {"no-R", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 }, + {"implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 1, 0, 0 }, + {"no-implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 }, + {"chmod", 0, POPT_ARG_STRING, 0, OPT_CHMOD, 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 }, - {"one-file-system", 'x', POPT_ARG_NONE, &one_file_system, 0, 0, 0 }, - {"existing", 0, POPT_ARG_NONE, &only_existing, 0, 0, 0 }, - {"ignore-existing", 0, POPT_ARG_NONE, &opt_ignore_existing, 0, 0, 0 }, + {"one-file-system", 'x', POPT_ARG_NONE, 0, 'x', 0, 0 }, + {"update", 'u', POPT_ARG_NONE, &update_only, 0, 0, 0 }, + {"existing", 0, POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 }, + {"ignore-non-existing",0,POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 }, + {"ignore-existing", 0, POPT_ARG_NONE, &ignore_existing, 0, 0, 0 }, + {"max-size", 0, POPT_ARG_STRING, &max_size_arg, OPT_MAX_SIZE, 0, 0 }, + {"min-size", 0, POPT_ARG_STRING, &min_size_arg, OPT_MIN_SIZE, 0, 0 }, + {"sparse", 'S', POPT_ARG_NONE, &sparse_files, 0, 0, 0 }, + {"inplace", 0, POPT_ARG_NONE, &inplace, 0, 0, 0 }, + {"append", 0, POPT_ARG_VAL, &append_mode, 1, 0, 0 }, {"del", 0, POPT_ARG_NONE, &delete_during, 0, 0, 0 }, {"delete", 0, POPT_ARG_NONE, &delete_mode, 0, 0, 0 }, {"delete-before", 0, POPT_ARG_VAL, &delete_before, 2, 0, 0 }, @@ -386,85 +466,67 @@ {"delete-excluded", 0, POPT_ARG_NONE, &delete_excluded, 0, 0, 0 }, {"remove-sent-files",0, POPT_ARG_NONE, &remove_sent_files, 0, 0, 0 }, {"force", 0, POPT_ARG_NONE, &force_delete, 0, 0, 0 }, - {"numeric-ids", 0, POPT_ARG_NONE, &numeric_ids, 0, 0, 0 }, + {"ignore-errors", 0, POPT_ARG_NONE, &ignore_errors, 0, 0, 0 }, + {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 }, + {0, 'F', POPT_ARG_NONE, 0, 'F', 0, 0 }, {"filter", 'f', POPT_ARG_STRING, 0, OPT_FILTER, 0, 0 }, {"exclude", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE, 0, 0 }, {"include", 0, POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 }, {"exclude-from", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 }, {"include-from", 0, POPT_ARG_STRING, 0, OPT_INCLUDE_FROM, 0, 0 }, - {"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 }, - {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 }, - {"backup", 'b', POPT_ARG_NONE, &make_backups, 0, 0, 0 }, - {"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 }, - {"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 }, - {"dirs", 'd', POPT_ARG_VAL, &xfer_dirs, 2, 0, 0 }, - {"links", 'l', POPT_ARG_NONE, &preserve_links, 0, 0, 0 }, - {"copy-links", 'L', POPT_ARG_NONE, ©_links, 0, 0, 0 }, - {"keep-dirlinks", 'K', POPT_ARG_NONE, &keep_dirlinks, 0, 0, 0 }, {"whole-file", 'W', POPT_ARG_VAL, &whole_file, 1, 0, 0 }, {"no-whole-file", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 }, - {"copy-unsafe-links",0, POPT_ARG_NONE, ©_unsafe_links, 0, 0, 0 }, - {"perms", 'p', POPT_ARG_NONE, &preserve_perms, 0, 0, 0 }, - {"owner", 'o', POPT_ARG_NONE, &preserve_uid, 0, 0, 0 }, - {"group", 'g', POPT_ARG_NONE, &preserve_gid, 0, 0, 0 }, - {"devices", 'D', POPT_ARG_NONE, &preserve_devices, 0, 0, 0 }, - {"times", 't', POPT_ARG_NONE, &preserve_times, 0, 0, 0 }, - {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 2, 0, 0 }, + {"no-W", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 }, {"checksum", 'c', POPT_ARG_NONE, &always_checksum, 0, 0, 0 }, - {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 }, - {"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 }, - {"archive", 'a', POPT_ARG_NONE, &archive_mode, 0, 0, 0 }, - {"server", 0, POPT_ARG_NONE, &am_server, 0, 0, 0 }, - {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 }, - {"recursive", 'r', POPT_ARG_NONE, &recurse, 0, 0, 0 }, - {"list-only", 0, POPT_ARG_VAL, &list_only, 2, 0, 0 }, - {"relative", 'R', POPT_ARG_VAL, &relative_paths, 1, 0, 0 }, - {"no-relative", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 }, - {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 }, {"block-size", 'B', POPT_ARG_LONG, &block_size, 0, 0, 0 }, - {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 }, - {"max-size", 0, POPT_ARG_STRING, &max_size_arg, OPT_MAX_SIZE, 0, 0 }, - {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 }, - {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, {"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 }, {"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 }, {"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 }, {"fuzzy", 'y', POPT_ARG_NONE, &fuzzy_basis, 0, 0, 0 }, - /* TODO: Should this take an optional int giving the compression level? */ - {"compress", 'z', POPT_ARG_NONE, &do_compression, 0, 0, 0 }, - {"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 }, + {"compress", 'z', POPT_ARG_NONE, 0, 'z', 0, 0 }, + {"compress-level", 0, POPT_ARG_INT, &def_compress_level, 'z', 0, 0 }, + {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 }, + {"progress", 0, POPT_ARG_VAL, &do_progress, 1, 0, 0 }, + {"no-progress", 0, POPT_ARG_VAL, &do_progress, 0, 0, 0 }, + {"partial", 0, POPT_ARG_VAL, &keep_partial, 1, 0, 0 }, + {"no-partial", 0, POPT_ARG_VAL, &keep_partial, 0, 0, 0 }, {"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 }, {"delay-updates", 0, POPT_ARG_NONE, &delay_updates, 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 }, - {0, 'F', POPT_ARG_NONE, 0, 'F', 0, 0 }, - {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 }, - {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 }, - {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 }, + {"prune-empty-dirs",'m', POPT_ARG_NONE, &prune_empty_dirs, 0, 0, 0 }, {"log-format", 0, POPT_ARG_STRING, &log_format, 0, 0, 0 }, - {"itemize-changes", 'i', POPT_ARG_NONE, &itemize_changes, 0, 0, 0 }, + {"itemize-changes", 'i', POPT_ARG_NONE, 0, 'i', 0, 0 }, {"bwlimit", 0, POPT_ARG_INT, &bwlimit, 0, 0, 0 }, + {"backup", 'b', POPT_ARG_NONE, &make_backups, 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 }, + {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 }, + {"list-only", 0, POPT_ARG_VAL, &list_only, 2, 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 }, {"only-write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_ONLY_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 }, + {"numeric-ids", 0, POPT_ARG_NONE, &numeric_ids, 0, 0, 0 }, + {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 }, + {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 }, + {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, + {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, #ifdef INET6 {"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 - /* All these options switch us into daemon-mode option-parsing. */ + {"8-bit-output", '8', POPT_ARG_NONE, &allow_8bit_chars, 0, 0, 0 }, + {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 }, + {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 }, + {"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 }, + {"password-file", 0, POPT_ARG_STRING, &password_file, 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 }, + {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, + {"checksum-seed", 0, POPT_ARG_INT, &checksum_seed, 0, 0, 0 }, + {"server", 0, POPT_ARG_NONE, 0, OPT_SERVER, 0, 0 }, + {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 }, + /* All the following options switch us into daemon-mode option-parsing. */ {"config", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 }, {"daemon", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 }, {"detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 }, @@ -482,12 +544,13 @@ rprintf(F," --config=FILE specify alternate rsyncd.conf file\n"); rprintf(F," --no-detach do not detach from the parent\n"); rprintf(F," --port=PORT listen on alternate port number\n"); + rprintf(F," --sockopts=OPTIONS specify custom TCP options\n"); rprintf(F," -v, --verbose increase verbosity\n"); #ifdef INET6 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," --help show this help screen\n"); rprintf(F,"\nIf you were not trying to invoke rsync as a daemon, avoid using any of the\n"); rprintf(F,"daemon-specific rsync options. See also the rsyncd.conf(5) man page.\n"); @@ -506,9 +569,13 @@ {"detach", 0, POPT_ARG_VAL, &no_detach, 0, 0, 0 }, {"no-detach", 0, POPT_ARG_VAL, &no_detach, 1, 0, 0 }, {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 }, + {"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 }, {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, {"server", 0, POPT_ARG_NONE, &am_server, 0, 0, 0 }, + {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 }, + {"no-verbose", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 }, + {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 }, {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 }, {0,0,0,0, 0, 0, 0} }; @@ -530,6 +597,7 @@ } rprintf(FERROR, RSYNC_NAME ": %s", err_buf); + msleep(20); } @@ -570,6 +638,9 @@ case 't': case 'g': case 'o': case 'D': refused_archive_part = op->val; break; + case 'z': + refused_compress = op->val; + break; case '\0': if (wildmatch("delete", op->longName)) refused_delete = op->val; @@ -579,6 +650,8 @@ refused_partial = op->val; else if (wildmatch("progress", op->longName)) refused_progress = op->val; + else if (wildmatch("inplace", op->longName)) + refused_inplace = op->val; break; } if (!is_wild) @@ -621,30 +694,58 @@ } -static OFF_T parse_size_arg(const char *size_arg) +static OFF_T parse_size_arg(char **size_arg, char def_suf) { + int reps, mult, make_compatible = 0; const char *arg; - OFF_T size; + OFF_T size = 1; - for (arg = size_arg; isdigit(*(uchar*)arg); arg++) {} + for (arg = *size_arg; isdigit(*(uchar*)arg); arg++) {} if (*arg == '.') for (arg++; isdigit(*(uchar*)arg); arg++) {} - switch (*arg) { + switch (*arg && *arg != '+' && *arg != '-' ? *arg++ : def_suf) { + case 'b': case 'B': + reps = 0; + break; case 'k': case 'K': - size = atof(size_arg) * 1024; + reps = 1; break; case 'm': case 'M': - size = atof(size_arg) * 1024*1024; + reps = 2; break; case 'g': case 'G': - size = atof(size_arg) * 1024*1024*1024; - break; - case '\0': - size = atof(size_arg); + reps = 3; break; default: - size = 0; - break; + return -1; + } + if (*arg == 'b' || *arg == 'B') + mult = 1000, make_compatible = 1, arg++; + else if (!*arg || *arg == '+' || *arg == '-') + mult = 1024; + else if (strncasecmp(arg, "ib", 2) == 0) + mult = 1024, arg += 2; + else + return -1; + while (reps--) + size *= mult; + size *= atof(*size_arg); + if ((*arg == '+' || *arg == '-') && arg[1] == '1') + size += atoi(arg), make_compatible = 1, arg += 2; + if (*arg) + return -1; + if (size > 0 && make_compatible) { + /* We convert this manually because we may need %lld precision, + * and that's not a portable sprintf() escape. */ + char buf[128], *s = buf + sizeof buf - 1; + OFF_T num = size; + *s = '\0'; + while (num) { + *--s = (num % 10) + '0'; + num /= 10; + } + if (!(*size_arg = strdup(s))) + out_of_memory("parse_size_arg"); } return size; } @@ -698,6 +799,25 @@ print_rsync_version(FINFO); exit_cleanup(0); + case OPT_SERVER: + if (!am_server) { + /* Disable popt aliases on the server side and + * then start parsing the options again. */ + poptFreeContext(pc); + pc = poptGetContext(RSYNC_NAME, *argc, *argv, + long_options, 0); + am_server = 1; + } + break; + + case OPT_SENDER: + if (!am_server) { + usage(FERROR); + exit_cleanup(RERR_SYNTAX); + } + am_sender = 1; + break; + case OPT_DAEMON: if (am_daemon) { strcpy(err_buf, "Attempt to hack rsync thwarted!\n"); @@ -724,6 +844,13 @@ goto daemon_error; } } + + if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) { + snprintf(err_buf, sizeof err_buf, + "the --temp-dir path is WAY too long.\n"); + return 0; + } + if (!daemon_opt) { rprintf(FERROR, "Daemon option(s) used without --daemon.\n"); daemon_error: @@ -731,6 +858,7 @@ "(Type \"rsync --daemon --help\" for assistance with daemon mode.)\n"); exit_cleanup(RERR_SYNTAX); } + *argv = poptGetArgs(pc); *argc = count_args(*argv); am_starting_up = 0; @@ -777,9 +905,39 @@ XFLG_FATAL_ERRORS | XFLG_OLD_PREFIXES); break; + case 'a': + if (refused_archive_part) { + create_refuse_error(refused_archive_part); + return 0; + } + if (!recurse) /* preserve recurse == 2 */ + recurse = 1; +#ifdef SUPPORT_LINKS + preserve_links = 1; +#endif + preserve_perms = 1; + preserve_times = 1; + preserve_gid = 1; + preserve_uid = 1; + preserve_devices = 1; + preserve_specials = 1; + break; + + case 'D': + preserve_devices = preserve_specials = 1; + break; + + case OPT_NO_D: + preserve_devices = preserve_specials = 0; + break; + case 'h': - usage(FINFO); - exit_cleanup(0); + human_readable++; + break; + + case 'i': + itemize_changes++; + break; case 'v': verbose++; @@ -790,12 +948,8 @@ quiet++; break; - case OPT_SENDER: - if (!am_server) { - usage(FERROR); - exit_cleanup(RERR_SYNTAX); - } - am_sender = 1; + case 'x': + one_file_system++; break; case 'F': @@ -819,6 +973,21 @@ keep_partial = 1; break; + case 'z': + if (def_compress_level < Z_DEFAULT_COMPRESSION + || def_compress_level > Z_BEST_COMPRESSION) { + snprintf(err_buf, sizeof err_buf, + "--compress-level value is invalid: %d\n", + def_compress_level); + return 0; + } + do_compression = def_compress_level != Z_NO_COMPRESSION; + if (do_compression && refused_compress) { + create_refuse_error(refused_compress); + return 0; + } + break; + case OPT_WRITE_BATCH: /* batch_name is already set */ write_batch = 1; @@ -835,7 +1004,7 @@ break; case OPT_MAX_SIZE: - if ((max_size = parse_size_arg(max_size_arg)) <= 0) { + if ((max_size = parse_size_arg(&max_size_arg, 'b')) <= 0) { snprintf(err_buf, sizeof err_buf, "--max-size value is invalid: %s\n", max_size_arg); @@ -843,6 +1012,15 @@ } break; + case OPT_MIN_SIZE: + if ((min_size = parse_size_arg(&min_size_arg, 'b')) <= 0) { + snprintf(err_buf, sizeof err_buf, + "--min-size value is invalid: %s\n", + min_size_arg); + return 0; + } + break; + case OPT_LINK_DEST: #ifdef HAVE_LINK link_dest = 1; @@ -876,6 +1054,20 @@ basis_dir[basis_dir_cnt++] = (char *)arg; break; + case OPT_CHMOD: + arg = poptGetOptArg(pc); + if (!parse_chmod(arg, &chmod_modes)) { + snprintf(err_buf, sizeof err_buf, + "Invalid argument passed to --chmod (%s)\n", + arg); + return 0; + } + break; + + case OPT_HELP: + usage(FINFO); + exit_cleanup(0); + default: /* A large opt value means that set_refuse_options() * turned this option off. */ @@ -891,6 +1083,12 @@ } } + if (human_readable && *argc == 2) { + /* Allow the old meaning of 'h' (--help) on its own. */ + usage(FINFO); + exit_cleanup(0); + } + #ifndef SUPPORT_LINKS if (preserve_links && !am_sender) { snprintf(err_buf, sizeof err_buf, @@ -951,38 +1149,27 @@ return 0; } - if (archive_mode) { - if (refused_archive_part) { - create_refuse_error(refused_archive_part); - return 0; - } - if (!files_from) - recurse = 1; -#ifdef SUPPORT_LINKS - preserve_links = 1; -#endif - preserve_perms = 1; - preserve_times = 1; - preserve_gid = 1; - preserve_uid = 1; - preserve_devices = 1; + if (files_from) { + if (recurse == 1) /* preserve recurse == 2 */ + recurse = 0; + if (xfer_dirs < 0) + xfer_dirs = 1; } - if (recurse || list_only || files_from) - xfer_dirs |= 1; + if (xfer_dirs < 1) + xfer_dirs = recurse || list_only; if (relative_paths < 0) relative_paths = files_from? 1 : 0; + if (!relative_paths) + implied_dirs = 0; if (!!delete_before + delete_during + delete_after > 1) { snprintf(err_buf, sizeof err_buf, "You may not combine multiple --delete-WHEN options.\n"); return 0; } - if (!recurse) { - delete_before = delete_during = delete_after = 0; - delete_mode = delete_excluded = 0; - } else if (delete_before || delete_during || delete_after) + if (delete_before || delete_during || delete_after) delete_mode = 1; else if (delete_mode || delete_excluded) { if (refused_delete_before) { @@ -991,6 +1178,12 @@ } delete_mode = delete_before = 1; } + saw_delete_opt = delete_mode; + saw_delete_excluded_opt = delete_excluded; + if (!xfer_dirs) { + delete_before = delete_during = delete_after = 0; + delete_mode = delete_excluded = 0; + } if (delete_mode && refused_delete) { create_refuse_error(refused_delete); @@ -1078,32 +1271,35 @@ backup_dir_buf[backup_dir_len++] = '/'; backup_dir_buf[backup_dir_len] = '\0'; } - if (verbose > 1 && !am_sender) { - rprintf(FINFO, "backup_dir is %s\n", - safe_fname(backup_dir_buf)); - } + if (verbose > 1 && !am_sender) + rprintf(FINFO, "backup_dir is %s\n", backup_dir_buf); } else if (!backup_suffix_len && (!am_server || !am_sender)) { snprintf(err_buf, sizeof err_buf, "--suffix cannot be a null string without --backup-dir\n"); return 0; + } else if (make_backups && delete_mode && !delete_excluded) { + snprintf(backup_dir_buf, sizeof backup_dir_buf, + "P *%s", backup_suffix); + parse_rule(&filter_list, backup_dir_buf, 0, 0); } if (make_backups && !backup_dir) omit_dir_times = 1; if (log_format) { - if (log_format_has(log_format, 'i')) - log_format_has_i = 1; + if (am_server && log_format_has(log_format, 'I')) + log_format_has_i = 2; + else if (log_format_has(log_format, 'i')) + log_format_has_i = itemize_changes | 1; if (!log_format_has(log_format, 'b') && !log_format_has(log_format, 'c')) log_before_transfer = !am_server; } else if (itemize_changes) { log_format = "%i %n%L"; - log_format_has_i = 1; + log_format_has_i = itemize_changes; log_before_transfer = !am_server; } - if ((do_progress || dry_run) && !verbose && !log_before_transfer - && !am_server) + if (do_progress && !verbose && !log_before_transfer && !am_server) verbose = 1; if (dry_run) @@ -1126,14 +1322,36 @@ bwlimit_writemax = 512; } + if (sparse_files && inplace) { + /* Note: we don't check for this below, because --append is + * OK with --sparse (as long as redos are handled right). */ + snprintf(err_buf, sizeof err_buf, + "--sparse cannot be used with --inplace\n"); + return 0; + } + + if (append_mode) { + if (whole_file > 0) { + snprintf(err_buf, sizeof err_buf, + "--append cannot be used with --whole-file\n"); + return 0; + } + if (refused_inplace) { + create_refuse_error(refused_inplace); + return 0; + } + inplace = 1; + } + if (delay_updates && !partial_dir) - partial_dir = partialdir_for_delayupdate; + partial_dir = tmp_partialdir; if (inplace) { #ifdef HAVE_FTRUNCATE if (partial_dir) { snprintf(err_buf, sizeof err_buf, - "--inplace cannot be used with --%s\n", + "--%s cannot be used with --%s\n", + append_mode ? "append" : "inplace", delay_updates ? "delay-updates" : "partial-dir"); return 0; } @@ -1146,12 +1364,13 @@ keep_partial = 0; #else snprintf(err_buf, sizeof err_buf, - "--inplace is not supported on this %s\n", + "--%s is not supported on this %s\n", + append_mode ? "append" : "inplace", am_server ? "server" : "client"); return 0; #endif } else { - if (keep_partial && !partial_dir) { + if (keep_partial && !partial_dir && !am_server) { if ((arg = getenv("RSYNC_PARTIAL_DIR")) != NULL && *arg) partial_dir = strdup(arg); } @@ -1241,6 +1460,7 @@ if (blocking_io == -1) blocking_io = 0; + /* This should always remain first on the server's command-line. */ args[ac++] = "--server"; if (daemon_over_rsh) { @@ -1263,16 +1483,25 @@ argstr[x++] = 'b'; if (update_only) argstr[x++] = 'u'; - if (!do_xfers) /* NOT "dry_run"! */ + if (!do_xfers) /* Note: NOT "dry_run"! */ argstr[x++] = 'n'; if (preserve_links) argstr[x++] = 'l'; - if (copy_links) - argstr[x++] = 'L'; - if (xfer_dirs > 1) + if (xfer_dirs > (recurse || !delete_mode || !am_sender)) argstr[x++] = 'd'; - if (keep_dirlinks && am_sender) - argstr[x++] = 'K'; + if (am_sender) { + if (keep_dirlinks) + argstr[x++] = 'K'; + if (prune_empty_dirs) + argstr[x++] = 'm'; + if (omit_dir_times == 2) + argstr[x++] = 'O'; + } else { + if (copy_links) + argstr[x++] = 'L'; + if (copy_dirlinks) + argstr[x++] = 'k'; + } if (whole_file > 0) argstr[x++] = 'W'; @@ -1286,14 +1515,14 @@ argstr[x++] = 'o'; if (preserve_gid) argstr[x++] = 'g'; - if (preserve_devices) + if (preserve_devices) /* ignore preserve_specials here */ argstr[x++] = 'D'; if (preserve_times) argstr[x++] = 't'; - if (omit_dir_times == 2 && am_sender) - argstr[x++] = 'O'; if (preserve_perms) argstr[x++] = 'p'; + else if (preserve_executability && am_sender) + argstr[x++] = 'E'; if (recurse) argstr[x++] = 'r'; if (always_checksum) @@ -1304,8 +1533,11 @@ argstr[x++] = 'I'; if (relative_paths) argstr[x++] = 'R'; - if (one_file_system) + if (one_file_system) { argstr[x++] = 'x'; + if (one_file_system > 1) + argstr[x++] = 'x'; + } if (sparse_files) argstr[x++] = 'S'; if (do_compression) @@ -1317,7 +1549,7 @@ if (list_only == 1 && !recurse) argstr[x++] = 'r'; - argstr[x] = 0; + argstr[x] = '\0'; if (x != 1) args[ac++] = argstr; @@ -1325,10 +1557,30 @@ if (list_only > 1) args[ac++] = "--list-only"; + /* This makes sure that the remote rsync can handle deleting with -d + * sans -r because the --no-r option was added at the same time. */ + if (xfer_dirs && !recurse && delete_mode && am_sender) + args[ac++] = "--no-r"; + + if (do_compression && def_compress_level != Z_DEFAULT_COMPRESSION) { + if (asprintf(&arg, "--compress-level=%d", def_compress_level) < 0) + goto oom; + args[ac++] = arg; + } + + if (preserve_devices) { + /* Note: sending "--devices" would not be backward-compatible. */ + if (!preserve_specials) + args[ac++] = "--no-specials"; /* -D is already set. */ + } else if (preserve_specials) + args[ac++] = "--specials"; + /* The server side doesn't use our log-format, but in certain * circumstances they need to know a little about the option. */ if (log_format && am_sender) { - if (log_format_has_i) + if (log_format_has_i > 1) + args[ac++] = "--log-format=%i%I"; + else if (log_format_has_i) args[ac++] = "--log-format=%i"; else if (log_format_has_o_or_i) args[ac++] = "--log-format=%o"; @@ -1348,6 +1600,11 @@ args[ac++] = arg; } + if (min_size && am_sender) { + args[ac++] = "--min-size"; + args[ac++] = min_size_arg; + } + if (max_size && am_sender) { args[ac++] = "--max-size"; args[ac++] = max_size_arg; @@ -1393,11 +1650,12 @@ args[ac++] = "--force"; if (write_batch < 0) args[ac++] = "--only-write-batch=X"; + if (am_root > 1) + args[ac++] = "--super"; + if (size_only) + args[ac++] = "--size-only"; } - if (size_only) - args[ac++] = "--size-only"; - if (modify_window_set) { if (asprintf(&arg, "--modify-window=%d", modify_window) < 0) goto oom; @@ -1411,13 +1669,13 @@ } if (partial_dir && am_sender) { - if (partial_dir != partialdir_for_delayupdate) { + if (partial_dir != tmp_partialdir) { args[ac++] = "--partial-dir"; args[ac++] = partial_dir; } if (delay_updates) args[ac++] = "--delay-updates"; - } else if (keep_partial) + } else if (keep_partial && am_sender) args[ac++] = "--partial"; if (ignore_errors) @@ -1432,16 +1690,19 @@ if (numeric_ids) args[ac++] = "--numeric-ids"; - if (only_existing && am_sender) - args[ac++] = "--existing"; - - if (opt_ignore_existing && am_sender) + if (ignore_existing && am_sender) args[ac++] = "--ignore-existing"; - if (inplace) + /* Backward compatibility: send --existing, not --ignore-non-existing. */ + if (ignore_non_existing && am_sender) + args[ac++] = "--existing"; + + if (append_mode) + args[ac++] = "--append"; + else if (inplace) args[ac++] = "--inplace"; - if (tmpdir) { + if (tmpdir && am_sender) { args[ac++] = "--temp-dir"; args[ac++] = tmpdir; } @@ -1471,7 +1732,7 @@ if (!relative_paths) args[ac++] = "--no-relative"; } - if (!implied_dirs && !am_sender) + if (relative_paths && !implied_dirs && !am_sender) args[ac++] = "--no-implied-dirs"; if (fuzzy_basis && am_sender) diff -urN --exclude=patches rsync-2.6.6/packaging/lsb/rsync.spec rsync-2.6.7/packaging/lsb/rsync.spec --- rsync-2.6.6/packaging/lsb/rsync.spec 2005-07-28 12:31:05.000000000 -0700 +++ rsync-2.6.7/packaging/lsb/rsync.spec 2006-03-11 10:25:04.000000000 -0800 @@ -1,6 +1,6 @@ Summary: A program for synchronizing files over a network. Name: rsync -Version: 2.6.6 +Version: 2.6.7 Release: 1 Group: Applications/Internet Source: ftp://rsync.samba.org/pub/rsync/rsync-%{version}.tar.gz diff -urN --exclude=patches rsync-2.6.6/packaging/nightly-rsync rsync-2.6.7/packaging/nightly-rsync --- rsync-2.6.6/packaging/nightly-rsync 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.7/packaging/nightly-rsync 2006-02-20 10:35:41.000000000 -0800 @@ -0,0 +1,116 @@ +#!/usr/bin/perl +use strict; + +# This script expects the directory ~/samba-rsync-ftp to exist and to be a +# copy of the /home/ftp/pub/rsync dir on samba.org. It also requires a +# pristine CVS checkout of rsync (don't use your normal rsync build dir +# unless you're 100% sure that there are not unchecked-in changes). +# +# If this is run with -ctu, it will make an updated "nightly" tar file in +# the nightly dir. It will also remove any old tar files, regenerate the +# HTML man pages in the nightly dir, and then rsync the changes to the +# samba.org server. + +use Getopt::Long; +use Date::Format; + +# Choose any dir where a pristine rsync has been checked out of CVS. +our $unpacked = $ENV{HOME} . '/release/nightly'; +# Where the local copy of /home/ftp/pub/rsync/nightly should be updated. +our $nightly = $ENV{HOME} . '/samba-rsync-ftp/nightly'; + +our($cvs_update, $make_tar, $upload, $help_opt); +&Getopt::Long::Configure('bundling'); +&usage if !&GetOptions( + 'cvs-update|c' => \$cvs_update, + 'make-tar|t' => \$make_tar, + 'upload|u' => \$upload, + 'help|h' => \$help_opt, +) || $help_opt; + +our $name = time2str('rsync-HEAD-%Y%m%d-%H%M%Z', time, 'GMT'); +our $ztoday = time2str('%d %b %Y', time); +our $today = $ztoday; + +chdir($unpacked) or die $!; + +if ($cvs_update) { + print "Updating from cvs...\n"; + system 'cvs -q up' and die $!; +} + +if ($make_tar) { + print "Generating list of active CVS files...\n"; + my($dir, @files); + open(CVS, '-|', 'cvs status 2>&1') or die $!; + while () { + if (/^cvs status: Examining (.*)/) { + if ($1 eq '.') { + $dir = ''; + } else { + push(@files, $1); + $dir = $1 . '/'; + } + } elsif (/^File: (.*?)\s+Status: (.*)/ && $1 ne '.cvsignore') { + push(@files, $dir . $1); + if ($2 ne 'Up-to-date') { + print "*** Not up-to-date: $dir$1\n"; + } + } + } + close CVS; + + print "Creating $unpacked/$name.tar.gz\n"; + chdir('..') or die $!; + rename($unpacked, $name) or die $!; + open(TAR, '|-', "fakeroot tar --files-from=- --no-recursion --mode=g-w -czf $nightly/$name.tar.gz $name") or die $!; + foreach (@files) { + print TAR "$name/$_\n"; + } + close TAR; + rename($name, $unpacked) or die $!; +} + +chdir($nightly) or die $!; + +foreach my $fn (qw( rsync.yo rsyncd.conf.yo )) { + my $html_fn = $fn; + $html_fn =~ s/\.yo/.html/; + + open(IN, '<', "$unpacked/$fn") or die $!; + undef $/; $_ = ; $/ = "\n"; + close IN; + + s/^(manpage\([^)]+\)\(\d+\)\()[^)]+(\).*)/$1$today$2/m; + #s/^(This man ?page is current for version) \S+ (of rsync)/$1 $version $2/m; + + open(OUT, '>', $fn) or die $!; + print OUT $_; + close OUT; + + system "yodl2html -o $html_fn $fn"; + + unlink($fn); +} + +system "find . -name 'rsync-HEAD-*' -daystart -mtime +14 | xargs rm -f"; +system 'ls -ltr'; + +if ($upload) { + $ENV{RSYNC_PARTIAL_DIR} = ''; # The rsync on samba.org is OLD. + system "rsync -aviHP --delete . samba.org:/home/ftp/pub/rsync/nightly"; +} + +exit; + +sub usage +{ + die <) { + print $_; + next if /\.(cvs)?ignore$/; + if (m#^[UP] rsync/(.*)#) { + my $fn = $1; + my($dir) = $fn =~ m#^(.+)/#; + push(@files, $dir) if defined($dir) && !$dirs{$1}++; + push(@files, $fn); + } +} + +chdir('rsync') or die $!; + +my($version, $lastversion); +open(IN, 'configure.in') or die $!; +while () { + if (/^RSYNC_VERSION=(.*)/) { + $version = $lastversion = $1; + last; + } +} +close IN; + +$lastversion =~ s/(\d+)cvs$/ $1 - 1 /e; +$version =~ s/cvs/pre1/ || $version =~ s/pre(\d+)/ 'pre' . ($1 + 1) /e; + +print $break, "\nPlease enter the version number of this release: [$version] "; +chomp($_ = ); +if ($_ eq '.') { + $version =~ s/pre\d+//; +} elsif ($_ ne '') { + $version = $_; +} +$version =~ s/[-.]*pre[-.]*/pre/; + +$lastversion =~ s/(\d+)pre\d+$/ $1 - 1 /e unless $version =~ /pre/; + +my $cvstag = "release-$version"; +$cvstag =~ s/[.]/-/g; +$cvstag =~ s/pre/-pre/; + +print "Enter the previous version to produce a patch against: [$lastversion] "; +chomp($_ = ); +$lastversion = $_ if $_ ne ''; +$lastversion =~ s/[-.]*pre[-.]*/pre/; + +my $release = 1; +print "Please enter the RPM release number of this release: [$release] "; +chomp($_ = ); +$release = $_ if $_ ne ''; + +my $diffdir; +my $skipping2; +if ($lastversion =~ /pre/) { + if ($version !~ /pre/) { + die "You should not diff a release version against a pre-release version.\n"; + } + $diffdir = "$dest/old-previews"; + $skipping2 = ' ** SKIPPING **'; +} elsif ($version =~ /pre/) { + $diffdir = $dest; + $skipping2 = ' ** SKIPPING **'; +} else { + $diffdir = "$dest/old-versions"; + $skipping2 = ''; +} + +print "\n", $break, < "; +$_ = ; +my $f_opt = /f/ ? ' -f' : ''; + +print $break; +system "./prepare-source && touch proto.h"; + +my @tweak_files = ( glob('packaging/*.spec'), glob('packaging/*/*.spec'), + glob('*.yo'), qw( configure.in configure ) ); +if ($version !~ /pre/) { + push(@tweak_files, qw( NEWS OLDNEWS )); +} +foreach my $fn (@tweak_files) { + open(IN, '<', $fn) or die $!; + undef $/; $_ = ; $/ = "\n"; + close IN; + if ($fn =~ /configure/) { + s/^RSYNC_VERSION.*/RSYNC_VERSION=$version/m; + } elsif ($fn =~ /\.spec/) { + s/^(Version:) .*/$1 $version/m; + s/^(Release:) .*/$1 $release/m; + } elsif ($fn =~ /\.yo/) { + s/^(manpage\([^)]+\)\(\d+\)\()[^)]+(\).*)/$1$today$2/m; + s/^(This man ?page is current for version) \S+ (of rsync)/$1 $version $2/m; + } else { + s/^(NEWS for rsync \Q$version\E) \(UNRELEASED\)\s*$/$1 ($today)\n/m; + s/^\t\S\S\s\S\S\S\s\d\d\d\d(\t\Q$version\E)/\t$ztoday$1/m; + } + open(OUT, '>', $fn) or die $!; + print OUT $_; + close OUT; +} + +system "yodl2man -o rsync.1 rsync.yo"; +system "yodl2man -o rsyncd.conf.5 rsyncd.conf.yo"; +#system "perl -pi -e \"s/\\\\\\'/\\\\&'/g\" rsync.1 rsyncd.conf.5"; + +mkdir('patches/tmp') or die $!; +system "rsync -a --exclude=patches/ --exclude-from=.cvsignore . patches/tmp/cvsdir/"; + +print "\n", $break, $note, $break; +system "patches/verify-patches -n -an$f_opt"; + +print $break; +system "cvs -q diff | egrep -v '^(===============|RCS file: |retrieving revision |Index: )' | less -p '^diff .*'"; + +print $break, < "; +$_ = ; + +if ($live) { + system "cvs commit -m 'Preparing for release of $version'"; + system "cvs tag -F $cvstag ."; +} + +if (!/skip/i) { + print "\n", $break, $note, $break; + system "patches/verify-patches -pun -an"; +} + +my $tar_file = "$dest/rsync-$version.tar.gz"; +my $diff_file = "$dest/rsync-$lastversion-$version.diffs.gz"; + +print $break, < "; +$_ = ; + +chdir($releasedir) or die $!; + +print $break; +system "rm -rf rsync-$version"; +rename('rsync', "rsync-$version") or die $!; + +# When creating a pre-release after a normal release, there's nothing to move. +if ($diffdir ne $dest) { + chdir($dest) or die $!; + + print "Shuffling old files ...\n"; + + # We need to run this regardless of $lastversion's "pre"ness. + my @moved_files; + foreach my $fn (glob('rsync*pre*.tar.gz*'), glob('rsync*pre*-NEWS')) { + my $new_fn = "old-previews/$fn"; + rename($fn, $new_fn) or die $!; + push(@moved_files, $new_fn); + } + + if ($version !~ /pre/) { + foreach my $fn (glob('rsync*.tar.gz*'), glob('rsync*-NEWS')) { + next if $fn =~ /^rsync.*pre/; + my $new_fn = "old-versions/$fn"; + rename($fn, $new_fn) or die $!; + push(@moved_files, $new_fn); + } + + foreach my $fn (glob('rsync*.diffs.gz*')) { + next if $fn =~ /^rsync.*pre/; + my $new_fn = "old-patches/$fn"; + rename($fn, $new_fn) or die $!; + push(@moved_files, $new_fn); + } + } + + # Optimize our future upload (in the absence of --detect-renamed) by + # hard-linking the above moved files on the remote server. + if ($live) { + my $remote_cmd = ''; + foreach (@moved_files) { + my($path, $fn) = m#(.*)/([^/]+)$#; + $remote_cmd .= "ln -f /home/ftp/pub/rsync/{$fn,$path};"; + } + system "ssh samba.org '$remote_cmd'"; + } + foreach (glob("rsync*pre*.diffs.gz*")) { + unlink($_); + } + + chdir($releasedir) or die $!; +} + +print "Creating $tar_file ...\n"; +system "fakeroot tar czf $tar_file rsync-$version"; +open(TAR, '|-', "fakeroot tar --files-from=- --no-recursion --mode=g+w -czf $tar_file rsync-$version") or die $!; +foreach (@files) { + print TAR "rsync-$version/$_\n"; +} +close TAR; + +print "Creating $diff_file ...\n"; +system "rm -rf rsync-$version rsync-$lastversion"; +system "tar xzf $tar_file; tar xzf $diffdir/rsync-$lastversion.tar.gz"; +## TWEAK THE VERSIONS AS DESIRED HERE ## +#mkdir("rsync-$lastversion/support", 0755) or die $!; +#rename("rsync-$lastversion/rsyncstats", "rsync-$lastversion/support/rsyncstats"); +unlink("rsync-$lastversion/.ignore"); +## END ## +system "diff -urN --exclude=patches rsync-$lastversion rsync-$version| gzip -9 >$diff_file"; + +print "Updating the other files in $dest ...\n"; +system "rsync -a rsync-$version/{README,NEWS,OLDNEWS,TODO} $dest"; +unlink("$dest/rsync-$version-NEWS"); +link("$dest/NEWS", "$dest/rsync-$version-NEWS"); +system "rsync -a $cvsroot/CVSROOT/rsync.updates $dest/cvs.log"; + +system "yodl2html -o $dest/rsync.html rsync-$version/rsync.yo"; +system "yodl2html -o $dest/rsyncd.conf.html rsync-$version/rsyncd.conf.yo"; + +system "rm -rf rsync-*"; + +if ($live) { + chdir($dest) or die $!; + system "gpg -ba rsync-$version.tar.gz"; + system "gpg -ba rsync-$lastversion-$version.diffs.gz"; +} + +print $break, < 0) set_blocking(STDOUT_FILENO); execvp(command[0], command); - rsyserr(FERROR, errno, "Failed to exec %s", - safe_fname(command[0])); + rsyserr(FERROR, errno, "Failed to exec %s", command[0]); exit_cleanup(RERR_IPC); } @@ -111,6 +111,9 @@ int to_child_pipe[2]; int from_child_pipe[2]; + /* The parent process is always the sender for a local rsync. */ + assert(am_sender); + if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) { rsyserr(FERROR, errno, "pipe"); @@ -124,11 +127,10 @@ } if (pid == 0) { - am_sender = !am_sender; + am_sender = 0; am_server = 1; - - if (!am_sender) - filesfrom_fd = -1; + filesfrom_fd = -1; + chmod_modes = NULL; /* Let the sending side handle this. */ if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 || close(to_child_pipe[1]) < 0 || @@ -144,9 +146,6 @@ child_main(argc, argv); } - if (!am_sender) - filesfrom_fd = -1; - if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) { rsyserr(FERROR, errno, "Failed to close"); diff -urN --exclude=patches rsync-2.6.6/prepare-source rsync-2.6.7/prepare-source --- rsync-2.6.6/prepare-source 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.7/prepare-source 2006-02-14 08:44:11.000000000 -0800 @@ -0,0 +1,13 @@ +#!/bin/sh +# Use autoconf, autoheader, yodl, etc. to ready the generated files +# in the release. This is typically used after applying a diff from +# "patches" directory in CVS. +# +# NOTE: if you use a diff from the "patches" directory in a release +# tar, this is not needed (but doesn't hurt anything). +dir=`dirname $0` +if test x"$dir" = x -o x"$dir" = x.; then + make -f prepare-source.mak +else + make -C "$dir" -f prepare-source.mak +fi diff -urN --exclude=patches rsync-2.6.6/prepare-source.mak rsync-2.6.7/prepare-source.mak --- rsync-2.6.6/prepare-source.mak 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.7/prepare-source.mak 2006-02-07 09:30:35.000000000 -0800 @@ -0,0 +1,23 @@ +gen: configure config.h.in proto.h man + +configure: configure.in aclocal.m4 + autoconf + +config.h.in: configure.in aclocal.m4 + autoheader && touch config.h.in + +proto.h: *.c lib/compat.c + cat *.c lib/compat.c | awk -f mkproto.awk >proto.h.new + if diff proto.h proto.h.new >/dev/null; then \ + rm proto.h.new; \ + else \ + mv proto.h.new proto.h; \ + fi + +man: rsync.1 rsyncd.conf.5 + +rsync.1: rsync.yo + yodl2man -o rsync.1 rsync.yo + +rsyncd.conf.5: rsyncd.conf.yo + yodl2man -o rsyncd.conf.5 rsyncd.conf.yo diff -urN --exclude=patches rsync-2.6.6/progress.c rsync-2.6.7/progress.c --- rsync-2.6.6/progress.c 2005-03-05 09:49:46.000000000 -0800 +++ rsync-2.6.7/progress.c 2006-01-14 09:10:52.000000000 -0800 @@ -97,15 +97,14 @@ remain_h = (int) (remain / 3600.0); if (is_last) { - snprintf(eol, sizeof eol, " (%d, %.1f%% of %d)\n", + snprintf(eol, sizeof eol, " (xfer#%d, to-check=%d/%d)\n", stats.num_transferred_files, - (float)((stats.current_file_index+1) * 100) - / stats.num_files, + stats.num_files - stats.current_file_index - 1, stats.num_files); } else strcpy(eol, "\r"); - rprintf(FINFO, "%12.0f %3d%% %7.2f%s %4d:%02d:%02d%s", - (double) ofs, pct, rate, units, + rprintf(FINFO, "%12s %3d%% %7.2f%s %4d:%02d:%02d%s", + human_num(ofs), pct, rate, units, remain_h, remain_m, remain_s, eol); } diff -urN --exclude=patches rsync-2.6.6/proto.h rsync-2.6.7/proto.h --- rsync-2.6.6/proto.h 2005-07-07 12:49:14.000000000 -0700 +++ rsync-2.6.7/proto.h 2006-03-11 10:25:04.000000000 -0800 @@ -1,7 +1,7 @@ /* This file is automatically generated with "make proto". DO NOT EDIT */ int allow_access(char *addr, char *host, char *allow_list, char *deny_list); -void base64_encode(char *buf, int len, char *out); +void base64_encode(char *buf, int len, char *out, int pad); char *auth_server(int f_in, int f_out, int module, char *host, char *addr, char *leader); void auth_client(int fd, char *user, char *challenge); @@ -10,14 +10,16 @@ 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, int32 len); void get_checksum2(char *buf, int32 len, char *sum); void file_checksum(char *fname,char *sum,OFF_T size); void sum_init(int seed); void sum_update(char *p, int32 len); void sum_end(char *sum); +struct chmod_mode_struct *parse_chmod(const char *modestr, + struct chmod_mode_struct **root_mode_ptr); +int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes); +int free_chmod_mode(struct chmod_mode_struct *chmod_modes); void close_all(void); void _exit_cleanup(int code, const char *file, int line); void cleanup_disable(void); @@ -68,22 +70,23 @@ void show_flist_stats(void); 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); struct file_struct *make_file(char *fname, struct file_list *flist, + STRUCT_STAT *stp, unsigned short flags, int filter_level); struct file_list *send_file_list(int f, int argc, char *argv[]); struct file_list *recv_file_list(int f); int flist_find(struct file_list *flist, struct file_struct *f); -void clear_file(int i, struct file_list *flist); +void clear_file(struct file_struct *file, struct file_list *flist); struct file_list *flist_new(int with_hlink, char *msg); void flist_free(struct file_list *flist); int f_name_cmp(struct file_struct *f1, struct file_struct *f2); -char *f_name_to(struct file_struct *f, char *fbuf); -char *f_name(struct file_struct *f); +char *f_name(struct file_struct *f, char *fbuf); struct file_list *get_dirlist(char *dirname, int dlen, int ignore_filter_rules); +int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st); void itemize(struct file_struct *file, int ndx, int statret, STRUCT_STAT *st, int32 iflags, uchar fnamecmp_type, char *xname); +int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st); void check_for_finished_hlinks(int itemizing, enum logcode code); void generate_files(int f_out, struct file_list *flist, char *local_name); void init_hard_links(void); @@ -99,8 +102,9 @@ void set_io_timeout(int secs); void set_msg_fd_in(int fd); void set_msg_fd_out(int fd); +void increment_active_files(int ndx, int itemizing, enum logcode code); +void decrement_active_files(int ndx); void send_msg(enum msgcode code, char *buf, int len); -int msg_list_push(int flush_it_all); int get_redo_num(int itemizing, enum logcode code); int get_hlink_num(void); void io_set_filesfrom_fds(int f_in, int f_out); @@ -137,42 +141,47 @@ void close_multiplexing_out(void); void start_write_batch(int fd); void stop_write_batch(void); -char *lp_motd_file(void); +char *lp_bind_address(void); char *lp_log_file(void); +char *lp_motd_file(void); char *lp_pid_file(void); char *lp_socket_options(void); -int lp_syslog_facility(void); int lp_rsync_port(void); -char *lp_bind_address(void); -char *lp_name(int ); +int lp_syslog_facility(void); +char *lp_auth_users(int ); char *lp_comment(int ); -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 ); -BOOL lp_ignore_errors(int ); -BOOL lp_ignore_nonreadable(int ); -char *lp_uid(int ); +char *lp_dont_compress(int ); +char *lp_exclude(int ); +char *lp_exclude_from(int ); +char *lp_filter(int ); char *lp_gid(int ); char *lp_hosts_allow(int ); char *lp_hosts_deny(int ); -char *lp_auth_users(int ); -char *lp_secrets_file(int ); -BOOL lp_strict_modes(int ); -char *lp_filter(int ); -char *lp_exclude(int ); -char *lp_exclude_from(int ); char *lp_include(int ); char *lp_include_from(int ); +char *lp_incoming_chmod(int ); +char *lp_lock_file(int ); char *lp_log_format(int ); +char *lp_name(int ); +char *lp_outgoing_chmod(int ); +char *lp_path(int ); +char *lp_postxfer_exec(int ); +char *lp_prexfer_exec(int ); char *lp_refuse_options(int ); -char *lp_dont_compress(int ); -int lp_timeout(int ); +char *lp_secrets_file(int ); +char *lp_temp_dir(int ); +char *lp_uid(int ); int lp_max_connections(int ); int lp_max_verbosity(int ); +int lp_timeout(int ); +BOOL lp_ignore_errors(int ); +BOOL lp_ignore_nonreadable(int ); +BOOL lp_list(int ); +BOOL lp_read_only(int ); +BOOL lp_strict_modes(int ); +BOOL lp_transfer_logging(int ); +BOOL lp_use_chroot(int ); +BOOL lp_write_only(int ); BOOL lp_load(char *pszFname, int globals_only); int lp_numservices(void); int lp_number(char *name); @@ -190,10 +199,11 @@ char *buf); void log_delete(char *fname, int mode); void log_exit(int code, const char *file, int line); -void wait_process(pid_t pid, int *status); +pid_t wait_process(pid_t pid, int *status_ptr, int flags); int child_main(int argc, char *argv[]); void start_server(int f_in, int f_out, int argc, char *argv[]); int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]); +RETSIGTYPE remember_children(UNUSED(int val)); const char *get_panic_action(void); int main(int argc,char *argv[]); void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len); @@ -212,12 +222,15 @@ void end_progress(OFF_T size); void show_progress(OFF_T ofs, OFF_T size); int recv_files(int f_in, struct file_list *flist, char *local_name); +void setup_iconv(); void free_sums(struct sum_struct *s); -int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st, - int flags); -void sig_int(void); -void finish_transfer(char *fname, char *fnametmp, struct file_struct *file, - int ok_to_set_time, int overwriting_basis); +mode_t dest_mode(mode_t flist_mode, mode_t cur_mode, int exists); +int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st, + int flags); +RETSIGTYPE sig_int(UNUSED(int val)); +void finish_transfer(char *fname, char *fnametmp, char *partialptr, + struct file_struct *file, int ok_to_set_time, + int overwriting_basis); const char *who_am_i(void); void successful_send(int ndx); int read_item_attrs(int f_in, int f_out, int ndx, uchar *type_ptr, @@ -234,15 +247,15 @@ void set_socket_options(int fd, char *options); void become_daemon(void); int sock_exec(const char *prog); -int do_unlink(char *fname); -int do_symlink(char *fname1, char *fname2); -int do_link(char *fname1, char *fname2); +int do_unlink(const char *fname); +int do_symlink(const char *fname1, const char *fname2); +int do_link(const char *fname1, const char *fname2); int do_lchown(const char *path, uid_t owner, gid_t group); int do_mknod(char *pathname, mode_t mode, dev_t dev); -int do_rmdir(char *pathname); -int do_open(char *pathname, int flags, mode_t mode); +int do_rmdir(const char *pathname); +int do_open(const char *pathname, int flags, mode_t mode); int do_chmod(const char *path, mode_t mode); -int do_rename(char *fname1, char *fname2); +int do_rename(const char *fname1, const char *fname2); void trim_trailing_slashes(char *name); int do_mkdir(char *fname, mode_t mode); int do_mkstemp(char *template, mode_t perms); @@ -266,12 +279,14 @@ void print_child_argv(char **cmd); void out_of_memory(char *str); void overflow_exit(char *str); -int set_modtime(char *fname, time_t modtime); -int create_directory_path(char *fname, int base_umask); +int set_modtime(char *fname, time_t modtime, mode_t mode); +int mkdir_defmode(char *fname); +int create_directory_path(char *fname); 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); +int copy_file(const char *source, const char *dest, mode_t mode); +int robust_unlink(const char *fname); +int robust_rename(char *from, char *to, char *partialptr, + int mode); pid_t do_fork(void); void kill_all(int sig); int name_to_uid(char *name, uid_t *uid); @@ -286,18 +301,23 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth); int push_dir(char *dir); int pop_dir(char *dir); -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 *human_num(int64 num); +char *human_dnum(double dnum, int decimal_digits); char *timestring(time_t t); int msleep(int t); -int cmp_modtime(time_t file1, time_t file2); +int cmp_time(time_t file1, time_t file2); int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6); void *_new_array(unsigned int size, unsigned long num); void *_realloc_array(void *ptr, unsigned int size, unsigned long num); const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr); uint32 fuzzy_distance(const char *s1, int len1, const char *s2, int len2); +struct bitbag *bitbag_create(int max_ndx); +void bitbag_set_bit(struct bitbag *bb, int ndx); +void bitbag_clear_bit(struct bitbag *bb, int ndx); +int bitbag_check_bit(struct bitbag *bb, int ndx); +int bitbag_next_bit(struct bitbag *bb, int after); int sys_gettimeofday(struct timeval *tv); diff -urN --exclude=patches rsync-2.6.6/receiver.c rsync-2.6.7/receiver.c --- rsync-2.6.6/receiver.c 2005-04-13 18:42:13.000000000 -0700 +++ rsync-2.6.7/receiver.c 2006-02-24 08:43:44.000000000 -0800 @@ -34,17 +34,14 @@ extern int batch_gen_fd; extern int protocol_version; extern int relative_paths; -extern int keep_dirlinks; extern int preserve_hard_links; extern int preserve_perms; -extern int io_error; extern int basis_dir_cnt; extern int make_backups; extern int cleanup_got_literal; extern int remove_sent_files; -extern int module_id; -extern int ignore_errors; -extern int orig_umask; +extern int append_mode; +extern int sparse_files; extern int keep_partial; extern int checksum_seed; extern int inplace; @@ -57,70 +54,9 @@ extern struct file_list *the_file_list; extern struct filter_list_struct server_filter_list; -#define SLOT_SIZE (16*1024) /* Desired size in bytes */ -#define PER_SLOT_BITS (SLOT_SIZE * 8) /* Number of bits per slot */ -#define PER_SLOT_INTS (SLOT_SIZE / 4) /* Number of int32s per slot */ - -static uint32 **delayed_bits = NULL; -static int delayed_slot_cnt = 0; +static struct bitbag *delayed_bits = NULL; static int phase = 0; -static void init_delayed_bits(int max_ndx) -{ - delayed_slot_cnt = (max_ndx + PER_SLOT_BITS - 1) / PER_SLOT_BITS; - - if (!(delayed_bits = (uint32**)calloc(delayed_slot_cnt, sizeof (uint32*)))) - out_of_memory("set_delayed_bit"); -} - -static void set_delayed_bit(int ndx) -{ - int slot = ndx / PER_SLOT_BITS; - ndx %= PER_SLOT_BITS; - - if (!delayed_bits[slot]) { - if (!(delayed_bits[slot] = (uint32*)calloc(PER_SLOT_INTS, 4))) - out_of_memory("set_delayed_bit"); - } - - delayed_bits[slot][ndx/32] |= 1u << (ndx % 32); -} - -/* Call this with -1 to start checking from 0. Returns -1 at the end. */ -static int next_delayed_bit(int after) -{ - uint32 bits, mask; - int i, ndx = after + 1; - int slot = ndx / PER_SLOT_BITS; - ndx %= PER_SLOT_BITS; - - mask = (1u << (ndx % 32)) - 1; - for (i = ndx / 32; slot < delayed_slot_cnt; slot++, i = mask = 0) { - if (!delayed_bits[slot]) - continue; - for ( ; i < PER_SLOT_INTS; i++, mask = 0) { - if (!(bits = delayed_bits[slot][i] & ~mask)) - continue; - /* The xor magic figures out the lowest enabled bit in - * bits, and the switch quickly computes log2(bit). */ - switch (bits ^ (bits & (bits-1))) { -#define LOG2(n) case 1u << n: return slot*PER_SLOT_BITS + i*32 + n - LOG2(0); LOG2(1); LOG2(2); LOG2(3); - LOG2(4); LOG2(5); LOG2(6); LOG2(7); - LOG2(8); LOG2(9); LOG2(10); LOG2(11); - LOG2(12); LOG2(13); LOG2(14); LOG2(15); - LOG2(16); LOG2(17); LOG2(18); LOG2(19); - LOG2(20); LOG2(21); LOG2(22); LOG2(23); - LOG2(24); LOG2(25); LOG2(26); LOG2(27); - LOG2(28); LOG2(29); LOG2(30); LOG2(31); - } - return -1; /* impossible... */ - } - } - - return -1; -} - /* * get_tmpname() - create a tmp filename for a given filename @@ -171,8 +107,7 @@ maxname = MIN(MAXPATHLEN - 7 - length, NAME_MAX - 8); if (maxname < 1) { - rprintf(FERROR, "temporary filename too long: %s\n", - safe_fname(fname)); + rprintf(FERROR, "temporary filename too long: %s\n", fname); fnametmp[0] = '\0'; return 0; } @@ -205,13 +140,39 @@ mapbuf = map_file(fd_r, size_r, read_size, sum.blength); if (verbose > 2) { rprintf(FINFO, "recv mapped %s of size %.0f\n", - safe_fname(fname_r), (double)size_r); + fname_r, (double)size_r); } } else mapbuf = NULL; sum_init(checksum_seed); + if (append_mode) { + OFF_T j; + sum.flength = (OFF_T)sum.count * sum.blength; + if (sum.remainder) + sum.flength -= sum.blength - sum.remainder; + for (j = CHUNK_SIZE; j < sum.flength; j += CHUNK_SIZE) { + if (do_progress) + show_progress(offset, total_size); + sum_update(map_ptr(mapbuf, offset, CHUNK_SIZE), + CHUNK_SIZE); + offset = j; + } + if (offset < sum.flength) { + int32 len = sum.flength - offset; + if (do_progress) + show_progress(offset, total_size); + sum_update(map_ptr(mapbuf, offset, len), len); + offset = sum.flength; + } + if (fd != -1 && do_lseek(fd, offset, SEEK_SET) != offset) { + rsyserr(FERROR, errno, "lseek failed on %s", + full_fname(fname)); + exit_cleanup(RERR_FILEIO); + } + } + while ((i = recv_token(f_in, &data)) != 0) { if (do_progress) show_progress(offset, total_size); @@ -315,22 +276,22 @@ char *fname, *partialptr, numbuf[4]; int i; - for (i = -1; (i = next_delayed_bit(i)) >= 0; ) { + for (i = -1; (i = bitbag_next_bit(delayed_bits, i)) >= 0; ) { struct file_struct *file = flist->files[i]; - fname = local_name ? local_name : f_name(file); + fname = local_name ? local_name : f_name(file, NULL); if ((partialptr = partial_dir_fname(fname)) != NULL) { if (make_backups && !make_backup(fname)) continue; if (verbose > 2) { rprintf(FINFO, "renaming %s to %s\n", - safe_fname(partialptr), - safe_fname(fname)); + partialptr, fname); } + /* We don't use robust_rename() here because the + * partial-dir must be on the same drive. */ if (do_rename(partialptr, fname) < 0) { rsyserr(FERROR, errno, "rename failed for %s (from %s)", - full_fname(fname), - safe_fname(partialptr)); + full_fname(fname), partialptr); } else { if (remove_sent_files || (preserve_hard_links @@ -338,8 +299,7 @@ SIVAL(numbuf, 0, i); send_msg(MSG_SUCCESS,numbuf,4); } - handle_partial_dir(partialptr, - PDIR_DELETE); + handle_partial_dir(partialptr, PDIR_DELETE); } } } @@ -352,7 +312,7 @@ rprintf(FINFO, "(No batched update for%s \"%s\")\n", phase ? " resend of" : "", - safe_fname(f_name(the_file_list->files[next_gen_i]))); + f_name(the_file_list->files[next_gen_i], NULL)); } next_gen_i = read_int(batch_gen_fd); if (next_gen_i == -1) @@ -395,7 +355,7 @@ } if (delay_updates) - init_delayed_bits(flist->count); + delayed_bits = bitbag_create(flist->count); while (1) { cleanup_disable(); @@ -417,6 +377,10 @@ send_msg(MSG_DONE, "", 0); if (keep_partial && !partial_dir) make_backups = 0; /* prevents double backup */ + if (append_mode) { + append_mode = 0; + sparse_files = 0; + } continue; } @@ -426,10 +390,10 @@ continue; file = flist->files[i]; - fname = local_name ? local_name : f_name_to(file, fbuf); + fname = local_name ? local_name : f_name(file, fbuf); if (verbose > 2) - rprintf(FINFO, "recv_files(%s)\n", safe_fname(fname)); + rprintf(FINFO, "recv_files(%s)\n", fname); if (!(iflags & ITEM_TRANSFER)) { maybe_log_item(file, iflags, itemizing, xname); @@ -470,8 +434,9 @@ if (read_batch) { next_gen_i = get_next_gen_i(batch_gen_fd, next_gen_i, i); if (i < next_gen_i) { - rprintf(FINFO, "(Skipping batched update for \"%s\")\n", - safe_fname(fname)); + rprintf(FINFO, + "(Skipping batched update for \"%s\")\n", + fname); discard_receive_data(f_in, file->length); continue; } @@ -572,16 +537,16 @@ fd1 = -1; } - if (fd1 != -1 && !preserve_perms) { - /* if the file exists already and we aren't preserving - * permissions then act as though the remote end sent - * us the file permissions we already have */ - file->mode = st.st_mode; + /* If we're not preserving permissions, change the file-list's + * mode based on the local permissions and some heuristics. */ + if (!preserve_perms) { + int exists = fd1 != -1; + file->mode = dest_mode(file->mode, st.st_mode, exists); } /* We now check to see if we are writing file "inplace" */ if (inplace) { - fd2 = do_open(fname, O_WRONLY|O_CREAT, 0); + fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600); if (fd2 == -1) { rsyserr(FERROR, errno, "open %s failed", full_fname(fname)); @@ -610,7 +575,7 @@ * 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) { + && create_directory_path(fnametmp) == 0) { /* Get back to name with XXXXXX in it. */ get_tmpname(fnametmp, fname); fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS); @@ -624,7 +589,7 @@ continue; } - if (partialptr) + if (keep_partial) cleanup_set(fnametmp, partialptr, file, fd1, fd2); } @@ -632,7 +597,7 @@ if (log_before_transfer) log_item(file, &initial_stats, iflags, NULL); else if (!am_server && verbose && do_progress) - rprintf(FINFO, "%s\n", safe_fname(fname)); + rprintf(FINFO, "%s\n", fname); /* recv file data */ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, @@ -650,17 +615,20 @@ } if ((recv_ok && (!delay_updates || !partialptr)) || inplace) { - finish_transfer(fname, fnametmp, file, recv_ok, 1); - if (partialptr != fname && fnamecmp == partialptr) { + if (partialptr == fname || *partial_dir == '/') + partialptr = NULL; + finish_transfer(fname, fnametmp, partialptr, + file, recv_ok, 1); + if (fnamecmp == partialptr) { do_unlink(partialptr); handle_partial_dir(partialptr, PDIR_DELETE); } } else if (keep_partial && partialptr && handle_partial_dir(partialptr, PDIR_CREATE)) { - finish_transfer(partialptr, fnametmp, file, recv_ok, - !partial_dir); + finish_transfer(partialptr, fnametmp, NULL, + file, recv_ok, !partial_dir); if (delay_updates && recv_ok) { - set_delayed_bit(i); + bitbag_set_bit(delayed_bits, i); recv_ok = -1; } } else { @@ -695,8 +663,7 @@ } rprintf(msgtype, "%s: %s failed verification -- update %s%s.\n", - errstr, safe_fname(fname), - keptstr, redostr); + errstr, fname, keptstr, redostr); } if (!phase) { SIVAL(numbuf, 0, i); diff -urN --exclude=patches rsync-2.6.6/rsync.1 rsync-2.6.7/rsync.1 --- rsync-2.6.6/rsync.1 2005-07-28 12:31:08.000000000 -0700 +++ rsync-2.6.7/rsync.1 2006-03-11 10:25:07.000000000 -0800 @@ -1,21 +1,23 @@ -.TH "rsync" "1" "28 Jul 2005" "" "" +.TH "rsync" "1" "11 Mar 2006" "" "" .SH "NAME" rsync \- faster, flexible replacement for rcp .SH "SYNOPSIS" .PP +rsync [OPTION]\&.\&.\&. SRC [SRC]\&.\&.\&. DEST +.PP rsync [OPTION]\&.\&.\&. SRC [SRC]\&.\&.\&. [USER@]HOST:DEST .PP -rsync [OPTION]\&.\&.\&. [USER@]HOST:SRC [DEST] +rsync [OPTION]\&.\&.\&. SRC [SRC]\&.\&.\&. [USER@]HOST::DEST .PP -rsync [OPTION]\&.\&.\&. SRC [SRC]\&.\&.\&. DEST +rsync [OPTION]\&.\&.\&. SRC [SRC]\&.\&.\&. rsync://[USER@]HOST[:PORT]/DEST .PP -rsync [OPTION]\&.\&.\&. [USER@]HOST::SRC [DEST] +rsync [OPTION]\&.\&.\&. SRC .PP -rsync [OPTION]\&.\&.\&. SRC [SRC]\&.\&.\&. [USER@]HOST::DEST +rsync [OPTION]\&.\&.\&. [USER@]HOST:SRC [DEST] .PP -rsync [OPTION]\&.\&.\&. rsync://[USER@]HOST[:PORT]/SRC [DEST] +rsync [OPTION]\&.\&.\&. [USER@]HOST::SRC [DEST] .PP -rsync [OPTION]\&.\&.\&. SRC [SRC]\&.\&.\&. rsync://[USER@]HOST[:PORT]/DEST +rsync [OPTION]\&.\&.\&. rsync://[USER@]HOST[:PORT]/SRC [DEST] .PP .SH "DESCRIPTION" .PP @@ -40,7 +42,7 @@ .IP o can use any transparent remote shell, including ssh or rsh .IP o -does not require root privileges +does not require super-user privileges .IP o pipelining of file transfers to minimize latency costs .IP o @@ -62,8 +64,8 @@ "USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION" section for an exception to this latter rule)\&. .PP -As a special case, if a remote source is specified without a destination, -the remote files are listed in an output format similar to "ls -l"\&. +As a special case, if a single source arg is specified without a +destination, the files are listed in an output format similar to "ls -l"\&. .PP As expected, if neither the source or destination path specify a remote host, the copy occurs locally (see also the \fB--list-only\fP option)\&. @@ -81,9 +83,6 @@ You can also specify any remote shell you like, either by using the \fB-e\fP command line option, or by setting the RSYNC_RSH environment variable\&. .PP -One common substitute is to use ssh, which offers a high degree of -security\&. -.PP Note that rsync must be installed on both the source and destination machines\&. .PP @@ -150,12 +149,14 @@ destination don\&'t have a \&':\&' in the name\&. In this case it behaves like an improved copy command\&. .PP +Finally, you can list all the (listable) modules available from a +particular rsync daemon by leaving off the module name: +.PP .RS \f(CWrsync somehost\&.mydomain\&.com::\fP .RE .PP -This would list all the anonymous rsync modules available on the host -somehost\&.mydomain\&.com\&. (See the following section for more details\&.) +See the following section for more details\&. .PP .SH "ADVANCED USAGE" .PP @@ -208,7 +209,7 @@ you either use a double colon :: instead of a single colon to separate the hostname from the path, or you use an rsync:// URL\&. .IP o -the first word after the :: is a module name\&. +the first word of the "path" is actually a module name\&. .IP o the remote daemon may print a message of the day when you connect\&. @@ -218,6 +219,8 @@ .IP o if you specify no local destination then a listing of the specified files on the remote daemon is provided\&. +.IP o +you must not specify the \fB--rsh\fP (\fB-e\fP) option\&. .PP An example that copies all the files in a remote module named "src": .PP @@ -276,7 +279,8 @@ If you need to specify a different remote-shell user, keep in mind that the user@ prefix in front of the host is specifying the rsync-user value (for a module that requires user-based authentication)\&. This means that you must -give the \&'-l user\&' option to ssh when specifying the remote-shell: +give the \&'-l user\&' option to ssh when specifying the remote-shell, as in +this example that uses the short version of the \fB--rsh\fP option: .PP .nf @@ -353,37 +357,43 @@ -q, --quiet suppress non-error messages -c, --checksum skip based on checksum, not mod-time & size -a, --archive archive mode; same as -rlptgoD (no -H) + --no-OPTION turn off an implied OPTION (e\&.g\&. --no-D) -r, --recursive recurse into directories -R, --relative use relative path names - --no-relative turn off --relative - --no-implied-dirs don\&'t send implied dirs with -R + --no-implied-dirs don\&'t send implied dirs with --relative -b, --backup make backups (see --suffix & --backup-dir) --backup-dir=DIR make backups into hierarchy based in DIR --suffix=SUFFIX backup suffix (default ~ w/o --backup-dir) -u, --update skip files that are newer on the receiver --inplace update destination files in-place + --append append data onto shorter files -d, --dirs transfer directories without recursing -l, --links copy symlinks as symlinks -L, --copy-links transform symlink into referent file/dir --copy-unsafe-links only "unsafe" symlinks are transformed --safe-links ignore symlinks that point outside the tree - -H, --hard-links preserve hard links + -k, --copy-dirlinks transform symlink to dir into referent dir -K, --keep-dirlinks treat symlinked dir on receiver as dir + -H, --hard-links preserve hard links -p, --perms preserve permissions - -o, --owner preserve owner (root only) + -E, --executability preserve executability + --chmod=CHMOD change destination permissions + -o, --owner preserve owner (super-user only) -g, --group preserve group - -D, --devices preserve devices (root only) + --devices preserve device files (super-user only) + --specials preserve special files + -D same as --devices --specials -t, --times preserve times -O, --omit-dir-times omit directories when preserving times + --super receiver attempts super-user activities -S, --sparse handle sparse files efficiently -n, --dry-run show what would have been transferred -W, --whole-file copy files whole (without rsync algorithm) - --no-whole-file always use incremental rsync algorithm -x, --one-file-system don\&'t cross filesystem boundaries -B, --block-size=SIZE force a fixed checksum block-size -e, --rsh=COMMAND specify the remote shell to use --rsync-path=PROGRAM specify the rsync to run on remote machine - --existing only update files that already exist + --existing ignore non-existing files on receiving side --ignore-existing ignore files that already exist on receiver --remove-sent-files sent files/symlinks are removed from sender --del an alias for --delete-during @@ -396,9 +406,11 @@ --force force deletion of dirs even if not empty --max-delete=NUM don\&'t delete more than NUM files --max-size=SIZE don\&'t transfer any file larger than SIZE + --min-size=SIZE don\&'t transfer any file smaller than SIZE --partial keep partially transferred files --partial-dir=DIR put a partially transferred file into DIR --delay-updates put all updated files into place at end + -m, --prune-empty-dirs prune empty directory chains from file-list --numeric-ids don\&'t map uid/gid values by user/group name --timeout=TIME set I/O timeout in seconds -I, --ignore-times don\&'t skip files that match size and time @@ -410,6 +422,7 @@ --copy-dest=DIR \&.\&.\&. and include copies of unchanged files --link-dest=DIR hardlink to files in DIR when unchanged -z, --compress compress file data during the transfer + --compress-level=NUM explicitly set compression level -C, --cvs-exclude auto-ignore files in the same way CVS does -f, --filter=RULE add a file-filtering RULE -F same as --filter=\&'dir-merge /\&.rsync-filter\&' @@ -422,9 +435,11 @@ -0, --from0 all *from/filter files are delimited by 0s --address=ADDRESS bind address for outgoing socket to daemon --port=PORT specify double-colon alternate port number + --sockopts=OPTIONS specify custom TCP options --blocking-io use blocking I/O for the remote shell - --no-blocking-io turn off blocking I/O when it is default --stats give some file-transfer stats + -8, --8-bit-output leave high-bit chars unescaped in output + -h, --human-readable output numbers in a human-readable format --progress show progress during transfer -P same as --partial --progress -i, --itemize-changes output a change-summary for all updates @@ -440,7 +455,8 @@ -4, --ipv4 prefer IPv4 -6, --ipv6 prefer IPv6 --version print version number - -h, --help show this help screen +(-h) --help show this help (see below for -h comment) + .fi @@ -456,10 +472,12 @@ --config=FILE specify alternate rsyncd\&.conf file --no-detach do not detach from the parent --port=PORT listen on alternate port number + --sockopts=OPTIONS specify custom TCP options -v, --verbose increase verbosity -4, --ipv4 prefer IPv4 -6, --ipv6 prefer IPv6 - -h, --help show this help screen + -h, --help show this help (if used after --daemon) + .fi @@ -472,9 +490,11 @@ The \&'=\&' for options that take a parameter is optional; whitespace can be used instead\&. .PP -.IP "\fB-h, --help\fP" +.IP "\fB--help\fP" Print a short help page describing the options -available in rsync\&. +available in rsync and exit\&. For backward-compatibility with older +versions of rsync, the help will also be output if you use the \fB-h\fP +option without any other args\&. .IP .IP "\fB--version\fP" print the rsync version number and exit\&. @@ -526,22 +546,53 @@ (allowing times to differ by up to 1 second)\&. .IP .IP "\fB-c, --checksum\fP" -This forces the sender to checksum all files using -a 128-bit MD4 checksum before transfer\&. The checksum is then -explicitly checked on the receiver and any files of the same name -which already exist and have the same checksum and size on the -receiver are not transferred\&. This option can be quite slow\&. +This forces the sender to checksum \fIevery\fP +regular file using a 128-bit MD4 checksum\&. It does this during the initial +file-system scan as it builds the list of all available files\&. The receiver +then checksums its version of each file (if it exists and it has the same +size as its sender-side counterpart) in order to decide which files need to +be updated: files with either a changed size or a changed checksum are +selected for transfer\&. Since this whole-file checksumming of all files on +both sides of the connection occurs in addition to the automatic checksum +verifications that occur during a file\&'s transfer, this option can be quite +slow\&. +.IP +Note that rsync always verifies that each \fItransferred\fP file was correctly +reconstructed on the receiving side by checking its whole-file checksum, but +that automatic after-the-transfer verification has nothing to do with this +option\&'s before-the-transfer "Does this file need to be updated?" check\&. .IP .IP "\fB-a, --archive\fP" This is equivalent to \fB-rlptgoD\fP\&. It is a quick way of saying you want recursion and want to preserve almost -everything\&. The only exception to this is if \fB--files-from\fP was +everything (with -H being a notable omission)\&. +The only exception to the above equivalence is when \fB--files-from\fP is specified, in which case \fB-r\fP is not implied\&. .IP Note that \fB-a\fP \fBdoes not preserve hardlinks\fP, because finding multiply-linked files is expensive\&. You must separately specify \fB-H\fP\&. .IP +.IP "--no-OPTION" +You may turn off one or more implied options by prefixing +the option name with "no-"\&. Not all options may be prefixed with a "no-": +only options that are implied by other options (e\&.g\&. \fB--no-D\fP, +\fB--no-perms\fP) or have different defaults in various circumstances +(e\&.g\&. \fB--no-whole-file\fP, \fB--no-blocking-io\fP, \fB--no-dirs\fP)\&. You may +specify either the short or the long option name after the "no-" prefix +(e\&.g\&. \fB--no-R\fP is the same as \fB--no-relative\fP)\&. +.IP +For example: if you want to use \fB-a\fP (\fB--archive\fP) but don\&'t want +\fB-o\fP (\fB--owner\fP), instead of converting \fB-a\fP into \fB-rlptgD\fP, you +could specify \fB-a --no-o\fP (or \fB-a --no-owner\fP)\&. +.IP +The order of the options is important: if you specify \fB--no-r -a\fP, the +\fB-r\fP option would end up being turned on, the opposite of \fB-a --no-r\fP\&. +Note also that the side-effects of the \fB--files-from\fP option are NOT +positional, as it affects the default state of several options and slightly +changes the meaning of \fB-a\fP (see the \fB--files-from\fP option for more +details)\&. +.IP .IP "\fB-r, --recursive\fP" This tells rsync to copy directories recursively\&. See also \fB--dirs\fP (\fB-d\fP)\&. @@ -551,56 +602,96 @@ names specified on the command line are sent to the server rather than just the last parts of the filenames\&. This is particularly useful when you want to send several different directories at the same time\&. For -example, if you used the command +example, if you used this command: .IP .RS -\f(CW rsync /foo/bar/foo\&.c remote:/tmp/\fP +\f(CW rsync -av /foo/bar/baz\&.c remote:/tmp/\fP .RE .IP -then this would create a file called foo\&.c in /tmp/ on the remote +\&.\&.\&. this would create a file named baz\&.c in /tmp/ on the remote machine\&. If instead you used .IP .RS -\f(CW rsync -R /foo/bar/foo\&.c remote:/tmp/\fP +\f(CW rsync -avR /foo/bar/baz\&.c remote:/tmp/\fP .RE .IP -then a file called /tmp/foo/bar/foo\&.c would be created on the remote +then a file named /tmp/foo/bar/baz\&.c would be created on the remote machine -- the full path name is preserved\&. To limit the amount of -path information that is sent, do something like this: +path information that is sent, you have a couple options: (1) With +a modern rsync on the sending side (beginning with 2\&.6\&.7), you can +insert a dot and a slash into the source path, like this: .IP .RS -\f(CW cd /foo\fP -.br -\f(CW rsync -R bar/foo\&.c remote:/tmp/\fP -.br +\f(CW rsync -avR /foo/\&./bar/baz\&.c remote:/tmp/\fP +.RE +.IP +That would create /tmp/bar/baz\&.c on the remote machine\&. (Note that the +dot must be followed by a slash, so "/foo/\&." would not be abbreviated\&.) +(2) For older rsync versions, you would need to use a chdir to limit the +source path\&. For example, when pushing files: +.IP +.RS +\f(CW (cd /foo; rsync -avR bar/baz\&.c remote:/tmp/) \fP .RE .IP -That would create /tmp/bar/foo\&.c on the remote machine\&. +(Note that the parens put the two commands into a sub-shell, so that the +"cd" command doesn\&'t remain in effect for future commands\&.) +If you\&'re pulling files, use this idiom (which doesn\&'t work with an +rsync daemon): .IP -.IP "\fB--no-relative\fP" -Turn off the \fB--relative\fP option\&. This is only -needed if you want to use \fB--files-from\fP without its implied \fB--relative\fP -file processing\&. +.RS +\f(CW rsync -avR --rsync-path="cd /foo; rsync" \e \fP +.br +\f(CW remote:bar/baz\&.c /tmp/\fP +.RE .IP .IP "\fB--no-implied-dirs\fP" -When combined with the \fB--relative\fP option, the -implied directories in each path are not explicitly duplicated as part -of the transfer\&. This makes the transfer more optimal and also allows -the two sides to have non-matching symlinks in the implied part of the -path\&. For instance, if you transfer the file "/path/foo/file" with \fB-R\fP, -the default is for rsync to ensure that "/path" and "/path/foo" on the -destination exactly match the directories/symlinks of the source\&. Using -the \fB--no-implied-dirs\fP option would omit both of these implied dirs, -which means that if "/path" was a real directory on one machine and a -symlink of the other machine, rsync would not try to change this\&. +This option affects the default behavior of the +\fB--relative\fP option\&. When it is specified, the attributes of the implied +directories from the source names are not included in the transfer\&. This +means that the corresponding path elements on the destination system are +left unchanged if they exist, and any missing implied directories are +created with default attributes\&. This even allows these implied path +elements to have big differences, such as being a symlink to a directory on +one side of the transfer, and a real directory on the other side\&. +.IP +For instance, if a command-line arg or a files-from entry told rsync to +transfer the file "path/foo/file", the directories "path" and "path/foo" +are implied when \fB--relative\fP is used\&. If "path/foo" is a symlink to +"bar" on the destination system, the receiving rsync would ordinarily +delete "path/foo", recreate it as a directory, and receive the file into +the new directory\&. With \fB--no-implied-dirs\fP, the receiving rsync updates +"path/foo/file" using the existing path elements, which means that the file +ends up being created in "path/bar"\&. Another way to accomplish this link +preservation is to use the \fB--keep-dirlinks\fP option (which will also +affect symlinks to directories in the rest of the transfer)\&. +.IP +In a similar but opposite scenario, if the transfer of "path/foo/file" is +requested and "path/foo" is a symlink on the sending side, running without +\fB--no-implied-dirs\fP would cause rsync to transform "path/foo" on the +receiving side into an identical symlink, and then attempt to transfer +"path/foo/file", which might fail if the duplicated symlink did not point +to a directory on the receiving side\&. Another way to avoid this sending of +a symlink as an implied directory is to use \fB--copy-unsafe-links\fP, or +\fB--copy-dirlinks\fP (both of which also affect symlinks in the rest of the +transfer -- see their descriptions for full details)\&. .IP .IP "\fB-b, --backup\fP" With this option, preexisting destination files are renamed as each file is transferred or deleted\&. You can control where the backup file goes and what (if any) suffix gets appended using the \fB--backup-dir\fP and \fB--suffix\fP options\&. -Note that if you don\&'t specify \fB--backup-dir\fP, the \fB--omit-dir-times\fP -option will be enabled\&. +.IP +Note that if you don\&'t specify \fB--backup-dir\fP, (1) the +\fB--omit-dir-times\fP option will be implied, and (2) if \fB--delete\fP is +also in effect (without \fB--delete-excluded\fP), rsync will add a "protect" +filter-rule for the backup suffix to the end of all your existing excludes +(e\&.g\&. -f "P *~")\&. This will prevent previously backed-up files from being +deleted\&. Note that if you are supplying your own filter rules, you may +need to manually insert your own exclude/protect rule 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 inclusion/exclusion of \&'*\&', the auto-added +rule would never be reached)\&. .IP .IP "\fB--backup-dir=DIR\fP" In combination with the \fB--backup\fP option, this @@ -653,20 +744,33 @@ rsync will be unable to update a file in-place that is not writable by the receiving user\&. .IP +.IP "\fB--append\fP" +This causes rsync to update a file by appending data onto +the end of the file, which presumes that the data that already exists on +the receiving side is identical with the start of the file on the sending +side\&. If that is not true, the file will fail the checksum test, and the +resend will do a normal \fB--inplace\fP update to correct the mismatched data\&. +Only files on the receiving side that are shorter than the corresponding +file on the sending side (as well as new files) are sent\&. +Implies \fB--inplace\fP, but does not conflict with \fB--sparse\fP (though the +\fB--sparse\fP option will be auto-disabled if a resend of the already-existing +data is required)\&. +.IP .IP "\fB-d, --dirs\fP" Tell the sending side to include any directories that are encountered\&. Unlike \fB--recursive\fP, a directory\&'s contents are not copied -unless the directory was specified on the command-line as either "\&." or a -name with a trailing slash (e\&.g\&. "foo/")\&. Without this option or the +unless the directory name specified is "\&." or ends with a trailing slash +(e\&.g\&. "\&.", "dir/\&.", "dir/", etc\&.)\&. Without this option or the \fB--recursive\fP option, rsync will skip all directories it encounters (and -output a message to that effect for each one)\&. +output a message to that effect for each one)\&. If you specify both +\fB--dirs\fP and \fB--recursive\fP, \fB--recursive\fP takes precedence\&. .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 +When symlinks are encountered, the item that 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 @@ -679,7 +783,8 @@ This tells rsync to copy the referent of symbolic links that point outside the copied tree\&. Absolute symlinks are also treated like ordinary files, and so are any symlinks in the -source path itself when \fB--relative\fP is used\&. +source path itself when \fB--relative\fP is used\&. This option has no +additional effect if \fB--copy-links\fP was also specified\&. .IP .IP "\fB--safe-links\fP" This tells rsync to ignore any symbolic links @@ -687,62 +792,169 @@ also ignored\&. Using this option in conjunction with \fB--relative\fP may give unexpected results\&. .IP +.IP "\fB-K, --copy-dirlinks\fP" +This option causes the sending side to treat +a symlink to a directory as though it were a real directory\&. This is +useful if you don\&'t want symlinks to non-directories to be affected, as +they would be using \fB--copy-links\fP\&. +.IP +Without this option, if the sending side has replaced a directory with a +symlink to a directory, the receiving side will delete anything that is in +the way of the new symlink, including a directory hierarchy (as long as +\fB--force\fP or \fB--delete\fP is in effect)\&. +.IP +See also \fB--keep-dirlinks\fP for an analogous option for the receiving +side\&. +.IP +.IP "\fB-K, --keep-dirlinks\fP" +This option causes the receiving side to treat +a symlink to a directory as though it were a real directory, but only if it +matches a real directory from the sender\&. Without this option, the +receiver\&'s symlink would be deleted and replaced with a real directory\&. +.IP +For example, suppose you transfer a directory "foo" that contains a file +"file", but "foo" is a symlink to directory "bar" on the receiver\&. Without +\fB--keep-dirlinks\fP, the receiver deletes symlink "foo", recreates it as a +directory, and receives the file into the new directory\&. With +\fB--keep-dirlinks\fP, the receiver keeps the symlink and "file" ends up in +"bar"\&. +.IP +See also \fB--copy-dirlinks\fP for an analogous option for the sending side\&. +.IP .IP "\fB-H, --hard-links\fP" -This tells rsync to recreate hard links on -the remote system to be the same as the local system\&. Without this -option hard links are treated like regular files\&. +This tells rsync to look for hard-linked files in +the transfer and link together the corresponding files on the receiving +side\&. Without this option, hard-linked files in the transfer are treated +as though they were separate files\&. .IP Note that rsync can only detect hard links if both parts of the link are in the list of files being sent\&. .IP -This option can be quite slow, so only use it if you need it\&. +.IP "\fB-p, --perms\fP" +This option causes the receiving rsync to set the +destination permissions to be the same as the source permissions\&. (See +also the \fB--chmod\fP option for a way to modify what rsync considers to +be the source permissions\&.) .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\&. +When this option is \fIoff\fP, permissions are set as follows: .IP -.IP "\fB-W, --whole-file\fP" -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 -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 destination are specified as local paths\&. +.RS +.IP o +Existing files (including updated files) retain their existing +permissions, though the \fB--executability\fP option might change just +the execute permission for the file\&. +.IP o +New files get their "normal" permission bits set to the source +file\&'s permissions masked with the receiving end\&'s umask setting, and +their special permission bits disabled except in the case where a new +directory inherits a setgid bit from its parent directory\&. +.RE .IP -.IP "\fB--no-whole-file\fP" -Turn off \fB--whole-file\fP, for use when it is the -default\&. +Thus, when \fB--perms\fP and \fB--executability\fP are both disabled, +rsync\&'s behavior is the same as that of other file-copy utilities, +such as \fBcp\fP(1) and \fBtar\fP(1)\&. .IP -.IP "\fB-p, --perms\fP" -This option causes rsync to set the destination -permissions to be the same as the source permissions\&. +In summary: to give destination files (both old and new) the source +permissions, use \fB--perms\fP\&. To give new files the destination-default +permissions (while leaving existing files unchanged), make sure that the +\fB--perms\fP option is off and use \fB--chmod=ugo=rwX\fP (which ensures that +all non-masked bits get enabled)\&. If you\&'d care to make this latter +behavior easier to type, you could define a popt alias for it, such as +putting this line in the file ~/\&.popt (this defines the \fB-s\fP option, +and includes --no-g to use the default group of the destination dir): +.IP +.RS +\f(CW rsync alias -s --no-p --no-g --chmod=ugo=rwX\fP +.RE +.IP +You could then use this new option in a command such as this one: +.IP +.RS +\f(CW rsync -asv src/ dest/\fP +.RE +.IP +(Caveat: make sure that \fB-a\fP does not follow \fB-s\fP, or it will re-enable +the "--no-*" options\&.) +.IP +The preservation of the destination\&'s setgid bit on newly-created +directories when \fB--perms\fP is off was added in rsync 2\&.6\&.7\&. Older rsync +versions erroneously preserved the three special permission bits for +newly-created files when \fB--perms\fP was off, while overriding the +destination\&'s setgid bit setting on a newly-created directory\&. (Keep in +mind that it is the version of the receiving rsync that affects this +behavior\&.) +.IP +.IP "\fB-E, --executability\fP" +This option causes rsync to preserve the +executability (or non-executability) of regular files when \fB--perms\fP is +not enabled\&. A regular file is considered to be executable if at least one +\&'x\&' is turned on in its permissions\&. When an existing destination file\&'s +executability differs from that of the corresponding source file, rsync +modifies the destination file\&'s permissions as follows: .IP -Without this option, all existing files (including updated files) retain -their existing permissions, while each new file gets its permissions set -based on the source file\&'s permissions, but masked by the receiving end\&'s -umask setting -(which is the same behavior as other file-copy utilities, such as cp)\&. +.RS +.IP o +To make a file non-executable, rsync turns off all its \&'x\&' +permissions\&. +.IP o +To make a file executable, rsync turns on each \&'x\&' permission that +has a corresponding \&'r\&' permission enabled\&. +.RE +.IP +If \fB--perms\fP is enabled, this option is ignored\&. +.IP +.IP "\fB--chmod\fP" +This option tells rsync to apply one or more +comma-separated "chmod" strings to the permission of the files in the +transfer\&. The resulting value is treated as though it was the permissions +that the sending side supplied for the file, which means that this option +can seem to have no effect on existing files if \fB--perms\fP is not enabled\&. +.IP +In addition to the normal parsing rules specified in the \fBchmod\fP(1) +manpage, you can specify an item that should only apply to a directory by +prefixing it with a \&'D\&', or specify an item that should only apply to a +file by prefixing it with a \&'F\&'\&. For example: +.IP +.RS +--chmod=Dg+s,ug+w,Fo-w,+X +.RE +.IP +It is also legal to specify multiple \fB--chmod\fP options, as each +additional option is just appended to the list of changes to make\&. +.IP +See the \fB--perms\fP and \fB--executability\fP options for how the resulting +permission value can be applied to the files in the transfer\&. .IP .IP "\fB-o, --owner\fP" This option causes rsync to set the owner of the -destination file to be the same as the source file\&. On most systems, -only the super-user can set file ownership\&. By default, the preservation -is done by name, but may fall back to using the ID number in some -circumstances\&. See the \fB--numeric-ids\fP option for a full discussion\&. +destination file to be the same as the source file\&. By default, the +preservation is done by name, but may fall back to using the ID number +in some circumstances (see the \fB--numeric-ids\fP option for a full +discussion)\&. +This option has no effect if the receiving rsync is not run as the +super-user and \fB--super\fP is not specified\&. .IP .IP "\fB-g, --group\fP" This option causes rsync to set the group of the destination file to be the same as the source file\&. If the receiving -program is not running as the super-user, only groups that the +program is not running as the super-user (or with the \fB--no-super\fP +option), only groups that the receiver is a member of will be preserved\&. By default, the preservation is done by name, but may fall back to using the ID number in some circumstances\&. See the \fB--numeric-ids\fP option for a full discussion\&. .IP -.IP "\fB-D, --devices\fP" +.IP "\fB--devices\fP" This option causes rsync to transfer character and -block device information to the remote system to recreate these -devices\&. This option is only available to the super-user\&. +block device files to the remote system to recreate these devices\&. +This option has no effect if the receiving rsync is not run as the +super-user and \fB--super\fP is not specified\&. +.IP +.IP "\fB--specials\fP" +This option causes rsync to transfer special files +such as named sockets and fifos\&. +.IP +.IP "\fB-D\fP" +The \fB-D\fP option is equivalent to \fB--devices\fP \fB--specials\fP\&. .IP .IP "\fB-t, --times\fP" This tells rsync to transfer modification times along @@ -759,30 +971,66 @@ the directories on the receiving side, it is a good idea to use \fB-O\fP\&. This option is inferred if you use \fB--backup\fP without \fB--backup-dir\fP\&. .IP -.IP "\fB-n, --dry-run\fP" -This tells rsync to not do any file transfers, -instead it will just report the actions it would have taken\&. +.IP "\fB--super\fP" +This tells the receiving side to attempt super-user +activities even if the receiving rsync wasn\&'t run by the super-user\&. These +activities include: preserving users via the \fB--owner\fP option, preserving +all groups (not just the current user\&'s groups) via the \fB--groups\fP +option, and copying devices via the \fB--devices\fP option\&. This is useful +for systems that allow such activities without being the super-user, and +also for ensuring that you will get errors if the receiving side isn\&'t +being running as the super-user\&. To turn off super-user activities, the +super-user can use \fB--no-super\fP\&. .IP .IP "\fB-S, --sparse\fP" Try to handle sparse files efficiently so they take -up less space on the destination\&. +up less space on the destination\&. Conflicts with \fB--inplace\fP because it\&'s +not possible to overwrite data in a sparse fashion\&. .IP NOTE: Don\&'t use this option when the destination is a Solaris "tmpfs" filesystem\&. It doesn\&'t seem to handle seeks over null regions correctly and ends up corrupting the files\&. .IP +.IP "\fB-n, --dry-run\fP" +This tells rsync to not do any file transfers, +instead it will just report the actions it would have taken\&. +.IP +.IP "\fB-W, --whole-file\fP" +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 +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 destination are specified as local paths\&. +.IP .IP "\fB-x, --one-file-system\fP" -This tells rsync not to cross filesystem -boundaries when recursing\&. This is useful for transferring the -contents of only one filesystem\&. -.IP -.IP "\fB--existing\fP" -This tells rsync not to create any new files -- -only update files that already exist on the destination\&. +This tells rsync to avoid crossing a +filesystem boundary when recursing\&. This does not limit the user\&'s ability +to specify items to copy from multiple filesystems, just rsync\&'s recursion +through the hierarchy of each directory that the user specified, and also +the analogous recursion on the receiving side during deletion\&. Also keep +in mind that rsync treats a "bind" mount to the same device as being on the +same filesystem\&. +.IP +If this option is repeated, rsync omits all mount-point directories from +the copy\&. Otherwise, it includes an empty directory at each mount-point it +encounters (using the attributes of the mounted directory because those of +the underlying mount-point directory are inaccessible)\&. +.IP +If rsync has been told to collapse symlinks (via \fB--copy-links\fP or +\fB--copy-unsafe-links\fP), a symlink to a directory on another device is +treated like a mount-point\&. Symlinks to non-directories are unaffected +by this option\&. +.IP +.IP "\fB--existing, --ignore-non-existing\fP" +This tells rsync to skip +updating files that do not exist yet on the destination\&. If this option is +combined with the \fB--ignore-existing\fP option, no files will be updated +(which can be useful if all you want to do is to delete missing files)\&. .IP .IP "\fB--ignore-existing\fP" -This tells rsync not to update files that already exist on -the destination\&. +This tells rsync to skip updating files that +already exist on the destination\&. See also \fB--ignore-non-existing\fP\&. .IP .IP "\fB--remove-sent-files\fP" This tells rsync to remove from the sending @@ -802,7 +1050,9 @@ option or mark the rules as only matching on the sending side (see the include/exclude modifiers in the FILTER RULES section)\&. .IP -This option has no effect unless directory recursion is enabled\&. +Prior to rsync 2\&.6\&.7, this option would have no effect unless \fB--recursive\fP +was in effect\&. Beginning with 2\&.6\&.7, deletions will also occur when \fB--dirs\fP +(\fB-d\fP) is in effect, but only for directories whose contents are being copied\&. .IP This option can be dangerous if used incorrectly! It is a very good idea to run first using the \fB--dry-run\fP option (\fB-n\fP) to see what files would be @@ -861,10 +1111,13 @@ even when there are I/O errors\&. .IP .IP "\fB--force\fP" -This options tells rsync to delete directories even if -they are not empty when they are to be replaced by non-directories\&. This -is only relevant without \fB--delete\fP because deletions are now done depth-first\&. -Requires the \fB--recursive\fP option (which is implied by \fB-a\fP) to have any effect\&. +This option tells rsync to delete a non-empty directory +when it is to be replaced by a non-directory\&. This is only relevant if +deletions are not active (see \fB--delete\fP for details)\&. +.IP +Note for older rsync versions: \fB--force\fP used to still be required when +using \fB--delete-after\fP, and it used to be non-functional unless the +\fB--recursive\fP option was also enabled\&. .IP .IP "\fB--max-delete=NUM\fP" This tells rsync not to delete more than NUM @@ -874,9 +1127,26 @@ .IP "\fB--max-size=SIZE\fP" This tells rsync to avoid transferring any file that is larger than the specified SIZE\&. The SIZE value can be -suffixed with a letter to indicate a size multiplier (K, M, or G) and +suffixed with a string to indicate a size multiplier, and may be a fractional value (e\&.g\&. "\fB--max-size=1\&.5m\fP")\&. .IP +The suffixes are as follows: "K" (or "KiB") is a kibibyte (1024), +"M" (or "MiB") is a mebibyte (1024*1024), and "G" (or "GiB") is a +gibibyte (1024*1024*1024)\&. +If you want the multiplier to be 1000 instead of 1024, use "KB", +"MB", or "GB"\&. (Note: lower-case is also accepted for all values\&.) +Finally, if the suffix ends in either "+1" or "-1", the value will +be offset by one byte in the indicated direction\&. +.IP +Examples: --max-size=1\&.5mb-1 is 1499999 bytes, and --max-size=2g+1 is +2147483649 bytes\&. +.IP +.IP "\fB--min-size=SIZE\fP" +This tells rsync to avoid transferring any +file that is smaller than the specified SIZE, which can help in not +transferring small, junk files\&. +See the \fB--max-size\fP option for a description of SIZE\&. +.IP .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 @@ -896,10 +1166,19 @@ RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION" above\&. .IP Command-line arguments are permitted in COMMAND provided that COMMAND is -presented to rsync as a single argument\&. For example: +presented to rsync as a single argument\&. You must use spaces (not tabs +or other whitespace) to separate the command and args from each other, +and you can use single- and/or double-quotes to preserve spaces in an +argument (but not backslashes)\&. Note that doubling a single-quote +inside a single-quoted string gives you a single-quote; likewise for +double-quotes (though you need to pay attention to which quotes your +shell is parsing and which quotes rsync is parsing)\&. Some examples: .IP .RS -\f(CW -e "ssh -p 2234"\fP +\f(CW -e \&'ssh -p 2234\&'\fP +.br +\f(CW -e \&'ssh -o "ProxyCommand nohup ssh firewall nc -w1 %h %p"\&'\fP +.br .RE .IP (Note that ssh users can alternately customize site-specific connect @@ -978,7 +1257,7 @@ your command\&. The first time it is used is a shorthand for this rule: .IP .RS -\f(CW --filter=\&': /\&.rsync-filter\&'\fP +\f(CW --filter=\&'dir-merge /\&.rsync-filter\&'\fP .RE .IP This tells rsync to look for per-directory \&.rsync-filter files that have @@ -987,7 +1266,7 @@ rule: .IP .RS -\f(CW --filter=\&'- \&.rsync-filter\&'\fP +\f(CW --filter=\&'exclude \&.rsync-filter\&'\fP .RE .IP This filters out the \&.rsync-filter files themselves from the transfer\&. @@ -1003,11 +1282,10 @@ See the FILTER RULES section for detailed information on this option\&. .IP .IP "\fB--exclude-from=FILE\fP" -This option is similar to the \fB--exclude\fP -option, but instead it adds all exclude patterns listed in the file -FILE to the exclude list\&. Blank lines in FILE and lines starting with -\&';\&' or \&'#\&' are ignored\&. -If \fIFILE\fP is \fB-\fP the list will be read from standard input\&. +This option is related to the \fB--exclude\fP +option, but it specifies a FILE that contains exclude patterns (one per line)\&. +Blank lines in the file and lines starting with \&';\&' or \&'#\&' are ignored\&. +If \fIFILE\fP is \fB-\fP, the list will be read from standard input\&. .IP .IP "\fB--include=PATTERN\fP" This option is a simplified form of the @@ -1017,13 +1295,14 @@ See the FILTER RULES section for detailed information on this option\&. .IP .IP "\fB--include-from=FILE\fP" -This specifies a list of include patterns -from a file\&. -If \fIFILE\fP is "-" the list will be read from standard input\&. +This option is related to the \fB--include\fP +option, but it specifies a FILE that contains include patterns (one per line)\&. +Blank lines in the file and lines starting with \&';\&' or \&'#\&' are ignored\&. +If \fIFILE\fP is \fB-\fP, 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 "-" +exact list of files to transfer (as read from the specified FILE or \fB-\fP for standard input)\&. It also tweaks the default behavior of rsync to make transferring just the specified files and directories easier: .IP @@ -1031,14 +1310,19 @@ .IP o The \fB--relative\fP (\fB-R\fP) option is implied, which preserves the path information that is specified for each item in the file (use -\fB--no-relative\fP if you want to turn that off)\&. +\fB--no-relative\fP or \fB--no-R\fP if you want to turn that off)\&. .IP o The \fB--dirs\fP (\fB-d\fP) option is implied, which will create directories specified in the list on the destination rather than noisily skipping -them\&. +them (use \fB--no-dirs\fP or \fB--no-d\fP if you want to turn that off)\&. .IP o The \fB--archive\fP (\fB-a\fP) option\&'s behavior does not imply \fB--recursive\fP (\fB-r\fP), so specify it explicitly, if you want it\&. +.IP o +These side-effects change the default state of rsync, so the position +of the \fB--files-from\fP option on the command-line has no bearing on how +other options are parsed (e\&.g\&. \fB-a\fP works the same before or after +\fB--files-from\fP, as does \fB--no-R\fP and all other options)\&. .RE .IP The file names that are read from the FILE are all relative to the @@ -1086,9 +1370,36 @@ .IP .IP "\fB-T, --temp-dir=DIR\fP" This option instructs rsync to use DIR as a -scratch directory when creating temporary copies of the files -transferred on the receiving side\&. The default behavior is to create -the temporary files in the receiving directory\&. +scratch directory when creating temporary copies of the files transferred +on the receiving side\&. The default behavior is to create each temporary +file in the same directory as the associated destination file\&. +.IP +This option is most often used when the receiving disk partition does not +have enough free space to hold a copy of the largest file in the transfer\&. +In this case (i\&.e\&. when the scratch directory in on a different disk +partition), rsync will not be able to rename each received temporary file +over the top of the associated destination file, but instead must copy it +into place\&. Rsync does this by copying the file over the top of the +destination file, which means that the destination file will contain +truncated data during this copy\&. If this were not done this way (even if +the destination file were first removed, the data locally copied to a +temporary file in the destination directory, and then renamed into place) +it would be possible for the old file to continue taking up disk space (if +someone had it open), and thus there might not be enough room to fit the +new version on the disk at the same time\&. +.IP +If you are using this option for reasons other than a shortage of disk +space, you may wish to combine it with the \fB--delay-updates\fP option, +which will ensure that all copied files get put into subdirectories in the +destination hierarchy, awaiting the end of the transfer\&. If you don\&'t +have enough room to duplicate all the arriving files on the destination +partition, another way to tell rsync that you aren\&'t overly concerned +about disk space is to use the \fB--partial-dir\fP option with a relative +path; because this tells rsync that it is OK to stash off a copy of a +single file in a subdir in the destination hierarchy, rsync will use the +partial-dir as a staging area to bring over the copied file, and then +rename it into place from there\&. (Specifying a \fB--partial-dir\fP with +an absolute path does not have this side-effect\&.) .IP .IP "\fB-y, --fuzzy\fP" This option tells rsync that it should look for a @@ -1160,20 +1471,25 @@ See also \fB--compare-dest\fP and \fB--copy-dest\fP\&. .IP Note that rsync versions prior to 2\&.6\&.1 had a bug that could prevent -\fB--link-dest\fP from working properly for a non-root user when \fB-o\fP was specified -(or implied by \fB-a\fP)\&. You can work-around this bug by avoiding the \fB-o\fP option -when sending to an old rsync\&. +\fB--link-dest\fP from working properly for a non-super-user when \fB-o\fP was +specified (or implied by \fB-a\fP)\&. You can work-around this bug by avoiding +the \fB-o\fP option when sending to an old rsync\&. .IP .IP "\fB-z, --compress\fP" With this option, rsync compresses the file data as it is sent to the destination machine, which reduces the amount of data being transmitted -- something that is useful over a slow connection\&. .IP -Note this this option typically achieves better compression ratios that can +Note that this option typically achieves better compression ratios than can be achieved by using a compressing remote shell or a compressing transport because it takes advantage of the implicit information in the matching data blocks that are not explicitly sent over the connection\&. .IP +.IP "\fB--compress-level=NUM\fP" +Explicitly set the compression level to use +(see \fB--compress\fP) instead of letting it default\&. If NUM is non-zero, +the \fB--compress\fP option is implied\&. +.IP .IP "\fB--numeric-ids\fP" With this option rsync will transfer numeric group and user IDs rather than using user and group names and mapping them @@ -1209,6 +1525,16 @@ syntax has a way to specify the port as a part of the URL)\&. See also this option in the \fB--daemon\fP mode section\&. .IP +.IP "\fB--sockopts\fP" +This option can provide endless fun for people +who like to tune their systems to the utmost degree\&. You can set all +sorts of socket options which may make transfers faster (or +slower!)\&. Read the man page for the setsockopt() system call for +details on some of the options you may be able to set\&. By default no +special socket options are set\&. This only affects direct socket +connections to a remote rsync daemon\&. This option also exists in the +\fB--daemon\fP mode section\&. +.IP .IP "\fB--blocking-io\fP" This tells rsync to use blocking I/O when launching a remote shell transport\&. If the remote shell is either rsh or remsh, @@ -1216,22 +1542,22 @@ blocking I/O, otherwise it defaults to using non-blocking I/O\&. (Note that ssh prefers non-blocking I/O\&.) .IP -.IP "\fB--no-blocking-io\fP" -Turn off \fB--blocking-io\fP, for use when it is the -default\&. -.IP .IP "\fB-i, --itemize-changes\fP" Requests a simple itemized list of the changes that are being made to each file, including attribute changes\&. This is exactly the same as specifying \fB--log-format=\&'%i %n%L\&'\fP\&. +If you repeat the option, unchanged files will also be output, but only +if the receiving rsync is at least version 2\&.6\&.7 (you can use \fB-vv\fP +with older versions of rsync, but that also turns on the output of other +verbose messages)\&. .IP The "%i" escape has a cryptic output that is 9 letters long\&. The general -format is like the string \fBUXcstpoga\fP), where \fBU\fP is replaced by the -kind of update being done, \fBX\fP is replaced by the file-type, and the +format is like the string \fBYXcstpogz\fP, where \fBY\fP is replaced by the +type of update being done, \fBX\fP is replaced by the file-type, and the other letters represent attributes that may be output if they are being modified\&. .IP -The update types that replace the \fBU\fP are as follows: +The update types that replace the \fBY\fP are as follows: .IP .RS .IP o @@ -1244,7 +1570,7 @@ A \fBc\fP means that a local change/creation is occurring for the item (such as the creation of a directory or the changing of a symlink, etc\&.)\&. .IP o -A \fBh\fP means that the item is a hard-link to another item (requires +A \fBh\fP means that the item is a hard link to another item (requires \fB--hard-links\fP)\&. .IP o A \fB\&.\fP means that the item is not being updated (though it might @@ -1252,7 +1578,8 @@ .RE .IP The file-types that replace the \fBX\fP are: \fBf\fP for a file, a \fBd\fP for a -directory, an \fBL\fP for a symlink, and a \fBD\fP for a device\&. +directory, an \fBL\fP for a symlink, a \fBD\fP for a device, and a \fBS\fP for a +special file (e\&.g\&. named sockets and fifos)\&. .IP The other letters in the string above are the actual letters that will be output if the associated attribute for the item is being updated or @@ -1281,13 +1608,12 @@ the sender\&'s value (requires \fB--perms\fP)\&. .IP o An \fBo\fP means the owner is different and is being updated to the -sender\&'s value (requires \fB--owner\fP and root privileges)\&. +sender\&'s value (requires \fB--owner\fP and super-user privileges)\&. .IP o A \fBg\fP means the group is different and is being updated to the sender\&'s value (requires \fB--group\fP and the authority to set the group)\&. .IP o -The \fBa\fP is reserved for a future enhanced version that supports -extended file attributes, such as ACLs\&. +The \fBz\fP slot is reserved for future use\&. .RE .IP One other output is possible: when deleting files, the "%i" will output @@ -1305,14 +1631,14 @@ .IP Specifying this option will mention each file, dir, etc\&. that gets updated in a significant way (a transferred file, a recreated symlink/device, or a -touched directory) unless the itemized-changes escape (%i) is included in +touched directory) unless the itemize-changes escape (%i) is included in the string, in which case the logging of names increases to mention any item that is changed in any way (as long as the receiving side is at least -2\&.6\&.4)\&. See the \fB--itemized-changes\fP option for a description of the +2\&.6\&.4)\&. See the \fB--itemize-changes\fP option for a description of the output of "%i"\&. .IP The \fB--verbose\fP option implies a format of "%n%L", but you can use -\fB--log-format\fP without bv(--verbose) if you like, or you can override +\fB--log-format\fP without \fB--verbose\fP if you like, or you can override the format of its per-file output using this option\&. .IP Rsync will output the log-format string prior to a file\&'s transfer unless @@ -1327,6 +1653,67 @@ on the file transfer, allowing you to tell how effective the rsync algorithm is for your data\&. .IP +The current statistics are as follows: +.IP o +\fBNumber of files\fP is the count of all "files" (in the generic +sense), which includes directories, symlinks, etc\&. +.IP o +\fBNumber of files transferred\fP is the count of normal files that +were updated via the rsync algorithm, which does not include created +dirs, symlinks, etc\&. +.IP o +\fBTotal file size\fP is the total sum of all file sizes in the transfer\&. +This does not count any size for directories or special files, but does +include the size of symlinks\&. +.IP o +\fBTotal transferred file size\fP is the total sum of all files sizes +for just the transferred files\&. +.IP o +\fBLiteral data\fP is how much unmatched file-update data we had to +send to the receiver for it to recreate the updated files\&. +.IP o +\fBMatched data\fP is how much data the receiver got locally when +recreating the updated files\&. +.IP o +\fBFile list size\fP is how big the file-list data was when the sender +sent it to the receiver\&. This is smaller than the in-memory size for the +file list due to some compressing of duplicated data when rsync sends the +list\&. +.IP o +\fBFile list generation time\fP is the number of seconds that the +sender spent creating the file list\&. This requires a modern rsync on the +sending side for this to be present\&. +.IP o +\fBFile list transfer time\fP is the number of seconds that the sender +spent sending the file list to the receiver\&. +.IP o +\fBTotal bytes sent\fP is the count of all the bytes that rsync sent +from the client side to the server side\&. +.IP o +\fBTotal bytes received\fP is the count of all non-message bytes that +rsync received by the client side from the server side\&. "Non-message" +bytes means that we don\&'t count the bytes for a verbose message that the +server sent to us, which makes the stats more consistent\&. +.IP +.IP "\fB-8, --8-bit-output\fP" +This tells rsync to leave all high-bit characters +unescaped in the output instead of trying to test them to see if they\&'re +valid in the current locale and escaping the invalid ones\&. All control +characters (but never tabs) are always escaped, regardless of this option\&'s +setting\&. +.IP +The escape idiom that started in 2\&.6\&.7 is to output a literal backslash (\e) +and a hash (#), followed by exactly 3 octal digits\&. For example, a newline +would output as "\e#012"\&. A literal backslash that is in a filename is not +escaped unless it is followed by a hash and 3 digits (0-9)\&. +.IP +.IP "\fB-h, --human-readable\fP" +Output numbers in a more human-readable format\&. +This makes big numbers output using larger units, with a K, M, or G suffix\&. If +this option was specified once, these units are K (1000), M (1000*1000), and +G (1000*1000*1000); if the option is repeated, the units are powers of 1024 +instead of 1000\&. +.IP .IP "\fB--partial\fP" By default, rsync will delete any partially transferred file if the transfer is interrupted\&. In some circumstances @@ -1339,8 +1726,9 @@ \fB--partial\fP option is to specify a \fIDIR\fP that will be used to hold the partial data (instead of writing it out to the destination file)\&. On the next transfer, rsync will use a file found in this -dir as data to speed up the resumption of the transfer and then deletes it +dir as data to speed up the resumption of the transfer and then delete it after it has served its purpose\&. +.IP Note that if \fB--whole-file\fP is specified (or implied), any partial-dir file that is found for a file that is being updated will simply be removed (since @@ -1352,30 +1740,36 @@ partial-directory in the destination file\&'s directory when needed, and then remove it again when the partial file is deleted\&. .IP -If the partial-dir value is not an absolute path, rsync will also add a directory -\fB--exclude\fP 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 \fB--partial-dir\fP option would add an "\fB--exclude=\&.rsync-partial/\fP" -rule at the end of any other filter rules\&. Note that if you are -supplying your own filter 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 \fB--exclude=\&'*\&'\fP rule, the auto-added rule would never be -reached)\&. +If the partial-dir value is not an absolute path, rsync will add an exclude +rule at the end of all your existing excludes\&. This will prevent the +sending of any partial-dir files that may exist on the sending side, and +will also prevent the untimely deletion of partial-dir items on the +receiving side\&. An example: the above \fB--partial-dir\fP option would add +the equivalent of "\fB--exclude=\&.rsync-partial/\fP" at the end of any other +filter rules\&. +.IP +If you are supplying your own exclude rules, you may need to add your own +exclude/hide/protect rule for the partial-dir because (1) the auto-added +rule may be ineffective at the end of your other rules, or (2) you may wish +to override rsync\&'s exclude choice\&. For instance, if you want to make +rsync clean-up any left-over partial-dirs that may be lying around, you +should specify \fB--delete-after\fP and add a "risk" filter rule, e\&.g\&. +\fB-f \&'R \&.rsync-partial/\&'\fP\&. (Avoid using \fB--delete-before\fP or +\fB--delete-during\fP unless you don\&'t need rsync to use any of the +left-over partial-dir data during the current run\&.) .IP IMPORTANT: the \fB--partial-dir\fP 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 \fB--partial\fP to be -enabled, but rather it effects where partial files go when \fB--partial\fP is +enabled, but rather it affects where partial files go when \fB--partial\fP is specified\&. For instance, instead of using \fB--partial-dir=\&.rsync-tmp\fP along with \fB--progress\fP, you could set RSYNC_PARTIAL_DIR=\&.rsync-tmp in your environment and then just use the \fB-P\fP option to turn on the use of the -\&.rsync-tmp dir for partial transfers\&. The only time that the \fB--partial\fP -option does not look for this environment value is (1) when \fB--inplace\fP was -specified (since \fB--inplace\fP conflicts with \fB--partial-dir\fP), or (2) when +\&.rsync-tmp dir for partial transfers\&. The only times that the \fB--partial\fP +option does not look for this environment value are (1) when \fB--inplace\fP was +specified (since \fB--inplace\fP conflicts with \fB--partial-dir\fP), and (2) when \fB--delay-updates\fP was specified (see below)\&. .IP For the purposes of the daemon-config\&'s "refuse options" setting, @@ -1390,24 +1784,63 @@ transfer, at which time all the files are renamed into place in rapid succession\&. This attempts to make the updating of the files a little more atomic\&. By default the files are placed into a directory named "\&.~tmp~" in -each file\&'s destination directory, but you can override this by specifying -the \fB--partial-dir\fP option\&. (Note that RSYNC_PARTIAL_DIR has no effect -on this value, nor is \fB--partial-dir\fP considered to be implied for the -purposes of the daemon-config\&'s "refuse options" setting\&.) -Conflicts with \fB--inplace\fP\&. +each file\&'s destination directory, but if you\&'ve specified the +\fB--partial-dir\fP option, that directory will be used instead\&. See the +comments in the \fB--partial-dir\fP section for a discussion of how this +"\&.~tmp~" dir will be excluded from the transfer, and what you can do if +you wnat rsync to cleanup old "\&.~tmp~" dirs that might be lying around\&. +Conflicts with \fB--inplace\fP and \fB--append\fP\&. .IP This option uses more memory on the receiving side (one bit per file transferred) and also requires enough free disk space on the receiving side to hold an additional copy of all the updated files\&. Note also that -you should not use an absolute path to \fB--partial-dir\fP unless there is no +you should not use an absolute path to \fB--partial-dir\fP unless (1) +there is no chance of any of the files in the transfer having the same name (since all the updated files will be put into a single directory if the path is -absolute)\&. +absolute) +and (2) there are no mount points in the hierarchy (since the +delayed updates will fail if they can\&'t be renamed into place)\&. .IP See also the "atomic-rsync" perl script in the "support" subdir for an update algorithm that is even more atomic (it uses \fB--link-dest\fP and a parallel hierarchy of files)\&. .IP +.IP "\fB-m, --prune-empty-dirs\fP" +This option tells the receiving rsync to get +rid of empty directories from the file-list, including nested directories +that have no non-directory children\&. This is useful for avoiding the +creation of a bunch of useless directories when the sending rsync is +recursively scanning a hierarchy of files using include/exclude/filter +rules\&. +.IP +Because the file-list is actually being pruned, this option also affects +what directories get deleted when a delete is active\&. However, keep in +mind that excluded files and directories can prevent existing items from +being deleted (because an exclude hides source files and protects +destination files)\&. +.IP +You can prevent the pruning of certain empty directories from the file-list +by using a global "protect" filter\&. For instance, this option would ensure +that the directory "emptydir" was kept in the file-list: +.IP +.RS +--filter \&'protect emptydir/\&' +.RE +.IP +Here\&'s an example that copies all \&.pdf files in a hierarchy, only creating +the necessary destination directories to hold the \&.pdf files, and ensures +that any superfluous files and directories in the destination are removed +(note the hide filter of non-directories being used instead of an exclude): +.IP +.RS +rsync -avm --del --include=\&'*\&.pdf\&' -f \&'hide,! */\&' src/ dest +.RE +.IP +If you didn\&'t want to remove superfluous destination files, the more +time-honored options of "--include=\&'*/\&' --exclude=\&'*\&'" would work fine +in place of the hide-filter (if that is more natural to you)\&. +.IP .IP "\fB--progress\fP" This option tells rsync to print information showing the progress of the transfer\&. This gives a bored user @@ -1460,13 +1893,24 @@ .IP .IP "\fB--list-only\fP" This option will cause the source files to be listed -instead of transferred\&. This option is inferred if there is no destination -specified, so you don\&'t usually need to use it explicitly\&. However, it can -come in handy for a user that wants to avoid the "\fB-r --exclude=\&'/*/*\&'\fP" -options that rsync might use as a compatibility kluge when generating a -non-recursive listing, or to list the files that are involved in a local -copy (since the destination path is not optional for a local copy, you -must specify this option explicitly and still include a destination)\&. +instead of transferred\&. This option is inferred if there is a single source +arg and no destination specified, so its main uses are: (1) to turn a copy +command that includes a +destination arg into a file-listing command, (2) to be able to specify more +than one local source arg (note: be sure to include the destination), or +(3) to avoid the automatically added "\fB-r --exclude=\&'/*/*\&'\fP" options that +rsync usually uses as a compatibility kluge when generating a non-recursive +listing\&. Caution: keep in mind that a source arg with a wild-card is expanded +by the shell into multiple args, so it is never safe to try to list such an arg +without using this option\&. For example: +.IP + +.nf + + rsync -av --list-only foo* dest/ +.fi + + .IP .IP "\fB--bwlimit=KBPS\fP" This option allows you to specify a maximum @@ -1503,7 +1947,7 @@ .IP "\fB--read-batch=FILE\fP" Apply all of the changes stored in FILE, a file previously generated by \fB--write-batch\fP\&. -If \fIFILE\fP is "-" the batch data will be read from standard input\&. +If \fIFILE\fP is \fB-\fP, the batch data will be read from standard input\&. See the "BATCH MODE" section for details\&. .IP .IP "\fB--protocol=NUM\fP" @@ -1566,7 +2010,7 @@ This specifies an alternate config file than the default\&. This is only relevant when \fB--daemon\fP is specified\&. The default is /etc/rsyncd\&.conf unless the daemon is running over -a remote shell program and the remote user is not root; in that case +a remote shell program and the remote user is not the super-user; in that case the default is rsyncd\&.conf in the current directory (typically $HOME)\&. .IP .IP "\fB--no-detach\fP" @@ -1584,6 +2028,10 @@ daemon to listen on rather than the default of 873\&. See also the "port" global option in the rsyncd\&.conf manpage\&. .IP +.IP "\fB--sockopts\fP" +This overrides the \fBsocket options\fP setting in the +rsyncd\&.conf file and has the same syntax\&. +.IP .IP "\fB-v, --verbose\fP" This option increases the amount of information the daemon logs during its startup phase\&. After the client connects, the @@ -1685,7 +2133,7 @@ particular spot in the hierarchy of files, otherwise it is matched against the end of the pathname\&. This is similar to a leading ^ in regular expressions\&. -Thus "/foo" would match a file called "foo" at either the "root of the +Thus "/foo" would match a file named "foo" at either the "root of the transfer" (for a global rule) or in the merge-file\&'s directory (for a per-directory rule)\&. An unqualified "foo" would match any file or directory named "foo" @@ -1700,21 +2148,35 @@ .IP o if the pattern ends with a / then it will only match a directory, not a file, link, or device\&. +.IP +.IP o +rsync chooses between doing a simple string match and wildcard +matching by checking if the pattern contains one of these three wildcard +characters: \&'*\&', \&'?\&', and \&'[\&' \&. .IP o -if the pattern contains a wildcard character from the set -*?[ then expression matching is applied using the shell filename -matching rules\&. Otherwise a simple string match is used\&. +a \&'*\&' matches any non-empty path component (it stops at slashes)\&. .IP o -the double asterisk pattern "**" will match slashes while a -single asterisk pattern "*" will stop at slashes\&. +use \&'**\&' to match anything, including slashes\&. .IP o -if the pattern contains a / (not counting a trailing /) or a "**" +a \&'?\&' matches any character except a slash (/)\&. +.IP o +a \&'[\&' introduces a character class, such as [a-z] or [[:alpha:]]\&. +.IP o +in a wildcard pattern, a backslash can be used to escape a wildcard +character, but it is matched literally when no wildcards are present\&. +.IP o +if the pattern contains a / (not counting a trailing /) or a "**", then it is matched against the full pathname, including any leading directories\&. If the pattern doesn\&'t contain a / or a "**", then it is matched only against the final component of the filename\&. (Remember that the algorithm is applied recursively so "full filename" can actually be any portion of a path from the starting directory on down\&.) +.IP o +a trailing "dir_name/***" will match both the directory (as if +"dir_name/" had been specified) and all the files in the directory +(as if "dir_name/**" had been specified)\&. (This behavior is new for +version 2\&.6\&.7\&.) .PP Note that, when using the \fB--recursive\fP (\fB-r\fP) option (which is implied by \fB-a\fP), every subcomponent of every path is visited from the top down, so @@ -1741,7 +2203,8 @@ rule, so rsync never visits any of the files in the "some" or "some/path" directories\&. One solution is to ask for all directories in the hierarchy to be included by using a single rule: "+ */" (put it somewhere before the -"- *" rule)\&. Another solution is to add specific include rules for all +"- *" rule), and perhaps use the \fB--prune-empty-dirs\fP option\&. Another +solution is to add specific include rules for all the parent dirs that need to be visited\&. For instance, this set of rules works fine: .PP @@ -1763,18 +2226,20 @@ .IP o "- *\&.o" would exclude all filenames matching *\&.o .IP o -"- /foo" would exclude a file called foo in the transfer-root directory +"- /foo" would exclude a file (or directory) named foo in the +transfer-root directory .IP o -"- foo/" would exclude any directory called foo +"- foo/" would exclude any directory named foo .IP o -"- /foo/*/bar" would exclude any file called bar two -levels below a directory called foo in the transfer-root directory +"- /foo/*/bar" would exclude any file named bar which is at two +levels below a directory named foo in the transfer-root directory .IP o -"- /foo/**/bar" would exclude any file called bar two -or more levels below a directory called foo in the transfer-root directory +"- /foo/**/bar" would exclude any file named bar two +or more levels below a directory named foo in the transfer-root directory .IP o The combination of "+ */", "+ *\&.c", and "- *" would include all -directories and C source files but nothing else\&. +directories and C source files but nothing else (see also the +\fB--prune-empty-dirs\fP option) .IP o The combination of "+ foo/", "+ foo/bar\&.c", and "- *" would include only the foo directory and foo/bar\&.c (the foo directory must be @@ -1839,7 +2304,7 @@ also disabled)\&. .IP o You may also specify any of the modifiers for the "+" or "-" rules -(below) in order to have the rules that are read-in from the file +(below) in order to have the rules that are read in from the file default to having that modifier set\&. For instance, "merge,-/ \&.excl" would treat the contents of \&.excl as absolute-path excludes, while "dir-merge,s \&.filt" and ":sC" would each make all their @@ -1848,10 +2313,12 @@ The following modifiers are accepted after a "+" or "-": .PP .IP o -A "/" specifies that the include/exclude should be treated as an -absolute path, relative to the root of the filesystem\&. For example, +A "/" specifies that the include/exclude rule should be matched +against the absolute pathname of the current item\&. For example, "-/ /etc/passwd" would exclude the passwd file any time the transfer -was sending files from the "/etc" directory\&. +was sending files from the "/etc" directory, and "-/ subdir/foo" +would always exclude "foo" when it is in a dir named "subdir", even +if "foo" is at the root of the current transfer\&. .IP o A "!" specifies that the include/exclude should take effect if the pattern fails to match\&. For instance, "-! */" would exclude all @@ -1907,7 +2374,7 @@ .PP This will merge the contents of the /home/user/\&.global-filter file at the start of the list and also turns the "\&.rules" filename into a per-directory -filter file\&. All rules read-in prior to the start of the directory scan +filter file\&. All rules read in prior to the start of the directory scan follow the global anchoring rules (i\&.e\&. a leading slash matches at the root of the transfer)\&. .PP @@ -2396,7 +2863,7 @@ .PP .SH "BUGS" .PP -times are transferred as unix time_t values +times are transferred as *nix time_t values .PP When transferring to FAT filesystems rsync may re-sync unmodified files\&. @@ -2412,7 +2879,7 @@ .PP .SH "VERSION" .PP -This man page is current for version 2\&.6\&.6 of rsync\&. +This man page is current for version 2\&.6\&.7 of rsync\&. .PP .SH "CREDITS" .PP diff -urN --exclude=patches rsync-2.6.6/rsync.c rsync-2.6.7/rsync.c --- rsync-2.6.6/rsync.c 2005-03-14 09:06:08.000000000 -0800 +++ rsync-2.6.7/rsync.c 2006-02-23 17:56:26.000000000 -0800 @@ -21,10 +21,20 @@ process */ #include "rsync.h" +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H +#include +#endif +#if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET +#include +#elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO +#include +#endif extern int verbose; extern int dry_run; extern int daemon_log_format_has_i; +extern int preserve_perms; +extern int preserve_executability; extern int preserve_times; extern int omit_dir_times; extern int am_root; @@ -32,13 +42,53 @@ extern int am_sender; extern int am_generator; extern int am_starting_up; +extern int allow_8bit_chars; extern int preserve_uid; extern int preserve_gid; extern int inplace; extern int keep_dirlinks; extern int make_backups; +extern mode_t orig_umask; extern struct stats stats; +extern struct chmod_mode_struct *daemon_chmod_modes; + +#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H +iconv_t ic_chck = (iconv_t)-1; + +static const char *default_charset(void) +{ +#if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET + return locale_charset(); +#elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO + return nl_langinfo(CODESET); +#else + return ""; /* Works with (at the very least) gnu iconv... */ +#endif +} + +void setup_iconv() +{ + if (!am_server && !allow_8bit_chars) { + const char *defset = default_charset(); + + /* It's OK if this fails... */ + ic_chck = iconv_open(defset, defset); + if (verbose > 3) { + if (ic_chck == (iconv_t)-1) { + rprintf(FINFO, + "note: iconv_open(\"%s\", \"%s\") failed (%d)" + " -- using isprint() instead of iconv().\n", + defset, defset, errno); + } else { + rprintf(FINFO, + "note: iconv_open(\"%s\", \"%s\") succeeded.\n", + defset, defset); + } + } + } +} +#endif /* free a sums struct @@ -49,9 +99,31 @@ free(s); } +/* This is only called when we aren't preserving permissions. Figure out what + * the permissions should be and return them merged back into the mode. */ +mode_t dest_mode(mode_t flist_mode, mode_t cur_mode, int exists) +{ + /* If the file already exists, we'll return the local permissions, + * possibly tweaked by the --executability option. */ + if (exists) { + if (preserve_executability && S_ISREG(flist_mode)) { + /* If the source file is executable, grant execute + * rights to everyone who can read, but ONLY if the + * file isn't already executable. */ + if (!(flist_mode & 0111)) + cur_mode &= ~0111; + else if (!(cur_mode & 0111)) + cur_mode |= (cur_mode & 0444) >> 2; + } + } else + cur_mode = flist_mode & ACCESSPERMS & ~orig_umask; + if (daemon_chmod_modes && !S_ISLNK(flist_mode)) + cur_mode = tweak_mode(cur_mode, daemon_chmod_modes); + return (flist_mode & ~CHMOD_BITS) | (cur_mode & CHMOD_BITS); +} -int set_perms(char *fname,struct file_struct *file,STRUCT_STAT *st, - int flags) +int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st, + int flags) { int updated = 0; STRUCT_STAT st2; @@ -66,19 +138,26 @@ return 0; } st = &st2; + if (!preserve_perms && S_ISDIR(file->mode) + && st->st_mode & S_ISGID) { + /* We just created this directory and its setgid + * bit is on, so make sure it stays on. */ + file->mode |= S_ISGID; + } } - if (!preserve_times || S_ISLNK(st->st_mode) - || (S_ISDIR(st->st_mode) && omit_dir_times)) - flags |= PERMS_SKIP_MTIME; - if (!(flags & PERMS_SKIP_MTIME) - && cmp_modtime(st->st_mtime, file->modtime) != 0) { - if (set_modtime(fname,file->modtime) != 0) { + if (!preserve_times || (S_ISDIR(st->st_mode) && omit_dir_times)) + flags |= ATTRS_SKIP_MTIME; + if (!(flags & ATTRS_SKIP_MTIME) + && cmp_time(st->st_mtime, file->modtime) != 0) { + int ret = set_modtime(fname, file->modtime, st->st_mode); + if (ret < 0) { rsyserr(FERROR, errno, "failed to set times on %s", full_fname(fname)); return 0; } - updated = 1; + if (ret == 0) /* ret == 1 if symlink could not be set */ + updated = 1; } change_uid = am_root && preserve_uid && st->st_uid != file->uid; @@ -94,13 +173,13 @@ if (change_uid) { rprintf(FINFO, "set uid of %s from %ld to %ld\n", - safe_fname(fname), + fname, (long)st->st_uid, (long)file->uid); } if (change_gid) { rprintf(FINFO, "set gid of %s from %ld to %ld\n", - safe_fname(fname), + fname, (long)st->st_gid, (long)file->gid); } } @@ -125,31 +204,31 @@ } #ifdef HAVE_CHMOD - if (!S_ISLNK(st->st_mode)) { - if ((st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) { - updated = 1; - if (do_chmod(fname,(file->mode & CHMOD_BITS)) != 0) { - rsyserr(FERROR, errno, "failed to set permissions on %s", - full_fname(fname)); - return 0; - } + if ((st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) { + int ret = do_chmod(fname, file->mode); + if (ret < 0) { + rsyserr(FERROR, errno, + "failed to set permissions on %s", + full_fname(fname)); + return 0; } + if (ret == 0) /* ret == 1 if symlink could not be set */ + updated = 1; } #endif - if (verbose > 1 && flags & PERMS_REPORT) { + if (verbose > 1 && flags & ATTRS_REPORT) { enum logcode code = daemon_log_format_has_i || dry_run ? FCLIENT : FINFO; if (updated) - rprintf(code, "%s\n", safe_fname(fname)); + rprintf(code, "%s\n", fname); else - rprintf(code, "%s is uptodate\n", safe_fname(fname)); + rprintf(code, "%s is uptodate\n", fname); } return updated; } - -void sig_int(void) +RETSIGTYPE sig_int(UNUSED(int val)) { /* KLUGE: if the user hits Ctrl-C while ssh is prompting * for a password, then our cleanup's sending of a SIGUSR1 @@ -163,36 +242,39 @@ exit_cleanup(RERR_SIGNAL); } - -/* 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, - int ok_to_set_time, int overwriting_basis) +/* Finish off a file transfer: renaming the file and setting the file's + * attributes (e.g. permissions, ownership, etc.). If partialptr is not + * NULL and the robust_rename() call is forced to copy the temp file, we + * stage the file into the partial-dir and then rename it into place. */ +void finish_transfer(char *fname, char *fnametmp, char *partialptr, + struct file_struct *file, int ok_to_set_time, + int overwriting_basis) { int ret; if (inplace) { if (verbose > 2) - rprintf(FINFO, "finishing %s\n", safe_fname(fname)); - goto do_set_perms; + rprintf(FINFO, "finishing %s\n", fname); + fnametmp = fname; + goto do_set_file_attrs; } if (make_backups && overwriting_basis && !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); + set_file_attrs(fnametmp, file, NULL, + ok_to_set_time ? 0 : ATTRS_SKIP_MTIME); /* move tmp file over real file */ - if (verbose > 2) { - rprintf(FINFO, "renaming %s to %s\n", - safe_fname(fnametmp), safe_fname(fname)); - } - ret = robust_rename(fnametmp, fname, file->mode & INITACCESSPERMS); + if (verbose > 2) + rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname); + ret = robust_rename(fnametmp, fname, partialptr, + file->mode & INITACCESSPERMS); if (ret < 0) { rsyserr(FERROR, errno, "%s %s -> \"%s\"", - ret == -2 ? "copy" : "rename", - full_fname(fnametmp), safe_fname(fname)); + ret == -2 ? "copy" : "rename", + full_fname(fnametmp), fname); do_unlink(fnametmp); return; } @@ -200,8 +282,21 @@ /* 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); + /* The file was copied, so tweak the perms of the copied file. If it + * was copied to partialptr, move it into its final destination. */ + fnametmp = partialptr ? partialptr : fname; + + do_set_file_attrs: + set_file_attrs(fnametmp, file, NULL, + ok_to_set_time ? 0 : ATTRS_SKIP_MTIME); + + if (partialptr) { + if (do_rename(fnametmp, fname) < 0) { + rsyserr(FERROR, errno, "rename %s -> \"%s\"", + full_fname(fnametmp), fname); + } else + handle_partial_dir(partialptr, PDIR_DELETE); + } } const char *who_am_i(void) diff -urN --exclude=patches rsync-2.6.6/rsync.h rsync-2.6.7/rsync.h --- rsync-2.6.6/rsync.h 2005-05-03 10:00:47.000000000 -0700 +++ rsync-2.6.7/rsync.h 2006-02-27 13:11:56.000000000 -0800 @@ -58,12 +58,12 @@ /* These flags are used in the live flist data. */ #define FLAG_TOP_DIR (1<<0) +#define FLAG_SENT (1<<1) /* sender */ #define FLAG_HLINK_EOL (1<<1) /* receiver/generator */ -#define FLAG_MOUNT_POINT (1<<2) /* sender */ -#define FLAG_NO_FUZZY (1<<2) /* generator */ +#define FLAG_MOUNT_POINT (1<<2) /* sender/generator */ #define FLAG_DEL_HERE (1<<3) /* receiver/generator */ -#define FLAG_SENT (1<<3) /* sender */ #define FLAG_HLINK_TOL (1<<4) /* receiver/generator */ +#define FLAG_NO_FUZZY (1<<5) /* generator */ /* update this if you make incompatible changes */ #define PROTOCOL_VERSION 29 @@ -115,9 +115,10 @@ #define XFLG_FATAL_ERRORS (1<<0) #define XFLG_OLD_PREFIXES (1<<1) #define XFLG_ANCHORED2ABS (1<<2) +#define XFLG_ABS_IF_SLASH (1<<3) -#define PERMS_REPORT (1<<0) -#define PERMS_SKIP_MTIME (1<<1) +#define ATTRS_REPORT (1<<0) +#define ATTRS_SKIP_MTIME (1<<1) #define FULL_FLUSH 1 #define NORMAL_FLUSH 0 @@ -133,11 +134,6 @@ #define FNAMECMP_BACKUP 0x82 #define FNAMECMP_FUZZY 0x83 -/* For calling delete_file() */ -#define DEL_NO_RECURSE (1<<1) -#define DEL_FORCE_RECURSE (1<<2) /* recurse even w/o --force */ -#define DEL_TERSE (1<<3) - /* For use by the itemize_changes code */ #define ITEM_REPORT_CHECKSUM (1<<1) #define ITEM_REPORT_SIZE (1<<2) @@ -152,7 +148,6 @@ #define ITEM_LOCAL_CHANGE (1<<14) #define ITEM_TRANSFER (1<<15) /* These are outside the range of the transmitted flags. */ -#define ITEM_NO_DEST_AND_NO_UPDATE (1<<16) /* used by itemize() */ #define ITEM_MISSING_DATA (1<<16) /* used by log_formatted() */ #define ITEM_DELETED (1<<17) /* used by log_formatted() */ @@ -160,16 +155,17 @@ ITEM_BASIS_TYPE_FOLLOWS | ITEM_XNAME_FOLLOWS | ITEM_LOCAL_CHANGE)) -/* Log-message categories. FLOG and FCLIENT are only used on the daemon - * side for custom logging -- they don't get sent over the socket. */ -enum logcode { FERROR=1, FINFO=2, FLOG=3, FCLIENT=4 }; +/* Log-message categories. Only FERROR and FINFO get sent over the socket. + * FLOG and FCLIENT are only used on the daemon side for custom logging, + * while FNAME is only used on the client side. */ +enum logcode { FERROR=1, FINFO=2, FLOG=3, FCLIENT=4, FNAME=5, FSOCKERR=6 }; /* 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, /* remote logging */ - MSG_LOG=FLOG, MSG_FCLIENT=FCLIENT, /* sibling logging */ + MSG_LOG=FLOG, MSG_SOCKERR=FSOCKERR, /* sibling logging */ MSG_REDO=9, /* reprocess indicated flist index */ MSG_SUCCESS=100,/* successfully updated indicated flist index */ MSG_DELETED=101,/* successfully deleted a file on receiving side */ @@ -319,6 +315,10 @@ #include #endif +#ifdef HAVE_LIMITS_H +# include +#endif + #include #include "lib/pool_alloc.h" @@ -463,6 +463,14 @@ #define MAXPATHLEN 1024 #endif +/* We want a roomy line buffer that can hold more than MAXPATHLEN, + * and significantly more than an overly short MAXPATHLEN. */ +#if MAXPATHLEN < 4096 +#define BIGPATHBUFLEN (4096+1024) +#else +#define BIGPATHBUFLEN (MAXPATHLEN+1024) +#endif + #ifndef NAME_MAX #define NAME_MAX 255 #endif @@ -552,6 +560,7 @@ OFF_T offset; /**< offset in file of this chunk */ int32 len; /**< length of chunk of file */ uint32 sum1; /**< simple checksum */ + int32 chain; /**< next hash-table collision */ short flags; /**< flag bits */ char sum2[SUM_LENGTH]; /**< checksum */ }; @@ -579,11 +588,11 @@ #define MATCHFLG_WILD (1<<0) /* pattern has '*', '[', and/or '?' */ #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 */ +#define MATCHFLG_WILD2_PREFIX (1<<2) /* pattern starts with "**" */ +#define MATCHFLG_WILD3_SUFFIX (1<<3) /* pattern ends with "***" */ +#define MATCHFLG_ABS_PATH (1<<4) /* path-match on absolute path */ +#define MATCHFLG_INCLUDE (1<<5) /* this is an include, not an exclude */ +#define MATCHFLG_DIRECTORY (1<<6) /* this matches only directories */ #define MATCHFLG_WORD_SPLIT (1<<7) /* split rules on whitespace */ #define MATCHFLG_NO_INHERIT (1<<8) /* don't inherit these rules */ #define MATCHFLG_NO_PREFIXES (1<<9) /* parse no prefixes from patterns */ @@ -595,6 +604,7 @@ #define MATCHFLG_CVS_IGNORE (1<<15)/* rule was -C or :C */ #define MATCHFLG_SENDER_SIDE (1<<16)/* rule applies to the sending side */ #define MATCHFLG_RECEIVER_SIDE (1<<17)/* rule applies to the receiving side */ +#define MATCHFLG_CLEAR_LIST (1<<18)/* this item is the "!" token */ #define MATCHFLGS_FROM_CONTAINER (MATCHFLG_ABS_PATH | MATCHFLG_INCLUDE \ | MATCHFLG_DIRECTORY | MATCHFLG_SENDER_SIDE \ @@ -631,6 +641,7 @@ int current_file_index; }; +struct chmod_mode_struct; #include "byteorder.h" #include "lib/mdfour.h" @@ -638,6 +649,15 @@ #include "lib/permstring.h" #include "lib/addrinfo.h" +#if !defined __GNUC__ || defined __APPLE__ +/* Apparently the OS X port of gcc gags on __attribute__. + * + * */ +#define __attribute__(x) +#endif + +#define UNUSED(x) x __attribute__((__unused__)) + #include "proto.h" /* We have replacement versions of these if they're missing. */ @@ -681,7 +701,12 @@ #define SUPPORT_HARD_LINKS 1 #endif -#define SIGNAL_CAST (RETSIGTYPE (*)()) +#ifdef HAVE_SIGACTION +#define SIGACTION(n,h) sigact.sa_handler=(h), sigaction((n),&sigact,NULL) +#define signal(n,h) we_need_to_call_SIGACTION_not_signal(n,h) +#else +#define SIGACTION(n,h) signal(n,h) +#endif #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN @@ -778,7 +803,8 @@ #define INADDR_NONE 0xffffffff #endif -#define IS_DEVICE(mode) (S_ISCHR(mode) || S_ISBLK(mode) || S_ISSOCK(mode) || S_ISFIFO(mode)) +#define IS_SPECIAL(mode) (S_ISSOCK(mode) || S_ISFIFO(mode)) +#define IS_DEVICE(mode) (S_ISCHR(mode) || S_ISBLK(mode)) /* Initial mask on permissions given to temporary files. Mask off setuid bits and group access because of potential race-condition security @@ -786,15 +812,7 @@ #define INITACCESSPERMS 0700 /* handler for null strings in printf format */ -#define NS(s) ((s)?safe_fname(s):"") - -#if !defined __GNUC__ || defined __APPLE__ -/* Apparently the OS X port of gcc gags on __attribute__. - * - * */ -#define __attribute__(x) - -#endif +#define NS(s) ((s)?(s):"") /* Convenient wrappers for malloc and realloc. Use them. */ #define new(type) ((type *)malloc(sizeof(type))) @@ -860,7 +878,3 @@ #ifdef MAINTAINER_MODE const char *get_panic_action(void); #endif - -#define UNUSED(x) x __attribute__((__unused__)) - -extern const char *io_write_phase, *io_read_phase; diff -urN --exclude=patches rsync-2.6.6/rsync.yo rsync-2.6.7/rsync.yo --- rsync-2.6.6/rsync.yo 2005-07-28 12:31:05.000000000 -0700 +++ rsync-2.6.7/rsync.yo 2006-03-11 10:25:04.000000000 -0800 @@ -1,21 +1,23 @@ mailto(rsync-bugs@samba.org) -manpage(rsync)(1)(28 Jul 2005)()() +manpage(rsync)(1)(11 Mar 2006)()() manpagename(rsync)(faster, flexible replacement for rcp) manpagesynopsis() +rsync [OPTION]... SRC [SRC]... DEST + rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST -rsync [OPTION]... [USER@]HOST:SRC [DEST] +rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST -rsync [OPTION]... SRC [SRC]... DEST +rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST -rsync [OPTION]... [USER@]HOST::SRC [DEST] +rsync [OPTION]... SRC -rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST +rsync [OPTION]... [USER@]HOST:SRC [DEST] -rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST] +rsync [OPTION]... [USER@]HOST::SRC [DEST] -rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST +rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST] manpagedescription() @@ -36,7 +38,7 @@ 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 - it() does not require root privileges + it() does not require super-user privileges it() pipelining of file transfers to minimize latency costs it() support for anonymous or authenticated rsync daemons (ideal for mirroring) @@ -57,8 +59,8 @@ "USING RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION" section for an exception to this latter rule). -As a special case, if a remote source is specified without a destination, -the remote files are listed in an output format similar to "ls -l". +As a special case, if a single source arg is specified without a +destination, the files are listed in an output format similar to "ls -l". As expected, if neither the source or destination path specify a remote host, the copy occurs locally (see also the bf(--list-only) option). @@ -76,9 +78,6 @@ You can also specify any remote shell you like, either by using the bf(-e) command line option, or by setting the RSYNC_RSH environment variable. -One common substitute is to use ssh, which offers a high degree of -security. - Note that rsync must be installed on both the source and destination machines. @@ -135,10 +134,12 @@ destination don't have a ':' in the name. In this case it behaves like an improved copy command. +Finally, you can list all the (listable) modules available from a +particular rsync daemon by leaving off the module name: + quote(tt(rsync somehost.mydomain.com::)) -This would list all the anonymous rsync modules available on the host -somehost.mydomain.com. (See the following section for more details.) +See the following section for more details. manpagesection(ADVANCED USAGE) @@ -184,13 +185,14 @@ itemize( it() you either use a double colon :: instead of a single colon to separate the hostname from the path, or you use an rsync:// URL. - it() the first word after the :: is a module name. + it() the first word of the "path" is actually a module name. it() the remote daemon may print a message of the day when you connect. it() if you specify no path name on the remote daemon then the list of accessible paths on the daemon will be shown. it() if you specify no local destination then a listing of the specified files on the remote daemon is provided. + it() you must not specify the bf(--rsh) (bf(-e)) option. ) An example that copies all the files in a remote module named "src": @@ -238,7 +240,8 @@ If you need to specify a different remote-shell user, keep in mind that the user@ prefix in front of the host is specifying the rsync-user value (for a module that requires user-based authentication). This means that you must -give the '-l user' option to ssh when specifying the remote-shell: +give the '-l user' option to ssh when specifying the remote-shell, as in +this example that uses the short version of the bf(--rsh) option: verb( rsync -av -e "ssh -l ssh-user" rsync-user@host::module /dest) @@ -298,37 +301,43 @@ -q, --quiet suppress non-error messages -c, --checksum skip based on checksum, not mod-time & size -a, --archive archive mode; same as -rlptgoD (no -H) + --no-OPTION turn off an implied OPTION (e.g. --no-D) -r, --recursive recurse into directories -R, --relative use relative path names - --no-relative turn off --relative - --no-implied-dirs don't send implied dirs with -R + --no-implied-dirs don't send implied dirs with --relative -b, --backup make backups (see --suffix & --backup-dir) --backup-dir=DIR make backups into hierarchy based in DIR --suffix=SUFFIX backup suffix (default ~ w/o --backup-dir) -u, --update skip files that are newer on the receiver --inplace update destination files in-place + --append append data onto shorter files -d, --dirs transfer directories without recursing -l, --links copy symlinks as symlinks -L, --copy-links transform symlink into referent file/dir --copy-unsafe-links only "unsafe" symlinks are transformed --safe-links ignore symlinks that point outside the tree - -H, --hard-links preserve hard links + -k, --copy-dirlinks transform symlink to dir into referent dir -K, --keep-dirlinks treat symlinked dir on receiver as dir + -H, --hard-links preserve hard links -p, --perms preserve permissions - -o, --owner preserve owner (root only) + -E, --executability preserve executability + --chmod=CHMOD change destination permissions + -o, --owner preserve owner (super-user only) -g, --group preserve group - -D, --devices preserve devices (root only) + --devices preserve device files (super-user only) + --specials preserve special files + -D same as --devices --specials -t, --times preserve times -O, --omit-dir-times omit directories when preserving times + --super receiver attempts super-user activities -S, --sparse handle sparse files efficiently -n, --dry-run show what would have been transferred -W, --whole-file copy files whole (without rsync algorithm) - --no-whole-file always use incremental rsync algorithm -x, --one-file-system don't cross filesystem boundaries -B, --block-size=SIZE force a fixed checksum block-size -e, --rsh=COMMAND specify the remote shell to use --rsync-path=PROGRAM specify the rsync to run on remote machine - --existing only update files that already exist + --existing ignore non-existing files on receiving side --ignore-existing ignore files that already exist on receiver --remove-sent-files sent files/symlinks are removed from sender --del an alias for --delete-during @@ -341,9 +350,11 @@ --force force deletion of dirs even if not empty --max-delete=NUM don't delete more than NUM files --max-size=SIZE don't transfer any file larger than SIZE + --min-size=SIZE don't transfer any file smaller than SIZE --partial keep partially transferred files --partial-dir=DIR put a partially transferred file into DIR --delay-updates put all updated files into place at end + -m, --prune-empty-dirs prune empty directory chains from file-list --numeric-ids don't map uid/gid values by user/group name --timeout=TIME set I/O timeout in seconds -I, --ignore-times don't skip files that match size and time @@ -355,6 +366,7 @@ --copy-dest=DIR ... and include copies of unchanged files --link-dest=DIR hardlink to files in DIR when unchanged -z, --compress compress file data during the transfer + --compress-level=NUM explicitly set compression level -C, --cvs-exclude auto-ignore files in the same way CVS does -f, --filter=RULE add a file-filtering RULE -F same as --filter='dir-merge /.rsync-filter' @@ -367,9 +379,11 @@ -0, --from0 all *from/filter files are delimited by 0s --address=ADDRESS bind address for outgoing socket to daemon --port=PORT specify double-colon alternate port number + --sockopts=OPTIONS specify custom TCP options --blocking-io use blocking I/O for the remote shell - --no-blocking-io turn off blocking I/O when it is default --stats give some file-transfer stats + -8, --8-bit-output leave high-bit chars unescaped in output + -h, --human-readable output numbers in a human-readable format --progress show progress during transfer -P same as --partial --progress -i, --itemize-changes output a change-summary for all updates @@ -385,7 +399,8 @@ -4, --ipv4 prefer IPv4 -6, --ipv6 prefer IPv6 --version print version number - -h, --help show this help screen) +(-h) --help show this help (see below for -h comment) +) Rsync can also be run as a daemon, in which case the following options are accepted: verb( @@ -395,10 +410,12 @@ --config=FILE specify alternate rsyncd.conf file --no-detach do not detach from the parent --port=PORT listen on alternate port number + --sockopts=OPTIONS specify custom TCP options -v, --verbose increase verbosity -4, --ipv4 prefer IPv4 -6, --ipv6 prefer IPv6 - -h, --help show this help screen) + -h, --help show this help (if used after --daemon) +) manpageoptions() @@ -409,8 +426,10 @@ can be used instead. startdit() -dit(bf(-h, --help)) Print a short help page describing the options -available in rsync. +dit(bf(--help)) Print a short help page describing the options +available in rsync and exit. For backward-compatibility with older +versions of rsync, the help will also be output if you use the bf(-h) +option without any other args. dit(bf(--version)) print the rsync version number and exit. @@ -455,21 +474,51 @@ times with a 2-second resolution), bf(--modify-window=1) is useful (allowing times to differ by up to 1 second). -dit(bf(-c, --checksum)) This forces the sender to checksum all files using -a 128-bit MD4 checksum before transfer. The checksum is then -explicitly checked on the receiver and any files of the same name -which already exist and have the same checksum and size on the -receiver are not transferred. This option can be quite slow. +dit(bf(-c, --checksum)) This forces the sender to checksum em(every) +regular file using a 128-bit MD4 checksum. It does this during the initial +file-system scan as it builds the list of all available files. The receiver +then checksums its version of each file (if it exists and it has the same +size as its sender-side counterpart) in order to decide which files need to +be updated: files with either a changed size or a changed checksum are +selected for transfer. Since this whole-file checksumming of all files on +both sides of the connection occurs in addition to the automatic checksum +verifications that occur during a file's transfer, this option can be quite +slow. + +Note that rsync always verifies that each em(transferred) file was correctly +reconstructed on the receiving side by checking its whole-file checksum, but +that automatic after-the-transfer verification has nothing to do with this +option's before-the-transfer "Does this file need to be updated?" check. dit(bf(-a, --archive)) This is equivalent to bf(-rlptgoD). It is a quick way of saying you want recursion and want to preserve almost -everything. The only exception to this is if bf(--files-from) was +everything (with -H being a notable omission). +The only exception to the above equivalence is when bf(--files-from) is specified, in which case bf(-r) is not implied. Note that bf(-a) bf(does not preserve hardlinks), because finding multiply-linked files is expensive. You must separately specify bf(-H). +dit(--no-OPTION) You may turn off one or more implied options by prefixing +the option name with "no-". Not all options may be prefixed with a "no-": +only options that are implied by other options (e.g. bf(--no-D), +bf(--no-perms)) or have different defaults in various circumstances +(e.g. bf(--no-whole-file), bf(--no-blocking-io), bf(--no-dirs)). You may +specify either the short or the long option name after the "no-" prefix +(e.g. bf(--no-R) is the same as bf(--no-relative)). + +For example: if you want to use bf(-a) (bf(--archive)) but don't want +bf(-o) (bf(--owner)), instead of converting bf(-a) into bf(-rlptgD), you +could specify bf(-a --no-o) (or bf(-a --no-owner)). + +The order of the options is important: if you specify bf(--no-r -a), the +bf(-r) option would end up being turned on, the opposite of bf(-a --no-r). +Note also that the side-effects of the bf(--files-from) option are NOT +positional, as it affects the default state of several options and slightly +changes the meaning of bf(-a) (see the bf(--files-from) option for more +details). + dit(bf(-r, --recursive)) This tells rsync to copy directories recursively. See also bf(--dirs) (bf(-d)). @@ -477,47 +526,85 @@ names specified on the command line are sent to the server rather than just the last parts of the filenames. This is particularly useful when you want to send several different directories at the same time. For -example, if you used the command +example, if you used this command: -quote(tt( rsync /foo/bar/foo.c remote:/tmp/)) +quote(tt( rsync -av /foo/bar/baz.c remote:/tmp/)) -then this would create a file called foo.c in /tmp/ on the remote +... this would create a file named baz.c in /tmp/ on the remote machine. If instead you used -quote(tt( rsync -R /foo/bar/foo.c remote:/tmp/)) +quote(tt( rsync -avR /foo/bar/baz.c remote:/tmp/)) -then a file called /tmp/foo/bar/foo.c would be created on the remote +then a file named /tmp/foo/bar/baz.c would be created on the remote machine -- the full path name is preserved. To limit the amount of -path information that is sent, do something like this: +path information that is sent, you have a couple options: (1) With +a modern rsync on the sending side (beginning with 2.6.7), you can +insert a dot and a slash into the source path, like this: + +quote(tt( rsync -avR /foo/./bar/baz.c remote:/tmp/)) + +That would create /tmp/bar/baz.c on the remote machine. (Note that the +dot must be followed by a slash, so "/foo/." would not be abbreviated.) +(2) For older rsync versions, you would need to use a chdir to limit the +source path. For example, when pushing files: + +quote(tt( (cd /foo; rsync -avR bar/baz.c remote:/tmp/) )) + +(Note that the parens put the two commands into a sub-shell, so that the +"cd" command doesn't remain in effect for future commands.) +If you're pulling files, use this idiom (which doesn't work with an +rsync daemon): quote( -tt( cd /foo)nl() -tt( rsync -R bar/foo.c remote:/tmp/)nl() +tt( rsync -avR --rsync-path="cd /foo; rsync" \ )nl() +tt( remote:bar/baz.c /tmp/) ) -That would create /tmp/bar/foo.c on the remote machine. - -dit(bf(--no-relative)) Turn off the bf(--relative) option. This is only -needed if you want to use bf(--files-from) without its implied bf(--relative) -file processing. - -dit(bf(--no-implied-dirs)) When combined with the bf(--relative) option, the -implied directories in each path are not explicitly duplicated as part -of the transfer. This makes the transfer more optimal and also allows -the two sides to have non-matching symlinks in the implied part of the -path. For instance, if you transfer the file "/path/foo/file" with bf(-R), -the default is for rsync to ensure that "/path" and "/path/foo" on the -destination exactly match the directories/symlinks of the source. Using -the bf(--no-implied-dirs) option would omit both of these implied dirs, -which means that if "/path" was a real directory on one machine and a -symlink of the other machine, rsync would not try to change this. +dit(bf(--no-implied-dirs)) This option affects the default behavior of the +bf(--relative) option. When it is specified, the attributes of the implied +directories from the source names are not included in the transfer. This +means that the corresponding path elements on the destination system are +left unchanged if they exist, and any missing implied directories are +created with default attributes. This even allows these implied path +elements to have big differences, such as being a symlink to a directory on +one side of the transfer, and a real directory on the other side. + +For instance, if a command-line arg or a files-from entry told rsync to +transfer the file "path/foo/file", the directories "path" and "path/foo" +are implied when bf(--relative) is used. If "path/foo" is a symlink to +"bar" on the destination system, the receiving rsync would ordinarily +delete "path/foo", recreate it as a directory, and receive the file into +the new directory. With bf(--no-implied-dirs), the receiving rsync updates +"path/foo/file" using the existing path elements, which means that the file +ends up being created in "path/bar". Another way to accomplish this link +preservation is to use the bf(--keep-dirlinks) option (which will also +affect symlinks to directories in the rest of the transfer). + +In a similar but opposite scenario, if the transfer of "path/foo/file" is +requested and "path/foo" is a symlink on the sending side, running without +bf(--no-implied-dirs) would cause rsync to transform "path/foo" on the +receiving side into an identical symlink, and then attempt to transfer +"path/foo/file", which might fail if the duplicated symlink did not point +to a directory on the receiving side. Another way to avoid this sending of +a symlink as an implied directory is to use bf(--copy-unsafe-links), or +bf(--copy-dirlinks) (both of which also affect symlinks in the rest of the +transfer -- see their descriptions for full details). dit(bf(-b, --backup)) With this option, preexisting destination files are renamed as each file is transferred or deleted. You can control where the backup file goes and what (if any) suffix gets appended using the bf(--backup-dir) and bf(--suffix) options. -Note that if you don't specify bf(--backup-dir), the bf(--omit-dir-times) -option will be enabled. + +Note that if you don't specify bf(--backup-dir), (1) the +bf(--omit-dir-times) option will be implied, and (2) if bf(--delete) is +also in effect (without bf(--delete-excluded)), rsync will add a "protect" +filter-rule for the backup suffix to the end of all your existing excludes +(e.g. -f "P *~"). This will prevent previously backed-up files from being +deleted. Note that if you are supplying your own filter rules, you may +need to manually insert your own exclude/protect rule 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 inclusion/exclusion of '*', the auto-added +rule would never be reached). dit(bf(--backup-dir=DIR)) In combination with the bf(--backup) option, this tells rsync to store all backups in the specified directory. This is @@ -566,17 +653,29 @@ rsync will be unable to update a file in-place that is not writable by the receiving user. +dit(bf(--append)) This causes rsync to update a file by appending data onto +the end of the file, which presumes that the data that already exists on +the receiving side is identical with the start of the file on the sending +side. If that is not true, the file will fail the checksum test, and the +resend will do a normal bf(--inplace) update to correct the mismatched data. +Only files on the receiving side that are shorter than the corresponding +file on the sending side (as well as new files) are sent. +Implies bf(--inplace), but does not conflict with bf(--sparse) (though the +bf(--sparse) option will be auto-disabled if a resend of the already-existing +data is required). + dit(bf(-d, --dirs)) Tell the sending side to include any directories that are encountered. Unlike bf(--recursive), a directory's contents are not copied -unless the directory was specified on the command-line as either "." or a -name with a trailing slash (e.g. "foo/"). Without this option or the +unless the directory name specified is "." or ends with a trailing slash +(e.g. ".", "dir/.", "dir/", etc.). Without this option or the bf(--recursive) option, rsync will skip all directories it encounters (and -output a message to that effect for each one). +output a message to that effect for each one). If you specify both +bf(--dirs) and bf(--recursive), bf(--recursive) takes precedence. 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 +dit(bf(-L, --copy-links)) When symlinks are encountered, the item that 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 @@ -588,61 +687,156 @@ dit(bf(--copy-unsafe-links)) This tells rsync to copy the referent of symbolic links that point outside the copied tree. Absolute symlinks are also treated like ordinary files, and so are any symlinks in the -source path itself when bf(--relative) is used. +source path itself when bf(--relative) is used. This option has no +additional effect if bf(--copy-links) was also specified. dit(bf(--safe-links)) This tells rsync to ignore any symbolic links which point outside the copied tree. All absolute symlinks are also ignored. Using this option in conjunction with bf(--relative) may give unexpected results. -dit(bf(-H, --hard-links)) This tells rsync to recreate hard links on -the remote system to be the same as the local system. Without this -option hard links are treated like regular files. +dit(bf(-K, --copy-dirlinks)) This option causes the sending side to treat +a symlink to a directory as though it were a real directory. This is +useful if you don't want symlinks to non-directories to be affected, as +they would be using bf(--copy-links). + +Without this option, if the sending side has replaced a directory with a +symlink to a directory, the receiving side will delete anything that is in +the way of the new symlink, including a directory hierarchy (as long as +bf(--force) or bf(--delete) is in effect). + +See also bf(--keep-dirlinks) for an analogous option for the receiving +side. + +dit(bf(-K, --keep-dirlinks)) This option causes the receiving side to treat +a symlink to a directory as though it were a real directory, but only if it +matches a real directory from the sender. Without this option, the +receiver's symlink would be deleted and replaced with a real directory. + +For example, suppose you transfer a directory "foo" that contains a file +"file", but "foo" is a symlink to directory "bar" on the receiver. Without +bf(--keep-dirlinks), the receiver deletes symlink "foo", recreates it as a +directory, and receives the file into the new directory. With +bf(--keep-dirlinks), the receiver keeps the symlink and "file" ends up in +"bar". + +See also bf(--copy-dirlinks) for an analogous option for the sending side. + +dit(bf(-H, --hard-links)) This tells rsync to look for hard-linked files in +the transfer and link together the corresponding files on the receiving +side. Without this option, hard-linked files in the transfer are treated +as though they were separate files. Note that rsync can only detect hard links if both parts of the link are in the list of files being sent. -This option can be quite slow, so only use it if you need it. +dit(bf(-p, --perms)) This option causes the receiving rsync to set the +destination permissions to be the same as the source permissions. (See +also the bf(--chmod) option for a way to modify what rsync considers to +be the source permissions.) -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. +When this option is em(off), permissions are set as follows: -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 -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 destination are specified as local paths. +quote(itemize( + it() Existing files (including updated files) retain their existing + permissions, though the bf(--executability) option might change just + the execute permission for the file. + it() New files get their "normal" permission bits set to the source + file's permissions masked with the receiving end's umask setting, and + their special permission bits disabled except in the case where a new + directory inherits a setgid bit from its parent directory. +)) + +Thus, when bf(--perms) and bf(--executability) are both disabled, +rsync's behavior is the same as that of other file-copy utilities, +such as bf(cp)(1) and bf(tar)(1). + +In summary: to give destination files (both old and new) the source +permissions, use bf(--perms). To give new files the destination-default +permissions (while leaving existing files unchanged), make sure that the +bf(--perms) option is off and use bf(--chmod=ugo=rwX) (which ensures that +all non-masked bits get enabled). If you'd care to make this latter +behavior easier to type, you could define a popt alias for it, such as +putting this line in the file ~/.popt (this defines the bf(-s) option, +and includes --no-g to use the default group of the destination dir): + +quote(tt( rsync alias -s --no-p --no-g --chmod=ugo=rwX)) + +You could then use this new option in a command such as this one: + +quote(tt( rsync -asv src/ dest/)) + +(Caveat: make sure that bf(-a) does not follow bf(-s), or it will re-enable +the "--no-*" options.) + +The preservation of the destination's setgid bit on newly-created +directories when bf(--perms) is off was added in rsync 2.6.7. Older rsync +versions erroneously preserved the three special permission bits for +newly-created files when bf(--perms) was off, while overriding the +destination's setgid bit setting on a newly-created directory. (Keep in +mind that it is the version of the receiving rsync that affects this +behavior.) + +dit(bf(-E, --executability)) This option causes rsync to preserve the +executability (or non-executability) of regular files when bf(--perms) is +not enabled. A regular file is considered to be executable if at least one +'x' is turned on in its permissions. When an existing destination file's +executability differs from that of the corresponding source file, rsync +modifies the destination file's permissions as follows: + +quote(itemize( + it() To make a file non-executable, rsync turns off all its 'x' + permissions. + it() To make a file executable, rsync turns on each 'x' permission that + has a corresponding 'r' permission enabled. +)) + +If bf(--perms) is enabled, this option is ignored. -dit(bf(--no-whole-file)) Turn off bf(--whole-file), for use when it is the -default. +dit(bf(--chmod)) This option tells rsync to apply one or more +comma-separated "chmod" strings to the permission of the files in the +transfer. The resulting value is treated as though it was the permissions +that the sending side supplied for the file, which means that this option +can seem to have no effect on existing files if bf(--perms) is not enabled. + +In addition to the normal parsing rules specified in the bf(chmod)(1) +manpage, you can specify an item that should only apply to a directory by +prefixing it with a 'D', or specify an item that should only apply to a +file by prefixing it with a 'F'. For example: -dit(bf(-p, --perms)) This option causes rsync to set the destination -permissions to be the same as the source permissions. +quote(--chmod=Dg+s,ug+w,Fo-w,+X) -Without this option, all existing files (including updated files) retain -their existing permissions, while each new file gets its permissions set -based on the source file's permissions, but masked by the receiving end's -umask setting -(which is the same behavior as other file-copy utilities, such as cp). +It is also legal to specify multiple bf(--chmod) options, as each +additional option is just appended to the list of changes to make. + +See the bf(--perms) and bf(--executability) options for how the resulting +permission value can be applied to the files in the transfer. dit(bf(-o, --owner)) This option causes rsync to set the owner of the -destination file to be the same as the source file. On most systems, -only the super-user can set file ownership. By default, the preservation -is done by name, but may fall back to using the ID number in some -circumstances. See the bf(--numeric-ids) option for a full discussion. +destination file to be the same as the source file. By default, the +preservation is done by name, but may fall back to using the ID number +in some circumstances (see the bf(--numeric-ids) option for a full +discussion). +This option has no effect if the receiving rsync is not run as the +super-user and bf(--super) is not specified. dit(bf(-g, --group)) This option causes rsync to set the group of the destination file to be the same as the source file. If the receiving -program is not running as the super-user, only groups that the +program is not running as the super-user (or with the bf(--no-super) +option), only groups that the receiver is a member of will be preserved. By default, the preservation is done by name, but may fall back to using the ID number in some circumstances. See the bf(--numeric-ids) option for a full discussion. -dit(bf(-D, --devices)) This option causes rsync to transfer character and -block device information to the remote system to recreate these -devices. This option is only available to the super-user. +dit(bf(--devices)) This option causes rsync to transfer character and +block device files to the remote system to recreate these devices. +This option has no effect if the receiving rsync is not run as the +super-user and bf(--super) is not specified. + +dit(bf(--specials)) This option causes rsync to transfer special files +such as named sockets and fifos. + +dit(bf(-D)) The bf(-D) option is equivalent to bf(--devices) bf(--specials). dit(bf(-t, --times)) This tells rsync to transfer modification times along with the files and update them on the remote system. Note that if this @@ -657,26 +851,59 @@ the directories on the receiving side, it is a good idea to use bf(-O). This option is inferred if you use bf(--backup) without bf(--backup-dir). -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. +dit(bf(--super)) This tells the receiving side to attempt super-user +activities even if the receiving rsync wasn't run by the super-user. These +activities include: preserving users via the bf(--owner) option, preserving +all groups (not just the current user's groups) via the bf(--groups) +option, and copying devices via the bf(--devices) option. This is useful +for systems that allow such activities without being the super-user, and +also for ensuring that you will get errors if the receiving side isn't +being running as the super-user. To turn off super-user activities, the +super-user can use bf(--no-super). dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take -up less space on the destination. +up less space on the destination. Conflicts with bf(--inplace) because it's +not possible to overwrite data in a sparse fashion. NOTE: Don't use this option when the destination is a Solaris "tmpfs" filesystem. It doesn't seem to handle seeks over null regions correctly and ends up corrupting the files. -dit(bf(-x, --one-file-system)) This tells rsync not to cross filesystem -boundaries when recursing. This is useful for transferring the -contents of only one filesystem. - -dit(bf(--existing)) This tells rsync not to create any new files -- -only update files that already exist on the destination. - -dit(bf(--ignore-existing)) -This tells rsync not to update files that already exist on -the destination. +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. + +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 +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 destination are specified as local paths. + +dit(bf(-x, --one-file-system)) This tells rsync to avoid crossing a +filesystem boundary when recursing. This does not limit the user's ability +to specify items to copy from multiple filesystems, just rsync's recursion +through the hierarchy of each directory that the user specified, and also +the analogous recursion on the receiving side during deletion. Also keep +in mind that rsync treats a "bind" mount to the same device as being on the +same filesystem. + +If this option is repeated, rsync omits all mount-point directories from +the copy. Otherwise, it includes an empty directory at each mount-point it +encounters (using the attributes of the mounted directory because those of +the underlying mount-point directory are inaccessible). + +If rsync has been told to collapse symlinks (via bf(--copy-links) or +bf(--copy-unsafe-links)), a symlink to a directory on another device is +treated like a mount-point. Symlinks to non-directories are unaffected +by this option. + +dit(bf(--existing, --ignore-non-existing)) This tells rsync to skip +updating files that do not exist yet on the destination. If this option is +combined with the bf(--ignore-existing) option, no files will be updated +(which can be useful if all you want to do is to delete missing files). + +dit(bf(--ignore-existing)) This tells rsync to skip updating files that +already exist on the destination. See also bf(--ignore-non-existing). dit(bf(--remove-sent-files)) This tells rsync to remove from the sending side the files and/or symlinks that are newly created or whose content is @@ -694,7 +921,9 @@ option or mark the rules as only matching on the sending side (see the include/exclude modifiers in the FILTER RULES section). -This option has no effect unless directory recursion is enabled. +Prior to rsync 2.6.7, this option would have no effect unless bf(--recursive) +was in effect. Beginning with 2.6.7, deletions will also occur when bf(--dirs) +(bf(-d)) is in effect, but only for directories whose contents are being copied. This option can be dangerous if used incorrectly! It is a very good idea to run first using the bf(--dry-run) option (bf(-n)) to see what files would be @@ -747,10 +976,13 @@ dit(bf(--ignore-errors)) Tells bf(--delete) to go ahead and delete files even when there are I/O errors. -dit(bf(--force)) This options tells rsync to delete directories even if -they are not empty when they are to be replaced by non-directories. This -is only relevant without bf(--delete) because deletions are now done depth-first. -Requires the bf(--recursive) option (which is implied by bf(-a)) to have any effect. +dit(bf(--force)) This option tells rsync to delete a non-empty directory +when it is to be replaced by a non-directory. This is only relevant if +deletions are not active (see bf(--delete) for details). + +Note for older rsync versions: bf(--force) used to still be required when +using bf(--delete-after), and it used to be non-functional unless the +bf(--recursive) option was also enabled. dit(bf(--max-delete=NUM)) This tells rsync not to delete more than NUM files or directories (NUM must be non-zero). @@ -758,9 +990,25 @@ dit(bf(--max-size=SIZE)) This tells rsync to avoid transferring any file that is larger than the specified SIZE. The SIZE value can be -suffixed with a letter to indicate a size multiplier (K, M, or G) and +suffixed with a string to indicate a size multiplier, and may be a fractional value (e.g. "bf(--max-size=1.5m)"). +The suffixes are as follows: "K" (or "KiB") is a kibibyte (1024), +"M" (or "MiB") is a mebibyte (1024*1024), and "G" (or "GiB") is a +gibibyte (1024*1024*1024). +If you want the multiplier to be 1000 instead of 1024, use "KB", +"MB", or "GB". (Note: lower-case is also accepted for all values.) +Finally, if the suffix ends in either "+1" or "-1", the value will +be offset by one byte in the indicated direction. + +Examples: --max-size=1.5mb-1 is 1499999 bytes, and --max-size=2g+1 is +2147483649 bytes. + +dit(bf(--min-size=SIZE)) This tells rsync to avoid transferring any +file that is smaller than the specified SIZE, which can help in not +transferring small, junk files. +See the bf(--max-size) option for a description of SIZE. + 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. @@ -778,9 +1026,18 @@ RSYNC-DAEMON FEATURES VIA A REMOTE-SHELL CONNECTION" above. Command-line arguments are permitted in COMMAND provided that COMMAND is -presented to rsync as a single argument. For example: +presented to rsync as a single argument. You must use spaces (not tabs +or other whitespace) to separate the command and args from each other, +and you can use single- and/or double-quotes to preserve spaces in an +argument (but not backslashes). Note that doubling a single-quote +inside a single-quoted string gives you a single-quote; likewise for +double-quotes (though you need to pay attention to which quotes your +shell is parsing and which quotes rsync is parsing). Some examples: -quote(tt( -e "ssh -p 2234")) +quote( +tt( -e 'ssh -p 2234')nl() +tt( -e 'ssh -o "ProxyCommand nohup ssh firewall nc -w1 %h %p"')nl() +) (Note that ssh users can alternately customize site-specific connect options in their .ssh/config file.) @@ -847,14 +1104,14 @@ dit(bf(-F)) The bf(-F) option is a shorthand for adding two bf(--filter) rules to your command. The first time it is used is a shorthand for this rule: -quote(tt( --filter=': /.rsync-filter')) +quote(tt( --filter='dir-merge /.rsync-filter')) This tells rsync to look for per-directory .rsync-filter files that have been sprinkled through the hierarchy and use their rules to filter the files in the transfer. If bf(-F) is repeated, it is a shorthand for this rule: -quote(tt( --filter='- .rsync-filter')) +quote(tt( --filter='exclude .rsync-filter')) This filters out the .rsync-filter files themselves from the transfer. @@ -867,11 +1124,10 @@ See the FILTER RULES section for detailed information on this option. -dit(bf(--exclude-from=FILE)) This option is similar to the bf(--exclude) -option, but instead it adds all exclude patterns listed in the file -FILE to the exclude list. Blank lines in FILE and lines starting with -';' or '#' are ignored. -If em(FILE) is bf(-) the list will be read from standard input. +dit(bf(--exclude-from=FILE)) This option is related to the bf(--exclude) +option, but it specifies a FILE that contains exclude patterns (one per line). +Blank lines in the file and lines starting with ';' or '#' are ignored. +If em(FILE) is bf(-), the list will be read from standard input. dit(bf(--include=PATTERN)) This option is a simplified form of the bf(--filter) option that defaults to an include rule and does not allow @@ -879,24 +1135,29 @@ See the FILTER RULES section for detailed information on this option. -dit(bf(--include-from=FILE)) This specifies a list of include patterns -from a file. -If em(FILE) is "-" the list will be read from standard input. +dit(bf(--include-from=FILE)) This option is related to the bf(--include) +option, but it specifies a FILE that contains include patterns (one per line). +Blank lines in the file and lines starting with ';' or '#' are ignored. +If em(FILE) is bf(-), 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 "-" +exact list of files to transfer (as read from the specified FILE or bf(-) for standard input). It also tweaks the default behavior of rsync to make transferring just the specified files and directories easier: quote(itemize( it() The bf(--relative) (bf(-R)) option is implied, which preserves the path information that is specified for each item in the file (use - bf(--no-relative) if you want to turn that off). + bf(--no-relative) or bf(--no-R) if you want to turn that off). it() The bf(--dirs) (bf(-d)) option is implied, which will create directories specified in the list on the destination rather than noisily skipping - them. + them (use bf(--no-dirs) or bf(--no-d) if you want to turn that off). it() The bf(--archive) (bf(-a)) option's behavior does not imply bf(--recursive) (bf(-r)), so specify it explicitly, if you want it. + it() These side-effects change the default state of rsync, so the position + of the bf(--files-from) option on the command-line has no bearing on how + other options are parsed (e.g. bf(-a) works the same before or after + bf(--files-from), as does bf(--no-R) and all other options). )) The file names that are read from the FILE are all relative to the @@ -938,9 +1199,36 @@ file are split on whitespace). dit(bf(-T, --temp-dir=DIR)) This option instructs rsync to use DIR as a -scratch directory when creating temporary copies of the files -transferred on the receiving side. The default behavior is to create -the temporary files in the receiving directory. +scratch directory when creating temporary copies of the files transferred +on the receiving side. The default behavior is to create each temporary +file in the same directory as the associated destination file. + +This option is most often used when the receiving disk partition does not +have enough free space to hold a copy of the largest file in the transfer. +In this case (i.e. when the scratch directory in on a different disk +partition), rsync will not be able to rename each received temporary file +over the top of the associated destination file, but instead must copy it +into place. Rsync does this by copying the file over the top of the +destination file, which means that the destination file will contain +truncated data during this copy. If this were not done this way (even if +the destination file were first removed, the data locally copied to a +temporary file in the destination directory, and then renamed into place) +it would be possible for the old file to continue taking up disk space (if +someone had it open), and thus there might not be enough room to fit the +new version on the disk at the same time. + +If you are using this option for reasons other than a shortage of disk +space, you may wish to combine it with the bf(--delay-updates) option, +which will ensure that all copied files get put into subdirectories in the +destination hierarchy, awaiting the end of the transfer. If you don't +have enough room to duplicate all the arriving files on the destination +partition, another way to tell rsync that you aren't overly concerned +about disk space is to use the bf(--partial-dir) option with a relative +path; because this tells rsync that it is OK to stash off a copy of a +single file in a subdir in the destination hierarchy, rsync will use the +partial-dir as a staging area to bring over the copied file, and then +rename it into place from there. (Specifying a bf(--partial-dir) with +an absolute path does not have this side-effect.) dit(bf(-y, --fuzzy)) This option tells rsync that it should look for a basis file for any destination file that is missing. The current algorithm @@ -1006,19 +1294,23 @@ See also bf(--compare-dest) and bf(--copy-dest). Note that rsync versions prior to 2.6.1 had a bug that could prevent -bf(--link-dest) from working properly for a non-root user when bf(-o) was specified -(or implied by bf(-a)). You can work-around this bug by avoiding the bf(-o) option -when sending to an old rsync. +bf(--link-dest) from working properly for a non-super-user when bf(-o) was +specified (or implied by bf(-a)). You can work-around this bug by avoiding +the bf(-o) option when sending to an old rsync. dit(bf(-z, --compress)) With this option, rsync compresses the file data as it is sent to the destination machine, which reduces the amount of data being transmitted -- something that is useful over a slow connection. -Note this this option typically achieves better compression ratios that can +Note that this option typically achieves better compression ratios than can be achieved by using a compressing remote shell or a compressing transport because it takes advantage of the implicit information in the matching data blocks that are not explicitly sent over the connection. +dit(bf(--compress-level=NUM)) Explicitly set the compression level to use +(see bf(--compress)) instead of letting it default. If NUM is non-zero, +the bf(--compress) option is implied. + dit(bf(--numeric-ids)) With this option rsync will transfer numeric group and user IDs rather than using user and group names and mapping them at both ends. @@ -1050,26 +1342,36 @@ syntax has a way to specify the port as a part of the URL). See also this option in the bf(--daemon) mode section. +dit(bf(--sockopts)) This option can provide endless fun for people +who like to tune their systems to the utmost degree. You can set all +sorts of socket options which may make transfers faster (or +slower!). Read the man page for the setsockopt() system call for +details on some of the options you may be able to set. By default no +special socket options are set. This only affects direct socket +connections to a remote rsync daemon. This option also exists in the +bf(--daemon) mode section. + dit(bf(--blocking-io)) This tells rsync to use blocking I/O when launching a remote shell transport. If the remote shell is either rsh or remsh, rsync defaults to using blocking I/O, otherwise it defaults to using non-blocking I/O. (Note that ssh prefers non-blocking I/O.) -dit(bf(--no-blocking-io)) Turn off bf(--blocking-io), for use when it is the -default. - dit(bf(-i, --itemize-changes)) Requests a simple itemized list of the changes that are being made to each file, including attribute changes. This is exactly the same as specifying bf(--log-format='%i %n%L'). +If you repeat the option, unchanged files will also be output, but only +if the receiving rsync is at least version 2.6.7 (you can use bf(-vv) +with older versions of rsync, but that also turns on the output of other +verbose messages). The "%i" escape has a cryptic output that is 9 letters long. The general -format is like the string bf(UXcstpoga)), where bf(U) is replaced by the -kind of update being done, bf(X) is replaced by the file-type, and the +format is like the string bf(YXcstpogz), where bf(Y) is replaced by the +type of update being done, bf(X) is replaced by the file-type, and the other letters represent attributes that may be output if they are being modified. -The update types that replace the bf(U) are as follows: +The update types that replace the bf(Y) are as follows: quote(itemize( it() A bf(<) means that a file is being transferred to the remote host @@ -1078,14 +1380,15 @@ (received). it() A bf(c) means that a local change/creation is occurring for the item (such as the creation of a directory or the changing of a symlink, etc.). - it() A bf(h) means that the item is a hard-link to another item (requires + it() A bf(h) means that the item is a hard link to another item (requires bf(--hard-links)). it() A bf(.) means that the item is not being updated (though it might have attributes that are being modified). )) The file-types that replace the bf(X) are: bf(f) for a file, a bf(d) for a -directory, an bf(L) for a symlink, and a bf(D) for a device. +directory, an bf(L) for a symlink, a bf(D) for a device, and a bf(S) for a +special file (e.g. named sockets and fifos). The other letters in the string above are the actual letters that will be output if the associated attribute for the item is being updated or @@ -1109,11 +1412,10 @@ it() A bf(p) means the permissions are different and are being updated to the sender's value (requires bf(--perms)). it() An bf(o) means the owner is different and is being updated to the - sender's value (requires bf(--owner) and root privileges). + sender's value (requires bf(--owner) and super-user privileges). it() A bf(g) means the group is different and is being updated to the sender's value (requires bf(--group) and the authority to set the group). - it() The bf(a) is reserved for a future enhanced version that supports - extended file attributes, such as ACLs. + it() The bf(z) slot is reserved for future use. )) One other output is possible: when deleting files, the "%i" will output @@ -1130,14 +1432,14 @@ Specifying this option will mention each file, dir, etc. that gets updated in a significant way (a transferred file, a recreated symlink/device, or a -touched directory) unless the itemized-changes escape (%i) is included in +touched directory) unless the itemize-changes escape (%i) is included in the string, in which case the logging of names increases to mention any item that is changed in any way (as long as the receiving side is at least -2.6.4). See the bf(--itemized-changes) option for a description of the +2.6.4). See the bf(--itemize-changes) option for a description of the output of "%i". The bf(--verbose) option implies a format of "%n%L", but you can use -bf(--log-format) without bv(--verbose) if you like, or you can override +bf(--log-format) without bf(--verbose) if you like, or you can override the format of its per-file output using this option. Rsync will output the log-format string prior to a file's transfer unless @@ -1151,6 +1453,55 @@ on the file transfer, allowing you to tell how effective the rsync algorithm is for your data. +The current statistics are as follows: itemize( + it() bf(Number of files) is the count of all "files" (in the generic + sense), which includes directories, symlinks, etc. + it() bf(Number of files transferred) is the count of normal files that + were updated via the rsync algorithm, which does not include created + dirs, symlinks, etc. + it() bf(Total file size) is the total sum of all file sizes in the transfer. + This does not count any size for directories or special files, but does + include the size of symlinks. + it() bf(Total transferred file size) is the total sum of all files sizes + for just the transferred files. + it() bf(Literal data) is how much unmatched file-update data we had to + send to the receiver for it to recreate the updated files. + it() bf(Matched data) is how much data the receiver got locally when + recreating the updated files. + it() bf(File list size) is how big the file-list data was when the sender + sent it to the receiver. This is smaller than the in-memory size for the + file list due to some compressing of duplicated data when rsync sends the + list. + it() bf(File list generation time) is the number of seconds that the + sender spent creating the file list. This requires a modern rsync on the + sending side for this to be present. + it() bf(File list transfer time) is the number of seconds that the sender + spent sending the file list to the receiver. + it() bf(Total bytes sent) is the count of all the bytes that rsync sent + from the client side to the server side. + it() bf(Total bytes received) is the count of all non-message bytes that + rsync received by the client side from the server side. "Non-message" + bytes means that we don't count the bytes for a verbose message that the + server sent to us, which makes the stats more consistent. +) + +dit(bf(-8, --8-bit-output)) This tells rsync to leave all high-bit characters +unescaped in the output instead of trying to test them to see if they're +valid in the current locale and escaping the invalid ones. All control +characters (but never tabs) are always escaped, regardless of this option's +setting. + +The escape idiom that started in 2.6.7 is to output a literal backslash (\) +and a hash (#), followed by exactly 3 octal digits. For example, a newline +would output as "\#012". A literal backslash that is in a filename is not +escaped unless it is followed by a hash and 3 digits (0-9). + +dit(bf(-h, --human-readable)) Output numbers in a more human-readable format. +This makes big numbers output using larger units, with a K, M, or G suffix. If +this option was specified once, these units are K (1000), M (1000*1000), and +G (1000*1000*1000); if the option is repeated, the units are powers of 1024 +instead of 1000. + dit(bf(--partial)) By default, rsync will delete any partially transferred file if the transfer is interrupted. In some circumstances it is more desirable to keep partially transferred files. Using the @@ -1161,8 +1512,9 @@ bf(--partial) option is to specify a em(DIR) that will be used to hold the partial data (instead of writing it out to the destination file). On the next transfer, rsync will use a file found in this -dir as data to speed up the resumption of the transfer and then deletes it +dir as data to speed up the resumption of the transfer and then delete it after it has served its purpose. + Note that if bf(--whole-file) is specified (or implied), any partial-dir file that is found for a file that is being updated will simply be removed (since @@ -1174,30 +1526,36 @@ partial-directory in the destination file's directory when needed, and then remove it again when the partial file is deleted. -If the partial-dir value is not an absolute path, rsync will also add a directory -bf(--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 bf(--partial-dir) option would add an "bf(--exclude=.rsync-partial/)" -rule at the end of any other filter rules. Note that if you are -supplying your own filter 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 bf(--exclude='*') rule, the auto-added rule would never be -reached). +If the partial-dir value is not an absolute path, rsync will add an exclude +rule at the end of all your existing excludes. This will prevent the +sending of any partial-dir files that may exist on the sending side, and +will also prevent the untimely deletion of partial-dir items on the +receiving side. An example: the above bf(--partial-dir) option would add +the equivalent of "bf(--exclude=.rsync-partial/)" at the end of any other +filter rules. + +If you are supplying your own exclude rules, you may need to add your own +exclude/hide/protect rule for the partial-dir because (1) the auto-added +rule may be ineffective at the end of your other rules, or (2) you may wish +to override rsync's exclude choice. For instance, if you want to make +rsync clean-up any left-over partial-dirs that may be lying around, you +should specify bf(--delete-after) and add a "risk" filter rule, e.g. +bf(-f 'R .rsync-partial/'). (Avoid using bf(--delete-before) or +bf(--delete-during) unless you don't need rsync to use any of the +left-over partial-dir data during the current run.) IMPORTANT: the bf(--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 bf(--partial) to be -enabled, but rather it effects where partial files go when bf(--partial) is +enabled, but rather it affects where partial files go when bf(--partial) is specified. For instance, instead of using bf(--partial-dir=.rsync-tmp) along with bf(--progress), you could set RSYNC_PARTIAL_DIR=.rsync-tmp in your environment and then just use the bf(-P) option to turn on the use of the -.rsync-tmp dir for partial transfers. The only time that the bf(--partial) -option does not look for this environment value is (1) when bf(--inplace) was -specified (since bf(--inplace) conflicts with bf(--partial-dir)), or (2) when +.rsync-tmp dir for partial transfers. The only times that the bf(--partial) +option does not look for this environment value are (1) when bf(--inplace) was +specified (since bf(--inplace) conflicts with bf(--partial-dir)), and (2) when bf(--delay-updates) was specified (see below). For the purposes of the daemon-config's "refuse options" setting, @@ -1211,24 +1569,58 @@ transfer, at which time all the files are renamed into place in rapid succession. This attempts to make the updating of the files a little more atomic. By default the files are placed into a directory named ".~tmp~" in -each file's destination directory, but you can override this by specifying -the bf(--partial-dir) option. (Note that RSYNC_PARTIAL_DIR has no effect -on this value, nor is bf(--partial-dir) considered to be implied for the -purposes of the daemon-config's "refuse options" setting.) -Conflicts with bf(--inplace). +each file's destination directory, but if you've specified the +bf(--partial-dir) option, that directory will be used instead. See the +comments in the bf(--partial-dir) section for a discussion of how this +".~tmp~" dir will be excluded from the transfer, and what you can do if +you wnat rsync to cleanup old ".~tmp~" dirs that might be lying around. +Conflicts with bf(--inplace) and bf(--append). This option uses more memory on the receiving side (one bit per file transferred) and also requires enough free disk space on the receiving side to hold an additional copy of all the updated files. Note also that -you should not use an absolute path to bf(--partial-dir) unless there is no +you should not use an absolute path to bf(--partial-dir) unless (1) +there is no chance of any of the files in the transfer having the same name (since all the updated files will be put into a single directory if the path is -absolute). +absolute) +and (2) there are no mount points in the hierarchy (since the +delayed updates will fail if they can't be renamed into place). See also the "atomic-rsync" perl script in the "support" subdir for an update algorithm that is even more atomic (it uses bf(--link-dest) and a parallel hierarchy of files). +dit(bf(-m, --prune-empty-dirs)) This option tells the receiving rsync to get +rid of empty directories from the file-list, including nested directories +that have no non-directory children. This is useful for avoiding the +creation of a bunch of useless directories when the sending rsync is +recursively scanning a hierarchy of files using include/exclude/filter +rules. + +Because the file-list is actually being pruned, this option also affects +what directories get deleted when a delete is active. However, keep in +mind that excluded files and directories can prevent existing items from +being deleted (because an exclude hides source files and protects +destination files). + +You can prevent the pruning of certain empty directories from the file-list +by using a global "protect" filter. For instance, this option would ensure +that the directory "emptydir" was kept in the file-list: + +quote( --filter 'protect emptydir/') + +Here's an example that copies all .pdf files in a hierarchy, only creating +the necessary destination directories to hold the .pdf files, and ensures +that any superfluous files and directories in the destination are removed +(note the hide filter of non-directories being used instead of an exclude): + +quote( rsync -avm --del --include='*.pdf' -f 'hide,! */' src/ dest) + +If you didn't want to remove superfluous destination files, the more +time-honored options of "--include='*/' --exclude='*'" would work fine +in place of the hide-filter (if that is more natural to you). + dit(bf(--progress)) This option tells rsync to print information showing the progress of the transfer. This gives a bored user something to watch. @@ -1265,13 +1657,18 @@ single line. dit(bf(--list-only)) This option will cause the source files to be listed -instead of transferred. This option is inferred if there is no destination -specified, so you don't usually need to use it explicitly. However, it can -come in handy for a user that wants to avoid the "bf(-r --exclude='/*/*')" -options that rsync might use as a compatibility kluge when generating a -non-recursive listing, or to list the files that are involved in a local -copy (since the destination path is not optional for a local copy, you -must specify this option explicitly and still include a destination). +instead of transferred. This option is inferred if there is a single source +arg and no destination specified, so its main uses are: (1) to turn a copy +command that includes a +destination arg into a file-listing command, (2) to be able to specify more +than one local source arg (note: be sure to include the destination), or +(3) to avoid the automatically added "bf(-r --exclude='/*/*')" options that +rsync usually uses as a compatibility kluge when generating a non-recursive +listing. Caution: keep in mind that a source arg with a wild-card is expanded +by the shell into multiple args, so it is never safe to try to list such an arg +without using this option. For example: + +verb( rsync -av --list-only foo* dest/) dit(bf(--bwlimit=KBPS)) This option allows you to specify a maximum transfer rate in kilobytes per second. This option is most effective when @@ -1304,7 +1701,7 @@ dit(bf(--read-batch=FILE)) Apply all of the changes stored in FILE, a file previously generated by bf(--write-batch). -If em(FILE) is "-" the batch data will be read from standard input. +If em(FILE) is bf(-), the batch data will be read from standard input. See the "BATCH MODE" section for details. dit(bf(--protocol=NUM)) Force an older protocol version to be used. This @@ -1362,7 +1759,7 @@ dit(bf(--config=FILE)) This specifies an alternate config file than the default. This is only relevant when bf(--daemon) is specified. The default is /etc/rsyncd.conf unless the daemon is running over -a remote shell program and the remote user is not root; in that case +a remote shell program and the remote user is not the super-user; in that case the default is rsyncd.conf in the current directory (typically $HOME). dit(bf(--no-detach)) When running as a daemon, this option instructs @@ -1378,6 +1775,9 @@ daemon to listen on rather than the default of 873. See also the "port" global option in the rsyncd.conf manpage. +dit(bf(--sockopts)) This overrides the bf(socket options) setting in the +rsyncd.conf file and has the same syntax. + dit(bf(-v, --verbose)) This option increases the amount of information the daemon logs during its startup phase. After the client connects, the daemon's verbosity level will be controlled by the options that the client @@ -1466,7 +1866,7 @@ particular spot in the hierarchy of files, otherwise it is matched against the end of the pathname. This is similar to a leading ^ in regular expressions. - Thus "/foo" would match a file called "foo" at either the "root of the + Thus "/foo" would match a file named "foo" at either the "root of the transfer" (for a global rule) or in the merge-file's directory (for a per-directory rule). An unqualified "foo" would match any file or directory named "foo" @@ -1480,18 +1880,27 @@ of the transfer. it() if the pattern ends with a / then it will only match a directory, not a file, link, or device. - it() if the pattern contains a wildcard character from the set - *?[ then expression matching is applied using the shell filename - matching rules. Otherwise a simple string match is used. - it() the double asterisk pattern "**" will match slashes while a - single asterisk pattern "*" will stop at slashes. - it() if the pattern contains a / (not counting a trailing /) or a "**" + + it() rsync chooses between doing a simple string match and wildcard + matching by checking if the pattern contains one of these three wildcard + characters: '*', '?', and '[' . + it() a '*' matches any non-empty path component (it stops at slashes). + it() use '**' to match anything, including slashes. + it() a '?' matches any character except a slash (/). + it() a '[' introduces a character class, such as [a-z] or [[:alpha:]]. + it() in a wildcard pattern, a backslash can be used to escape a wildcard + character, but it is matched literally when no wildcards are present. + it() if the pattern contains a / (not counting a trailing /) or a "**", then it is matched against the full pathname, including any leading directories. If the pattern doesn't contain a / or a "**", then it is matched only against the final component of the filename. (Remember that the algorithm is applied recursively so "full filename" can actually be any portion of a path from the starting directory on down.) + it() a trailing "dir_name/***" will match both the directory (as if + "dir_name/" had been specified) and all the files in the directory + (as if "dir_name/**" had been specified). (This behavior is new for + version 2.6.7.) ) Note that, when using the bf(--recursive) (bf(-r)) option (which is implied by @@ -1516,7 +1925,8 @@ rule, so rsync never visits any of the files in the "some" or "some/path" directories. One solution is to ask for all directories in the hierarchy to be included by using a single rule: "+ */" (put it somewhere before the -"- *" rule). Another solution is to add specific include rules for all +"- *" rule), and perhaps use the bf(--prune-empty-dirs) option. Another +solution is to add specific include rules for all the parent dirs that need to be visited. For instance, this set of rules works fine: @@ -1532,14 +1942,16 @@ itemize( it() "- *.o" would exclude all filenames matching *.o - it() "- /foo" would exclude a file called foo in the transfer-root directory - it() "- foo/" would exclude any directory called foo - it() "- /foo/*/bar" would exclude any file called bar two - levels below a directory called foo in the transfer-root directory - it() "- /foo/**/bar" would exclude any file called bar two - or more levels below a directory called foo in the transfer-root directory + it() "- /foo" would exclude a file (or directory) named foo in the + transfer-root directory + it() "- foo/" would exclude any directory named foo + it() "- /foo/*/bar" would exclude any file named bar which is at two + levels below a directory named foo in the transfer-root directory + it() "- /foo/**/bar" would exclude any file named bar two + or more levels below a directory named foo in the transfer-root directory it() The combination of "+ */", "+ *.c", and "- *" would include all - directories and C source files but nothing else. + directories and C source files but nothing else (see also the + bf(--prune-empty-dirs) option) it() The combination of "+ foo/", "+ foo/bar.c", and "- *" would include only the foo directory and foo/bar.c (the foo directory must be explicitly included or it would be excluded by the "*") @@ -1593,7 +2005,7 @@ "- foo + bar" is parsed as two rules (assuming that prefix-parsing wasn't also disabled). it() You may also specify any of the modifiers for the "+" or "-" rules - (below) in order to have the rules that are read-in from the file + (below) in order to have the rules that are read in from the file default to having that modifier set. For instance, "merge,-/ .excl" would treat the contents of .excl as absolute-path excludes, while "dir-merge,s .filt" and ":sC" would each make all their @@ -1603,10 +2015,12 @@ The following modifiers are accepted after a "+" or "-": itemize( - it() A "/" specifies that the include/exclude should be treated as an - absolute path, relative to the root of the filesystem. For example, + it() A "/" specifies that the include/exclude rule should be matched + against the absolute pathname of the current item. For example, "-/ /etc/passwd" would exclude the passwd file any time the transfer - was sending files from the "/etc" directory. + was sending files from the "/etc" directory, and "-/ subdir/foo" + would always exclude "foo" when it is in a dir named "subdir", even + if "foo" is at the root of the current transfer. it() A "!" specifies that the include/exclude should take effect if the pattern fails to match. For instance, "-! */" would exclude all non-directories. @@ -1654,7 +2068,7 @@ This will merge the contents of the /home/user/.global-filter file at the start of the list and also turns the ".rules" filename into a per-directory -filter file. All rules read-in prior to the start of the directory scan +filter file. All rules read in prior to the start of the directory scan follow the global anchoring rules (i.e. a leading slash matches at the root of the transfer). @@ -1972,7 +2386,7 @@ dit(bf(--copy-unsafe-links)) Turn all unsafe symlinks into files, noisily skip all safe symlinks. -dit(bf(--links --safe-links)) Duplicate safe symlinks and skip unsafe +dit(bf(--links --safe-links)) Duplicate safe symlinks and skip unsafe ones. dit(bf(--links)) Duplicate all symlinks. @@ -2062,7 +2476,7 @@ manpagebugs() -times are transferred as unix time_t values +times are transferred as *nix time_t values When transferring to FAT filesystems rsync may re-sync unmodified files. @@ -2078,7 +2492,7 @@ manpagesection(VERSION) -This man page is current for version 2.6.6 of rsync. +This man page is current for version 2.6.7 of rsync. manpagesection(CREDITS) diff -urN --exclude=patches rsync-2.6.6/rsyncd.conf.5 rsync-2.6.7/rsyncd.conf.5 --- rsync-2.6.6/rsyncd.conf.5 2005-07-28 12:31:08.000000000 -0700 +++ rsync-2.6.7/rsyncd.conf.5 2006-03-11 10:25:08.000000000 -0800 @@ -1,4 +1,4 @@ -.TH "rsyncd\&.conf" "5" "28 Jul 2005" "" "" +.TH "rsyncd\&.conf" "5" "11 Mar 2006" "" "" .SH "NAME" rsyncd\&.conf \- configuration file for rsync in daemon mode .SH "SYNOPSIS" @@ -134,7 +134,8 @@ sorts of socket options which may make transfers faster (or slower!)\&. Read the man page for the setsockopt() system call for details on some of the options you may be able to set\&. By default no -special socket options are set\&. +special socket options are set\&. These settings are superseded by the +\fB--sockopts\fP command-line option\&. .IP .PP .SH "MODULE OPTIONS" @@ -181,7 +182,7 @@ Note that you are free to setup user/group information in the chroot area differently from your normal system\&. For example, you could abbreviate the list of users and groups\&. Also, you can protect this information from -being downloaded/uploaded by adding an exclude rule to the rsync\&.conf file +being downloaded/uploaded by adding an exclude rule to the rsyncd\&.conf file (e\&.g\&. "exclude = /etc/**")\&. Note that having the exclusion affect uploads is a relatively new feature in rsync, so make sure your daemon is at least 2\&.6\&.3 to effect this\&. Also note that it is safest to exclude a @@ -293,6 +294,27 @@ \fB--include-from\fP option with a equivalent file\&. See the "exclude" option above\&. .IP +.IP "\fBincoming chmod\fP" +This option allows you to specify a set of +comma-separated chmod strings that will affect the permissions of all +incoming files (files that are being received by the daemon)\&. These +changes happen after all other permission calculations, and this will +even override destination-default and/or existing permissions when the +client does not specify \fB--perms\fP\&. +See the description of the \fB--chmod\fP rsync option and the \fBchmod\fP(1) +manpage for information on the format of this string\&. +.IP +.IP "\fBoutgoing chmod\fP" +This option allows you to specify a set of +comma-separated chmod strings that will affect the permissions of all +outgoing files (files that are being sent out from the daemon)\&. These +changes happen first, making the sent permissions appear to be different +than those stored in the filesystem itself\&. For instance, you could +disable group write permissions on the server while having it appear to +be on to the clients\&. +See the description of the \fB--chmod\fP rsync option and the \fBchmod\fP(1) +manpage for information on the format of this string\&. +.IP .IP "\fBauth users\fP" The "auth users" option specifies a comma and space-separated list of usernames that will be allowed to connect to @@ -301,7 +323,7 @@ "auth users" is set then the client will be challenged to supply a username and password to connect to the module\&. A challenge response authentication protocol is used for this exchange\&. The plain text -usernames are passwords are stored in the file specified by the +usernames and passwords are stored in the file specified by the "secrets file" option\&. The default is for all users to be able to connect without a password (this is called "anonymous rsync")\&. .IP @@ -435,38 +457,43 @@ .IP .RS .IP o -%h for the remote host name +%a the remote IP address .IP o -%a for the remote IP address +%b the number of bytes actually transferred .IP o -%l for the length of the file in bytes +%B the permission bits of the file (e\&.g\&. rwxrwxrwt) .IP o -%p for the process ID of this rsync session +%c the checksum bytes received for this file (only when sending) .IP o -%o for the operation, which is "send", "recv", or "del\&." -(the latter includes the trailing period) +%f the filename (long form on sender; no trailing "/") .IP o -%f for the filename (long form on sender; no trailing "/") +%G the gid of the file (decimal) or "DEFAULT" .IP o -%n for the filename (short form; trailing "/" on dir) +%h the remote host name .IP o -%L either the string " -> SYMLINK", or " => HARDLINK" or an -empty string (where \fBSYMLINK\fP or \fBHARDLINK\fP is a filename) +%i an itemized list of what is being updated .IP o -%P for the module path +%l the length of the file in bytes .IP o -%m for the module name +%L the string " -> SYMLINK", " => HARDLINK", or "" (where \fBSYMLINK\fP or \fBHARDLINK\fP is a filename) .IP o -%t for the current date time +%m the module name .IP o -%u for the authenticated username (or the null string) +%M the last-modified time of the file .IP o -%b for the number of bytes actually transferred +%n the filename (short form; trailing "/" on dir) .IP o -%c when sending files this gives the number of checksum bytes -received for this file +%o the operation, which is "send", "recv", or "del\&." (the latter includes the trailing period) .IP o -%i an itemized list of what is being updated +%p the process ID of this rsync session +.IP o +%P the module path +.IP o +%t the current date time +.IP o +%u the authenticated username or an empty string +.IP o +%U the uid of the file (decimal) .RE .IP For a list of what the characters mean that are output by "%i", see the @@ -522,15 +549,54 @@ .IP The default setting is \f(CW*\&.gz *\&.tgz *\&.zip *\&.z *\&.rpm *\&.deb *\&.iso *\&.bz2 *\&.tbz\fP .IP +.IP "\fBpre-xfer exec\fP, \fBpost-xfer exec\fP" +You may specify a command to be run +before and/or after the transfer\&. If the \fBpre-xfer exec\fP command fails, the +transfer is aborted before it begins\&. +.IP +The following environment variables will be set, though some are +specific to the pre-xfer or the post-xfer environment: +.IP +.RS +.IP o +\fBRSYNC_MODULE_NAME\fP: The name of the module being accessed\&. +.IP o +\fBRSYNC_MODULE_PATH\fP: The path configured for the module\&. +.IP o +\fBRSYNC_HOST_ADDR\fP: The accessing host\&'s IP address\&. +.IP o +\fBRSYNC_HOST_NAME\fP: The accessing host\&'s name\&. +.IP o +\fBRSYNC_USER_NAME\fP: The accessing user\&'s name (empty if no user)\&. +.IP o +\fBRSYNC_REQUEST\fP: (pre-xfer only) The module/path info specified +by the user (note that the user can specify multiple source files, +so the request can be something like "mod/path1 mod/path2", etc\&.)\&. +.IP o +\fBRSYNC_ARG#\fP: (pre-xfer only) The pre-request arguments are set +in these numbered values\&. RSYNC_ARG0 is always "rsyncd", and the last +value contains a single period\&. +.IP o +\fBRSYNC_EXIT_STATUS\fP: (post-xfer only) rsync\&'s exit value\&. This will be 0 for a +successful run, a positive value for an error that rsync returned +(e\&.g\&. 23=partial xfer), or a -1 if rsync failed to exit properly\&. +.IP o +\fBRSYNC_RAW_STATUS\fP: (post-xfer only) the raw exit value from waitpid()\&. +.RE +.IP +Even though the commands can be associated with a particular module, they +are run using the permissions of the user that started the daemon (not the +module\&'s uid/gid setting) without any chroot restrictions\&. +.IP .PP .SH "AUTHENTICATION STRENGTH" .PP The authentication protocol used in rsync is a 128 bit MD4 based -challenge response system\&. Although I believe that no one has ever -demonstrated a brute-force break of this sort of system you should -realize that this is not a "military strength" authentication system\&. -It should be good enough for most purposes but if you want really top -quality security then I recommend that you run rsync over ssh\&. +challenge response system\&. This is fairly weak protection, though (with +at least one brute-force hash-finding algorithm publicly available), so +if you want really top-quality security, then I recommend that you run +rsync over ssh\&. (Yes, a future version of rsync will switch over to a +stronger hashing method\&.) .PP Also note that the rsync daemon protocol does not currently provide any encryption of the data that is transferred over the connection\&. Only @@ -622,7 +688,7 @@ .PP .SH "VERSION" .PP -This man page is current for version 2\&.6\&.6 of rsync\&. +This man page is current for version 2\&.6\&.7 of rsync\&. .PP .SH "CREDITS" .PP diff -urN --exclude=patches rsync-2.6.6/rsyncd.conf.yo rsync-2.6.7/rsyncd.conf.yo --- rsync-2.6.6/rsyncd.conf.yo 2005-07-28 12:31:05.000000000 -0700 +++ rsync-2.6.7/rsyncd.conf.yo 2006-03-11 10:25:04.000000000 -0800 @@ -1,5 +1,5 @@ mailto(rsync-bugs@samba.org) -manpage(rsyncd.conf)(5)(28 Jul 2005)()() +manpage(rsyncd.conf)(5)(11 Mar 2006)()() manpagename(rsyncd.conf)(configuration file for rsync in daemon mode) manpagesynopsis() @@ -116,7 +116,8 @@ sorts of socket options which may make transfers faster (or slower!). Read the man page for the setsockopt() system call for details on some of the options you may be able to set. By default no -special socket options are set. +special socket options are set. These settings are superseded by the +bf(--sockopts) command-line option. enddit() @@ -163,7 +164,7 @@ Note that you are free to setup user/group information in the chroot area differently from your normal system. For example, you could abbreviate the list of users and groups. Also, you can protect this information from -being downloaded/uploaded by adding an exclude rule to the rsync.conf file +being downloaded/uploaded by adding an exclude rule to the rsyncd.conf file (e.g. "exclude = /etc/**"). Note that having the exclusion affect uploads is a relatively new feature in rsync, so make sure your daemon is at least 2.6.3 to effect this. Also note that it is safest to exclude a @@ -262,6 +263,25 @@ bf(--include-from) option with a equivalent file. See the "exclude" option above. +dit(bf(incoming chmod)) This option allows you to specify a set of +comma-separated chmod strings that will affect the permissions of all +incoming files (files that are being received by the daemon). These +changes happen after all other permission calculations, and this will +even override destination-default and/or existing permissions when the +client does not specify bf(--perms). +See the description of the bf(--chmod) rsync option and the bf(chmod)(1) +manpage for information on the format of this string. + +dit(bf(outgoing chmod)) This option allows you to specify a set of +comma-separated chmod strings that will affect the permissions of all +outgoing files (files that are being sent out from the daemon). These +changes happen first, making the sent permissions appear to be different +than those stored in the filesystem itself. For instance, you could +disable group write permissions on the server while having it appear to +be on to the clients. +See the description of the bf(--chmod) rsync option and the bf(chmod)(1) +manpage for information on the format of this string. + dit(bf(auth users)) The "auth users" option specifies a comma and space-separated list of usernames that will be allowed to connect to this module. The usernames do not need to exist on the local @@ -269,7 +289,7 @@ "auth users" is set then the client will be challenged to supply a username and password to connect to the module. A challenge response authentication protocol is used for this exchange. The plain text -usernames are passwords are stored in the file specified by the +usernames and passwords are stored in the file specified by the "secrets file" option. The default is for all users to be able to connect without a password (this is called "anonymous rsync"). @@ -386,24 +406,25 @@ The single-character escapes that are understood are as follows: quote(itemize( - it() %h for the remote host name - it() %a for the remote IP address - it() %l for the length of the file in bytes - it() %p for the process ID of this rsync session - it() %o for the operation, which is "send", "recv", or "del." - (the latter includes the trailing period) - it() %f for the filename (long form on sender; no trailing "/") - it() %n for the filename (short form; trailing "/" on dir) - it() %L either the string " -> SYMLINK", or " => HARDLINK" or an - empty string (where bf(SYMLINK) or bf(HARDLINK) is a filename) - it() %P for the module path - it() %m for the module name - it() %t for the current date time - it() %u for the authenticated username (or the null string) - it() %b for the number of bytes actually transferred - it() %c when sending files this gives the number of checksum bytes - received for this file + it() %a the remote IP address + it() %b the number of bytes actually transferred + it() %B the permission bits of the file (e.g. rwxrwxrwt) + it() %c the checksum bytes received for this file (only when sending) + it() %f the filename (long form on sender; no trailing "/") + it() %G the gid of the file (decimal) or "DEFAULT" + it() %h the remote host name it() %i an itemized list of what is being updated + it() %l the length of the file in bytes + it() %L the string " -> SYMLINK", " => HARDLINK", or "" (where bf(SYMLINK) or bf(HARDLINK) is a filename) + it() %m the module name + it() %M the last-modified time of the file + it() %n the filename (short form; trailing "/" on dir) + it() %o the operation, which is "send", "recv", or "del." (the latter includes the trailing period) + it() %p the process ID of this rsync session + it() %P the module path + it() %t the current date time + it() %u the authenticated username or an empty string + it() %U the uid of the file (decimal) )) For a list of what the characters mean that are output by "%i", see the @@ -454,16 +475,45 @@ The default setting is tt(*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz) +dit(bf(pre-xfer exec), bf(post-xfer exec)) You may specify a command to be run +before and/or after the transfer. If the bf(pre-xfer exec) command fails, the +transfer is aborted before it begins. + +The following environment variables will be set, though some are +specific to the pre-xfer or the post-xfer environment: + +quote(itemize( + it() bf(RSYNC_MODULE_NAME): The name of the module being accessed. + it() bf(RSYNC_MODULE_PATH): The path configured for the module. + it() bf(RSYNC_HOST_ADDR): The accessing host's IP address. + it() bf(RSYNC_HOST_NAME): The accessing host's name. + it() bf(RSYNC_USER_NAME): The accessing user's name (empty if no user). + it() bf(RSYNC_REQUEST): (pre-xfer only) The module/path info specified + by the user (note that the user can specify multiple source files, + so the request can be something like "mod/path1 mod/path2", etc.). + it() bf(RSYNC_ARG#): (pre-xfer only) The pre-request arguments are set + in these numbered values. RSYNC_ARG0 is always "rsyncd", and the last + value contains a single period. + it() bf(RSYNC_EXIT_STATUS): (post-xfer only) rsync's exit value. This will be 0 for a + successful run, a positive value for an error that rsync returned + (e.g. 23=partial xfer), or a -1 if rsync failed to exit properly. + it() bf(RSYNC_RAW_STATUS): (post-xfer only) the raw exit value from waitpid(). +)) + +Even though the commands can be associated with a particular module, they +are run using the permissions of the user that started the daemon (not the +module's uid/gid setting) without any chroot restrictions. + enddit() manpagesection(AUTHENTICATION STRENGTH) The authentication protocol used in rsync is a 128 bit MD4 based -challenge response system. Although I believe that no one has ever -demonstrated a brute-force break of this sort of system you should -realize that this is not a "military strength" authentication system. -It should be good enough for most purposes but if you want really top -quality security then I recommend that you run rsync over ssh. +challenge response system. This is fairly weak protection, though (with +at least one brute-force hash-finding algorithm publicly available), so +if you want really top-quality security, then I recommend that you run +rsync over ssh. (Yes, a future version of rsync will switch over to a +stronger hashing method.) Also note that the rsync daemon protocol does not currently provide any encryption of the data that is transferred over the connection. Only @@ -541,7 +591,7 @@ manpagesection(VERSION) -This man page is current for version 2.6.6 of rsync. +This man page is current for version 2.6.7 of rsync. manpagesection(CREDITS) diff -urN --exclude=patches rsync-2.6.6/runtests.sh rsync-2.6.7/runtests.sh --- rsync-2.6.6/runtests.sh 2005-02-20 13:04:03.000000000 -0800 +++ rsync-2.6.7/runtests.sh 2006-02-04 11:44:19.000000000 -0800 @@ -85,7 +85,7 @@ # they're explicitly given on the command line. # Also, we can't count on 'cp -a' or 'mkdir -p', although they're -# pretty handy. +# pretty handy (see function makepath for the latter). # I think some of the GNU documentation suggests that we shouldn't # rely on shell functions. However, the Bash manual seems to say that @@ -100,6 +100,8 @@ # You cannot do "export VAR=VALUE" all on one line; the export must be # separate from the assignment. (SCO SysV) +# Don't rely on grep -q, as that doesn't work everywhere -- just redirect +# stdout to /dev/null to keep it quiet. # STILL TO DO: @@ -124,6 +126,7 @@ . "./shconfig" RUNSHFLAGS='-e' +export RUNSHFLAGS # for Solaris [ -d /usr/xpg4/bin ] && PATH="/usr/xpg4/bin/:$PATH" @@ -160,6 +163,12 @@ echo " preserve_scratch=no" fi +# We'll use setfacl if it's around and it supports the -k option. +if setfacl --help 2>/dev/null | grep ' -k,' >/dev/null; then + setfacl=setfacl +else + setfacl=true +fi if [ ! -f "$rsync_bin" ]; then echo "rsync_bin $rsync_bin is not a file" >&2 @@ -194,6 +203,9 @@ prep_scratch() { [ -d "$scratchdir" ] && rm -rf "$scratchdir" mkdir "$scratchdir" + # Get rid of default ACLs and dir-setgid to avoid confusing some tests. + $setfacl -k "$scratchdir" + chmod g-s "$scratchdir" return 0 } diff -urN --exclude=patches rsync-2.6.6/sender.c rsync-2.6.7/sender.c --- rsync-2.6.6/sender.c 2005-05-19 01:52:24.000000000 -0700 +++ rsync-2.6.7/sender.c 2006-01-14 12:26:24.000000000 -0800 @@ -27,6 +27,7 @@ extern int log_format_has_i; extern int daemon_log_format_has_i; extern int csum_length; +extern int append_mode; extern int io_error; extern int allowed_lull; extern int protocol_version; @@ -72,6 +73,13 @@ (double)s->count, (long)s->blength, (long)s->remainder); } + if (append_mode) { + s->flength = (OFF_T)s->count * s->blength; + if (s->remainder) + s->flength -= s->blength - s->remainder; + return s; + } + if (s->count == 0) return(s); @@ -125,11 +133,9 @@ file->dir.root, "/", NULL); } else offset = 0; - f_name_to(file, fname + offset); - if (remove_sent_files && do_unlink(fname) == 0 && verbose > 1) { - rprintf(FINFO, "sender removed %s\n", - safe_fname(fname + offset)); - } + f_name(file, fname + offset); + if (remove_sent_files && do_unlink(fname) == 0 && verbose > 1) + rprintf(FINFO, "sender removed %s\n", fname + offset); } static void write_ndx_and_attrs(int f_out, int ndx, int iflags, @@ -231,6 +237,7 @@ /* For inplace: redo phase turns off the backup * flag so that we do a regular inplace send. */ make_backups = 0; + append_mode = 0; continue; } @@ -247,7 +254,7 @@ fname[offset++] = '/'; } else offset = 0; - fname2 = f_name_to(file, fname + offset); + fname2 = f_name(file, fname + offset); if (verbose > 2) rprintf(FINFO, "send_files(%d, %s)\n", i, fname); @@ -322,22 +329,20 @@ if (verbose > 2) { rprintf(FINFO, "send_files mapped %s of size %.0f\n", - safe_fname(fname), (double)st.st_size); + fname, (double)st.st_size); } write_ndx_and_attrs(f_out, i, iflags, fnamecmp_type, xname, xlen); write_sum_head(f_xfer, s); - if (verbose > 2) { - rprintf(FINFO, "calling match_sums %s\n", - safe_fname(fname)); - } + if (verbose > 2) + rprintf(FINFO, "calling match_sums %s\n", fname); if (log_before_transfer) log_item(file, &initial_stats, iflags, NULL); else if (!am_server && verbose && do_progress) - rprintf(FINFO, "%s\n", safe_fname(fname2)); + rprintf(FINFO, "%s\n", fname2); set_compression(fname); @@ -361,10 +366,8 @@ free_sums(s); - if (verbose > 2) { - rprintf(FINFO, "sender finished %s\n", - safe_fname(fname)); - } + if (verbose > 2) + rprintf(FINFO, "sender finished %s\n", fname); /* Flag that we actually sent this entry. */ file->flags |= FLAG_SENT; diff -urN --exclude=patches rsync-2.6.6/socket.c rsync-2.6.7/socket.c --- rsync-2.6.6/socket.c 2005-04-14 09:08:12.000000000 -0700 +++ rsync-2.6.7/socket.c 2006-03-07 00:46:29.000000000 -0800 @@ -36,6 +36,10 @@ extern char *bind_address; extern int default_af_hint; +#ifdef HAVE_SIGACTION +static struct sigaction sigact; +#endif + /** * Establish a proxy connection on an open socket to a web proxy by * using the CONNECT method. If proxy_user and proxy_pass are not NULL, @@ -54,13 +58,13 @@ proxy_user, ":", proxy_pass, NULL); len = strlen(buffer); - if ((len*8 + 5) / 6 >= (int)sizeof authbuf) { + if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) { rprintf(FERROR, "authentication information is too long\n"); return -1; } - base64_encode(buffer, len, authbuf); + base64_encode(buffer, len, authbuf, 1); authhdr = "\r\nProxy-Authorization: Basic "; } else { *authbuf = '\0'; @@ -433,7 +437,9 @@ #ifdef WNOHANG while (waitpid(-1, NULL, WNOHANG) > 0) {} #endif +#ifndef HAVE_SIGACTION signal(SIGCHLD, sigchld_handler); +#endif } @@ -442,6 +448,10 @@ fd_set deffds; int *sp, maxfd, i; +#ifdef HAVE_SIGACTION + sigact.sa_flags = SA_NOCLDSTOP; +#endif + /* open an incoming socket */ sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint); if (sp == NULL) @@ -465,7 +475,6 @@ maxfd = sp[i]; } - /* now accept incoming connections - forking a new process * for each incoming connection */ while (1) { @@ -500,7 +509,7 @@ if (fd < 0) continue; - signal(SIGCHLD, sigchld_handler); + SIGACTION(SIGCHLD, sigchld_handler); if ((pid = fork()) == 0) { int ret; diff -urN --exclude=patches rsync-2.6.6/support/files-to-excludes rsync-2.6.7/support/files-to-excludes --- rsync-2.6.6/support/files-to-excludes 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.7/support/files-to-excludes 2006-01-30 13:52:17.000000000 -0800 @@ -0,0 +1,27 @@ +#!/usr/bin/perl +# This script takes an input of filenames and outputs a set of +# include/exclude directives that can be used by rsync to copy +# just the indicated files using an --exclude-from=FILE option. +use strict; + +my %hash; + +while (<>) { + chomp; + s#^/+##; + my $path = '/'; + while (m#([^/]+/)/*#g) { + $path .= $1; + print "+ $path\n" unless $hash{$path}++; + } + if (m#([^/]+)$#) { + print "+ $path$1\n"; + } else { + delete $hash{$path}; + } +} + +foreach (sort keys %hash) { + print "- $_*\n"; +} +print "- /*\n"; diff -urN --exclude=patches rsync-2.6.6/support/logfilter rsync-2.6.7/support/logfilter --- rsync-2.6.6/support/logfilter 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.7/support/logfilter 2005-10-03 21:12:28.000000000 -0700 @@ -0,0 +1,34 @@ +#!/usr/bin/perl +# Filter the rsync daemon log messages by module name. The log file can be +# in either syslog format or rsync's own log-file format. Note that the +# MODULE_NAME parameter is used in a regular-expression match in order to +# allow regex wildcards to be used. You can also limit the output by +# directory hierarchy in a module. Examples: +# +# logfilter foo /var/log/rsyncd.log # output lines for module foo +# logfilter foo/dir /var/log/syslog # limit lines to those in dir of foo + +use strict; + +my $match = shift; +die "Usage: logfilter MODULE_NAME [LOGFILE ...]\n" unless defined $match; + +my $syslog_prefix = '\w\w\w +\d+ \d\d:\d\d:\d\d \S+ rsyncd'; +my $rsyncd_prefix = '\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d '; + +my %pids; + +while (<>) { + my($pid,$msg) = /^(?:$syslog_prefix|$rsyncd_prefix)\[(\d+)\]:? (.*)/o; + next unless defined $pid; + my($mod_spec) = $msg =~ /^rsync (?:on|to) (\S+) from /; + if (defined $mod_spec) { + if ($mod_spec =~ /^$match(\/\S*)?$/o) { + $pids{$pid} = 1; + } else { + delete $pids{$pid}; + } + } + next unless $pids{$pid}; + print $_; +} diff -urN --exclude=patches rsync-2.6.6/support/rsyncstats rsync-2.6.7/support/rsyncstats --- rsync-2.6.6/support/rsyncstats 2005-02-19 11:36:54.000000000 -0800 +++ rsync-2.6.7/support/rsyncstats 2006-02-02 02:20:13.000000000 -0800 @@ -12,7 +12,7 @@ use Getopt::Long; # You may wish to edit the next line to customize for your default log file. -$usage_file = "/var/adm/rsyncd.log"; +$usage_file = "/var/log/rsyncd.log"; # Edit the following lines for default report settings. # Entries defined here will be over-ridden by the command line. diff -urN --exclude=patches rsync-2.6.6/support/savetransfer.c rsync-2.6.7/support/savetransfer.c --- rsync-2.6.6/support/savetransfer.c 2005-02-13 18:34:01.000000000 -0800 +++ rsync-2.6.7/support/savetransfer.c 2005-12-19 08:57:55.000000000 -0800 @@ -7,20 +7,13 @@ * -o Save the output coming from PROGRAM to the OUTPUT_FILE * * If you want to capture the flow of data for an rsync command, use one of - * the following commands (the first two are push commands, the last two are - * pull commands): + * the following commands (the resulting files should be identical): * - * rsync -av --rsh="savetransfer -i /tmp/from.sender ssh" - * --rsync-path="savetransfer -i /tmp/to.receiver rsync" FILES HOST:DEST + * rsync -av --rsh="savetransfer -i /tmp/to.server ssh" + * --rsync-path="savetransfer -i /tmp/from.client rsync" SOURCE DEST * - * rsync -av --rsh="savetransfer -o /tmp/to.sender ssh" - * --rsync-path="savetransfer -o /tmp/from.generator rsync" FILES HOST:DEST - * - * rsync -av --rsh="savetransfer -i /tmp/from.generator ssh" - * --rsync-path="savetransfer -i /tmp/to.sender rsync" HOST:FILES DEST - * - * rsync -av --rsh="savetransfer -o /tmp/to.receiver ssh" - * --rsync-path="savetransfer -o /tmp/from.sender rsync" HOST:FILES DEST + * rsync -av --rsh="savetransfer -o /tmp/from.server ssh" + * --rsync-path="savetransfer -o /tmp/to.client rsync" SOURCE DEST * * Note that this program aborts after 30 seconds of inactivity, so you'll need * to change it if that is not enough dead time for your transfer. Also, some diff -urN --exclude=patches rsync-2.6.6/syscall.c rsync-2.6.7/syscall.c --- rsync-2.6.6/syscall.c 2005-04-05 19:08:21.000000000 -0700 +++ rsync-2.6.7/syscall.c 2006-01-29 10:52:53.000000000 -0800 @@ -45,14 +45,14 @@ #define RETURN_ERROR_IF_RO_OR_LO RETURN_ERROR_IF(read_only || list_only, EROFS) -int do_unlink(char *fname) +int do_unlink(const char *fname) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; return unlink(fname); } -int do_symlink(char *fname1, char *fname2) +int do_symlink(const char *fname1, const char *fname2) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; @@ -60,7 +60,7 @@ } #ifdef HAVE_LINK -int do_link(char *fname1, char *fname2) +int do_link(const char *fname1, const char *fname2) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; @@ -104,7 +104,11 @@ || (bind(sock, (struct sockaddr*)&saddr, sizeof saddr)) < 0) return -1; close(sock); +#ifdef HAVE_CHMOD return do_chmod(pathname, mode); +#else + return 0; +#endif } #endif #ifdef HAVE_MKNOD @@ -114,14 +118,14 @@ #endif } -int do_rmdir(char *pathname) +int do_rmdir(const char *pathname) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; return rmdir(pathname); } -int do_open(char *pathname, int flags, mode_t mode) +int do_open(const char *pathname, int flags, mode_t mode) { if (flags != O_RDONLY) { RETURN_ERROR_IF(dry_run, 0); @@ -137,21 +141,27 @@ int code; if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; - code = chmod(path, mode); + if (S_ISLNK(mode)) { +#ifdef HAVE_LCHMOD + code = lchmod(path, mode & CHMOD_BITS); +#else + code = 1; +#endif + } else + code = chmod(path, mode & CHMOD_BITS); if (code != 0 && preserve_perms) return code; return 0; } #endif -int do_rename(char *fname1, char *fname2) +int do_rename(const char *fname1, const char *fname2) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; return rename(fname1, fname2); } - void trim_trailing_slashes(char *name) { int l; @@ -170,7 +180,6 @@ } } - int do_mkdir(char *fname, mode_t mode) { if (dry_run) return 0; @@ -179,7 +188,6 @@ return mkdir(fname, mode); } - /* like mkstemp but forces permissions */ int do_mkstemp(char *template, mode_t perms) { @@ -244,7 +252,11 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence) { #ifdef HAVE_LSEEK64 +#if !SIZEOF_OFF64_T + OFF_T lseek64(); +#else off64_t lseek64(); +#endif return lseek64(fd, offset, whence); #else return lseek(fd, offset, whence); diff -urN --exclude=patches rsync-2.6.6/t_stub.c rsync-2.6.7/t_stub.c --- rsync-2.6.6/t_stub.c 2005-01-25 02:39:14.000000000 -0800 +++ rsync-2.6.7/t_stub.c 2006-02-23 17:56:29.000000000 -0800 @@ -28,6 +28,9 @@ int modify_window = 0; int module_id = -1; +int relative_paths = 0; +int human_readable = 0; +mode_t orig_umask = 002; char *partial_dir; struct filter_list_struct server_filter_list; diff -urN --exclude=patches rsync-2.6.6/testsuite/batch-mode.test rsync-2.6.7/testsuite/batch-mode.test --- rsync-2.6.6/testsuite/batch-mode.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/batch-mode.test 2006-02-03 09:58:48.000000000 -0800 @@ -9,8 +9,6 @@ . "$suitedir/rsync.fns" -set -x - hands_setup cd "$tmpdir" diff -urN --exclude=patches rsync-2.6.6/testsuite/chgrp.test rsync-2.6.7/testsuite/chgrp.test --- rsync-2.6.6/testsuite/chgrp.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/chgrp.test 2006-02-03 09:58:48.000000000 -0800 @@ -11,8 +11,6 @@ . "$suitedir/rsync.fns" -set -x - # Build some hardlinks mygrps="`rsync_getgroups`" || fail "Can't get groups" diff -urN --exclude=patches rsync-2.6.6/testsuite/chmod-option.test rsync-2.6.7/testsuite/chmod-option.test --- rsync-2.6.6/testsuite/chmod-option.test 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.7/testsuite/chmod-option.test 2006-02-03 09:58:48.000000000 -0800 @@ -0,0 +1,42 @@ +#! /bin/sh + +# Copyright (C) 2002 by Martin Pool + +# This program is distributable under the terms of the GNU GPL (see +# COPYING). + +# Test that the --chmod option functions correctly. + +. $srcdir/testsuite/rsync.fns + +# Build some files + +fromdir="$scratchdir/from" +todir="$scratchdir/to" +checkdir="$scratchdir/check" + +mkdir "$fromdir" +name1="$fromdir/name1" +name2="$fromdir/name2" +dir1="$fromdir/dir1" +dir2="$fromdir/dir2" +echo "This is the file" > "$name1" +echo "This is the other file" > "$name2" +mkdir "$dir1" "$dir2" + +chmod 4700 "$name1" || test_skipped "Can't chmod" +chmod 700 "$dir1" +chmod 770 "$dir2" + +# Copy the files we've created over to another directory +checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir" + +# And then manually make the changes which should occur +umask 002 +chmod ug-s,a+rX "$checkdir"/* +chmod +w "$checkdir" "$checkdir"/dir* + +checkit "$RSYNC -avv --chmod ug-s,a+rX,D+w \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir" + +# The script would have aborted on error, so getting here means we've won. +exit 0 diff -urN --exclude=patches rsync-2.6.6/testsuite/chmod-temp-dir.test rsync-2.6.7/testsuite/chmod-temp-dir.test --- rsync-2.6.6/testsuite/chmod-temp-dir.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/chmod-temp-dir.test 2006-02-04 11:29:13.000000000 -0800 @@ -2,7 +2,7 @@ # Copyright (C) 2004 by Wayne Davison -# This program is distributable under the terms of the GNU GPL see +# This program is distributable under the terms of the GNU GPL (see # COPYING). # Test that various read-only and set[ug]id permissions work properly, @@ -11,8 +11,6 @@ . "$suitedir/rsync.fns" -set -x - hands_setup tmpdir2=/tmp diff -urN --exclude=patches rsync-2.6.6/testsuite/chmod.test rsync-2.6.7/testsuite/chmod.test --- rsync-2.6.6/testsuite/chmod.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/chmod.test 2006-02-04 11:29:13.000000000 -0800 @@ -2,7 +2,7 @@ # Copyright (C) 2004 by Wayne Davison -# This program is distributable under the terms of the GNU GPL see +# This program is distributable under the terms of the GNU GPL (see # COPYING). # Test that various read-only and set[ug]id permissions work properly, @@ -11,8 +11,6 @@ . "$suitedir/rsync.fns" -set -x - hands_setup chmod 440 "$fromdir/text" diff -urN --exclude=patches rsync-2.6.6/testsuite/chown.test rsync-2.6.7/testsuite/chown.test --- rsync-2.6.6/testsuite/chown.test 2005-03-17 18:10:34.000000000 -0800 +++ rsync-2.6.7/testsuite/chown.test 2006-02-03 09:58:48.000000000 -0800 @@ -13,8 +13,6 @@ . "$suitedir/rsync.fns" -set -x - case `id -u` in '') ;; # If "id" failed, try to continue... 0) ;; diff -urN --exclude=patches rsync-2.6.6/testsuite/compare-dest.test rsync-2.6.7/testsuite/compare-dest.test --- rsync-2.6.6/testsuite/compare-dest.test 2005-03-11 09:36:05.000000000 -0800 +++ rsync-2.6.7/testsuite/compare-dest.test 2006-02-04 11:29:13.000000000 -0800 @@ -2,7 +2,7 @@ # Copyright (C) 2004 by Wayne Davison -# This program is distributable under the terms of the GNU GPL see +# This program is distributable under the terms of the GNU GPL (see # COPYING). # Test rsync handling of the --compare-dest option. @@ -14,8 +14,6 @@ # Build some files/dirs/links to copy -set -x - hands_setup # Setup the alt and chk dirs diff -urN --exclude=patches rsync-2.6.6/testsuite/daemon-gzip-download.test rsync-2.6.7/testsuite/daemon-gzip-download.test --- rsync-2.6.6/testsuite/daemon-gzip-download.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/daemon-gzip-download.test 2006-02-03 09:58:48.000000000 -0800 @@ -26,8 +26,6 @@ RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" export RSYNC_CONNECT_PROG -set -x - hands_setup # Build chkdir with a normal rsync and an --exclude. diff -urN --exclude=patches rsync-2.6.6/testsuite/daemon-gzip-upload.test rsync-2.6.7/testsuite/daemon-gzip-upload.test --- rsync-2.6.6/testsuite/daemon-gzip-upload.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/daemon-gzip-upload.test 2006-02-03 09:58:48.000000000 -0800 @@ -20,8 +20,6 @@ RSYNC_CONNECT_PROG="$RSYNC --config=$conf --daemon" export RSYNC_CONNECT_PROG -set -x - hands_setup # Build chkdir with a normal rsync and an --exclude. diff -urN --exclude=patches rsync-2.6.6/testsuite/delete.test rsync-2.6.7/testsuite/delete.test --- rsync-2.6.6/testsuite/delete.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/delete.test 2006-02-04 11:29:13.000000000 -0800 @@ -2,15 +2,13 @@ # Copyright (C) 2005 by Wayne Davison -# This program is distributable under the terms of the GNU GPL see +# This program is distributable under the terms of the GNU GPL (see # COPYING). # Test rsync handling of various delete directives. . "$suitedir/rsync.fns" -set -x - hands_setup makepath "$chkdir" diff -urN --exclude=patches rsync-2.6.6/testsuite/devices.test rsync-2.6.7/testsuite/devices.test --- rsync-2.6.6/testsuite/devices.test 2005-03-17 18:10:34.000000000 -0800 +++ rsync-2.6.7/testsuite/devices.test 2006-02-08 17:17:01.000000000 -0800 @@ -9,6 +9,9 @@ . "$suitedir/rsync.fns" +chkfile="$scratchdir/rsync.chk" +outfile="$scratchdir/rsync.out" + # Build some hardlinks case `id -u` in @@ -16,7 +19,7 @@ 0) ;; *) if [ -f /usr/bin/fakeroot ]; then echo "Let's try re-running the script under fakeroot..." - exec /usr/bin/fakeroot /bin/sh "$0" + exec /usr/bin/fakeroot /bin/sh $RUNSHFLAGS "$0" fi test_skipped "Rsync won't copy devices unless we're root" ;; @@ -25,15 +28,66 @@ # TODO: Need to test whether hardlinks are possible on this OS/filesystem mkdir "$fromdir" +mkdir "$todir" mknod "$fromdir/char" c 41 67 || test_skipped "Can't create char device node unless root" mknod "$fromdir/char2" c 42 68 || test_skipped "Can't create char device node unless root" mknod "$fromdir/char3" c 42 69 || test_skipped "Can't create char device node unless root" mknod "$fromdir/block" b 42 69 || test_skipped "Can't create block device node unless root" mknod "$fromdir/block2" b 42 73 || test_skipped "Can't create block device node unless root" mknod "$fromdir/block3" b 105 73 || test_skipped "Can't create block device node unless root" +ln "$fromdir/block3" "$fromdir/block2.5" || echo "Skipping hard-linked device test..." mkfifo "$fromdir/fifo" || test_skipped "Can't run mkfifo" +touch -r "$fromdir/block" "$fromdir/block2" -checkit "$RSYNC -aHvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir" skip_file_diff +$RSYNC -ai "$fromdir/block" "$todir/block2" \ + | tee "$outfile" +cat <"$chkfile" +cD+++++++ block +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 1 failed" + +$RSYNC -ai "$fromdir/block2" "$todir/block" \ + | tee "$outfile" +cat <"$chkfile" +cD+++++++ block2 +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 2 failed" + +sleep 1 + +$RSYNC -Di "$fromdir/block3" "$todir/block" \ + | tee "$outfile" +cat <"$chkfile" +cD..T.... block3 +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed" + +$RSYNC -aiHvv "$fromdir/" "$todir/" \ + | tee "$outfile" +filter_outfile +cat <"$chkfile" +.d..t.... ./ +cD..t.... block +cD....... block2 +cD+++++++ block3 +hD+++++++ block2.5 => block3 +cD+++++++ char +cD+++++++ char2 +cD+++++++ char3 +cS+++++++ fifo +EOT +if test ! -b "$fromdir/block2.5"; then + sed -e '/block2\.5/d' \ + <"$chkfile" >"$chkfile.new" + mv "$chkfile.new" "$chkfile" +fi +diff $diffopt "$chkfile" "$outfile" || test_fail "test 4 failed" + +echo "check how the directory listings compare with diff:" +echo "" +( cd "$fromdir" && rsync_ls_lR . ) > "$tmpdir/ls-from" +( cd "$todir" && rsync_ls_lR . ) > "$tmpdir/ls-to" +diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" # The script would have aborted on error, so getting here means we've won. exit 0 diff -urN --exclude=patches rsync-2.6.6/testsuite/dir-sgid.test rsync-2.6.7/testsuite/dir-sgid.test --- rsync-2.6.6/testsuite/dir-sgid.test 1969-12-31 16:00:00.000000000 -0800 +++ rsync-2.6.7/testsuite/dir-sgid.test 2006-02-04 11:42:47.000000000 -0800 @@ -0,0 +1,41 @@ +#! /bin/sh + +# This program is distributable under the terms of the GNU GPL (see +# COPYING). + +# Test that rsync obeys directory setgid. -- Matt McCutchen + +. $srcdir/testsuite/rsync.fns + +umask 077 + +# Call as: testit +testit() { + todir="$scratchdir/$1" + mkdir "$todir" + chmod $2 "$todir" + # Make sure we obey directory setgid when creating a directory to hold multiple transferred files, + # even though the directory itself is outside the transfer + $RSYNC -rvv "$scratchdir/dir" "$scratchdir/file" "$scratchdir/program" "$todir/to/" + check_perms "$todir/to" $5 "Target $1" + check_perms "$todir/to/dir" $5 "Target $1" + check_perms "$todir/to/file" $3 "Target $1" + check_perms "$todir/to/program" $4 "Target $1" +} + +echo "File!" >"$scratchdir/file" +echo "#!/bin/sh" >"$scratchdir/program" +mkdir "$scratchdir/dir" +chmod 2764 "$scratchdir/dir" || test_skipped "Can't chmod" +chmod 664 "$scratchdir/file" +chmod 775 "$scratchdir/program" +[ -g "$scratchdir/dir" ] || test_skipped "The directory setgid bit vanished!" +mkdir "$scratchdir/dir/blah" +[ -g "$scratchdir/dir/blah" ] || test_skipped "Your filesystem doesn't use directory setgid; maybe it's BSD." + +# Test some target directories +testit setgid-off 700 rw------- rwx------ rwx------ +testit setgid-on 2700 rw------- rwx------ rwx--S--- + +# Hooray +exit 0 diff -urN --exclude=patches rsync-2.6.6/testsuite/duplicates.test rsync-2.6.7/testsuite/duplicates.test --- rsync-2.6.6/testsuite/duplicates.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/duplicates.test 2006-02-04 11:29:13.000000000 -0800 @@ -2,7 +2,7 @@ # Copyright (C) 2002 by Martin Pool -# This program is distributable under the terms of the GNU GPL see +# This program is distributable under the terms of the GNU GPL (see # COPYING). # Test rsync handling of duplicate filenames. @@ -21,8 +21,6 @@ . "$suitedir/rsync.fns" -set -x - # Build some hardlinks mkdir "$fromdir" diff -urN --exclude=patches rsync-2.6.6/testsuite/exclude.test rsync-2.6.7/testsuite/exclude.test --- rsync-2.6.6/testsuite/exclude.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/exclude.test 2006-02-04 11:29:13.000000000 -0800 @@ -2,7 +2,7 @@ # Copyright (C) 2003, 2004, 2005 by Wayne Davison -# This program is distributable under the terms of the GNU GPL see +# This program is distributable under the terms of the GNU GPL (see # COPYING). # Test rsync handling of exclude/include directives. @@ -12,18 +12,18 @@ . "$suitedir/rsync.fns" -HOME="$scratchdir" CVSIGNORE='*.junk' -export HOME CVSIGNORE - -set -x +export CVSIGNORE # Build some files/dirs/links to copy makepath "$fromdir/foo/down/to/you" +makepath "$fromdir/foo/sub" makepath "$fromdir/bar/down/to/foo/too" makepath "$fromdir/bar/down/to/bar/baz" makepath "$fromdir/mid/for/foo/and/that/is/who" +makepath "$fromdir/new/keep/this" +makepath "$fromdir/new/lose/this" cat >"$fromdir/.filt" <"$fromdir/foo/file1" +echo filtered-1 >"$fromdir/foo/file1" echo removed >"$fromdir/foo/file2" echo cvsout >"$fromdir/foo/file2.old" cat >"$fromdir/foo/.filt" <"$fromdir/foo/sub/file1" cat >"$fromdir/bar/.filt" <"$fromdir/1" +#!/bin/sh +echo 'Program One!' +EOF +cat <"$fromdir/2" +#!/bin/sh +echo 'Program Two!' +EOF + +chmod 1700 "$fromdir/1" || test_skipped "Can't chmod" +chmod 600 "$fromdir/2" + +$RSYNC -rvv "$fromdir/" "$todir/" + +check_perms "$todir/1" rwx------ 1 +check_perms "$todir/2" rw------- 1 + +# Mix up the permissions a bit +chmod 600 "$fromdir/1" +chmod 601 "$fromdir/2" +chmod 604 "$todir/2" + +$RSYNC -rvv "$fromdir/" "$todir/" + +# No -E, so nothing should have changed +check_perms "$todir/1" rwx------ 2 +check_perms "$todir/2" rw----r-- 2 + +$RSYNC -rvvE "$fromdir/" "$todir/" + +# Now things should have happened! +check_perms "$todir/1" rw------- 3 +check_perms "$todir/2" rwx---r-x 3 + +# Hooray +exit 0 diff -urN --exclude=patches rsync-2.6.6/testsuite/fuzzy.test rsync-2.6.7/testsuite/fuzzy.test --- rsync-2.6.6/testsuite/fuzzy.test 2005-03-14 14:17:25.000000000 -0800 +++ rsync-2.6.7/testsuite/fuzzy.test 2006-02-04 11:29:13.000000000 -0800 @@ -2,15 +2,13 @@ # Copyright (C) 2005 by Wayne Davison -# This program is distributable under the terms of the GNU GPL see +# This program is distributable under the terms of the GNU GPL (see # COPYING). # Test rsync handling of the --fuzzy option. . "$suitedir/rsync.fns" -set -x - mkdir "$fromdir" mkdir "$todir" diff -urN --exclude=patches rsync-2.6.6/testsuite/hardlinks.test rsync-2.6.7/testsuite/hardlinks.test --- rsync-2.6.6/testsuite/hardlinks.test 2005-04-26 09:25:01.000000000 -0700 +++ rsync-2.6.7/testsuite/hardlinks.test 2006-02-03 09:58:48.000000000 -0800 @@ -12,8 +12,6 @@ . "$suitedir/rsync.fns" -set -x - # Build some hardlinks fromdir="$scratchdir/from" diff -urN --exclude=patches rsync-2.6.6/testsuite/itemize.test rsync-2.6.7/testsuite/itemize.test --- rsync-2.6.6/testsuite/itemize.test 2005-06-23 19:14:12.000000000 -0700 +++ rsync-2.6.7/testsuite/itemize.test 2006-02-08 17:17:01.000000000 -0800 @@ -2,7 +2,7 @@ # Copyright (C) 2005 by Wayne Davison -# This program is distributable under the terms of the GNU GPL see +# This program is distributable under the terms of the GNU GPL (see # COPYING). # Test the output of various copy commands to ensure itemized output @@ -10,26 +10,11 @@ . "$suitedir/rsync.fns" -set -x - lddir="$tmpdir/ld" chkfile="$scratchdir/rsync.chk" outfile="$scratchdir/rsync.out" -# This is only called if rsync was run with -v or -vv (not -i alone). -# Higher levels of -v would require more filtering. -filter_outfile() { - sed -e '/^building file list /d' \ - -e '/^created directory /d' \ - -e '/^done$/d' \ - -e '/ --whole-file$/d' \ - -e '/^total: /d' \ - -e '/^$/,$d' \ - <"$outfile" >"$outfile.new" - mv "$outfile.new" "$outfile" -} - makepath "$fromdir/foo" makepath "$fromdir/bar/baz" cp -p "$srcdir/configure.in" "$fromdir/foo/config1" @@ -152,27 +137,135 @@ .d..t.... ./ cd+++++++ bar/ cd+++++++ bar/baz/ -cf....... bar/baz/rsync +cf bar/baz/rsync cd+++++++ foo/ -cf....... foo/config1 -cf....... foo/config2 -hf+++++++ foo/extra => foo/config1 -cL+++++++ foo/sym -> ../bar/baz/rsync +cf foo/config1 +cf foo/config2 +hf foo/extra => foo/config1 +cL..T.... foo/sym -> ../bar/baz/rsync EOT diff $diffopt "$chkfile" "$outfile" || test_fail "test 8 failed" rm -rf "$todir" -$RSYNC -iplrtH --link-dest="$lddir" "$fromdir/" "$todir/" \ +$RSYNC -iplrtH --copy-dest="$lddir" "$fromdir/" "$todir/" \ | tee "$outfile" cat <"$chkfile" .d..t.... ./ cd+++++++ bar/ cd+++++++ bar/baz/ cd+++++++ foo/ -hf+++++++ foo/extra => foo/config1 -cL+++++++ foo/sym -> ../bar/baz/rsync +hf foo/extra => foo/config1 EOT diff $diffopt "$chkfile" "$outfile" || test_fail "test 9 failed" +rm -rf "$todir" +$RSYNC -vvplrtH --copy-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" +filter_outfile +cat <"$chkfile" +./ +bar/ +bar/baz/ +bar/baz/rsync is uptodate +foo/ +foo/config1 is uptodate +foo/config2 is uptodate +"foo/extra" is a hard link +foo/extra => foo/config1 +foo/sym is uptodate +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 10 failed" + +rm -rf "$todir" +$RSYNC -ivvplrtH --link-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" +filter_outfile +cat <"$chkfile" +.d..t.... ./ +cd+++++++ bar/ +cd+++++++ bar/baz/ +hf bar/baz/rsync +cd+++++++ foo/ +hf foo/config1 +hf foo/config2 +hf foo/extra => foo/config1 +hL foo/sym -> ../bar/baz/rsync +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 11 failed" + +rm -rf "$todir" +$RSYNC -iplrtH --link-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" +cat <"$chkfile" +.d..t.... ./ +cd+++++++ bar/ +cd+++++++ bar/baz/ +cd+++++++ foo/ +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 12 failed" + +rm -rf "$todir" +$RSYNC -vvplrtH --link-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" +filter_outfile +cat <"$chkfile" +./ +bar/ +bar/baz/ +bar/baz/rsync is uptodate +foo/ +foo/config1 is uptodate +foo/config2 is uptodate +"foo/extra" is a hard link +foo/sym is uptodate +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 13 failed" + +rm -rf "$todir" +$RSYNC -ivvplrtH --compare-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" +filter_outfile +# TODO fix really-old problem when combining -H with --compare-dest: +# missing output for foo/extra hard-link (and it might not be updated)! +cat <"$chkfile" +.d..t.... ./ +cd+++++++ bar/ +cd+++++++ bar/baz/ +.f bar/baz/rsync +cd+++++++ foo/ +.f foo/config1 +.f foo/config2 +.L foo/sym -> ../bar/baz/rsync +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 14 failed" + +rm -rf "$todir" +$RSYNC -iplrtH --compare-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" +cat <"$chkfile" +.d..t.... ./ +cd+++++++ bar/ +cd+++++++ bar/baz/ +cd+++++++ foo/ +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 15 failed" + +rm -rf "$todir" +$RSYNC -vvplrtH --compare-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" +filter_outfile +cat <"$chkfile" +./ +bar/ +bar/baz/ +bar/baz/rsync is uptodate +foo/ +foo/config1 is uptodate +foo/config2 is uptodate +"foo/extra" is a hard link +foo/sym is uptodate +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 16 failed" + # The script would have aborted on error, so getting here means we've won. exit 0 diff -urN --exclude=patches rsync-2.6.6/testsuite/longdir.test rsync-2.6.7/testsuite/longdir.test --- rsync-2.6.6/testsuite/longdir.test 2005-03-23 08:04:17.000000000 -0800 +++ rsync-2.6.7/testsuite/longdir.test 2006-02-03 09:58:48.000000000 -0800 @@ -7,8 +7,6 @@ . "$suitedir/rsync.fns" -set -x - hands_setup longname=This-is-a-directory-with-a-stupidly-long-name-created-in-an-attempt-to-provoke-an-error-found-in-2.0.11-that-should-hopefully-never-appear-again-if-this-test-does-its-job diff -urN --exclude=patches rsync-2.6.6/testsuite/merge.test rsync-2.6.7/testsuite/merge.test --- rsync-2.6.6/testsuite/merge.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/merge.test 2006-02-03 09:58:48.000000000 -0800 @@ -9,8 +9,6 @@ . "$suitedir/rsync.fns" -set -x - # Build some files/dirs/links to copy from1dir="${fromdir}1" diff -urN --exclude=patches rsync-2.6.6/testsuite/rsync.fns rsync-2.6.7/testsuite/rsync.fns --- rsync-2.6.6/testsuite/rsync.fns 2005-06-10 13:04:11.000000000 -0700 +++ rsync-2.6.7/testsuite/rsync.fns 2006-02-04 11:27:07.000000000 -0800 @@ -32,6 +32,9 @@ diffopt="-c" fi +HOME="$scratchdir" +export HOME + runtest() { echo $ECHO_N "Test $1: $ECHO_C" if eval "$2" @@ -44,15 +47,38 @@ fi } +# Call this if you want to filter out verbose messages (-v or -vv) from +# the output of an rsync run (whittling the output down to just the file +# messages). This isn't needed if you use -i without -v. +filter_outfile() { + sed -e '/^building file list /d' \ + -e '/^created directory /d' \ + -e '/^done$/d' \ + -e '/ --whole-file$/d' \ + -e '/^total: /d' \ + -e '/^$/,$d' \ + <"$outfile" >"$outfile.new" + mv "$outfile.new" "$outfile" +} + printmsg() { echo "$1" } - rsync_ls_lR() { find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls" } +check_perms() { + perms=`"$TOOLDIR/tls" "$1" | sed 's/^[-d]\(.........\).*/\1/'` + if test $perms = $2; then + return 0 + fi + echo "permissions: $perms on $1" + echo "should be: $2" + test_fail "failed test $3" +} + rsync_getgroups() { "$TOOLDIR/getgroups" } @@ -118,7 +144,7 @@ #################### # Many machines do not have "mkdir -p", so we have to build up long paths. # How boring. -makepath () { +makepath() { echo " makepath $1" p="$1" ( diff -urN --exclude=patches rsync-2.6.6/testsuite/symlink-ignore.test rsync-2.6.7/testsuite/symlink-ignore.test --- rsync-2.6.6/testsuite/symlink-ignore.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/symlink-ignore.test 2006-02-03 09:58:48.000000000 -0800 @@ -10,8 +10,6 @@ . "$suitedir/rsync.fns" -set -x - build_symlinks || test_fail "failed to build symlinks" # Copy recursively, but without -l or -L or -a, and all the symlinks diff -urN --exclude=patches rsync-2.6.6/testsuite/trimslash.test rsync-2.6.7/testsuite/trimslash.test --- rsync-2.6.6/testsuite/trimslash.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/trimslash.test 2006-02-03 09:58:48.000000000 -0800 @@ -9,8 +9,6 @@ . "$suitedir/rsync.fns" -set -x - "$TOOLDIR/trimslash" "/usr/local/bin" "/usr/local/bin/" "/usr/local/bin///" \ "//a//" "////" \ "/Users/Wierd Macintosh Name/// Ooh, translucent plastic/" \ diff -urN --exclude=patches rsync-2.6.6/testsuite/unsafe-links.test rsync-2.6.7/testsuite/unsafe-links.test --- rsync-2.6.6/testsuite/unsafe-links.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/unsafe-links.test 2006-02-03 09:58:48.000000000 -0800 @@ -32,8 +32,6 @@ ln -s ../files/file2 "from/safe/links/" ln -s ../../unsafe/unsafefile "from/safe/links/" -set -x - echo "rsync with relative path and just -a"; $RSYNC -avv from/safe/ to test_symlink to/links/file1 diff -urN --exclude=patches rsync-2.6.6/testsuite/wildmatch.test rsync-2.6.7/testsuite/wildmatch.test --- rsync-2.6.6/testsuite/wildmatch.test 2005-02-26 11:47:43.000000000 -0800 +++ rsync-2.6.7/testsuite/wildmatch.test 2006-02-03 09:58:48.000000000 -0800 @@ -9,12 +9,15 @@ . "$suitedir/rsync.fns" -set -x - -"$TOOLDIR/wildtest" "$srcdir/wildtest.txt" >"$scratchdir/wild.out" -diff $diffopt "$scratchdir/wild.out" - <"$scratchdir/wild.out" + diff $diffopt "$scratchdir/wild.out" - <tm_year + 1900, - mt->tm_mon + 1, - mt->tm_mday, - mt->tm_hour, - mt->tm_min, - mt->tm_sec); + (int)mt->tm_year + 1900, + (int)mt->tm_mon + 1, + (int)mt->tm_mday, + (int)mt->tm_hour, + (int)mt->tm_min, + (int)mt->tm_sec); } else { strcpy(datebuf, " "); } diff -urN --exclude=patches rsync-2.6.6/token.c rsync-2.6.7/token.c --- rsync-2.6.6/token.c 2005-02-14 00:28:00.000000000 -0800 +++ rsync-2.6.7/token.c 2005-12-29 23:19:16.000000000 -0800 @@ -22,46 +22,60 @@ extern int do_compression; extern int module_id; +extern int def_compress_level; -static int compression_level = Z_DEFAULT_COMPRESSION; +static int compression_level, per_file_default_level; /* determine the compression level based on a wildcard filename list */ void set_compression(char *fname) { - char *dont; - char *tok; + static char *match_list; + char *s; if (!do_compression) return; - compression_level = Z_DEFAULT_COMPRESSION; - dont = lp_dont_compress(module_id); - - if (!dont || !*dont) - return; - - if (dont[0] == '*' && !dont[1]) { - /* an optimization to skip the rest of this routine */ - compression_level = 0; - return; + if (!match_list) { + char *t, *f = lp_dont_compress(module_id); + int len = strlen(f); + if (!(match_list = t = new_array(char, len + 2))) + out_of_memory("set_compression"); + while (*f) { + if (*f == ' ') { + f++; + continue; + } + do { + if (isupper(*(unsigned char *)f)) + *t++ = tolower(*(unsigned char *)f); + else + *t++ = *f; + } while (*++f != ' ' && *f); + *t++ = '\0'; + } + /* Optimize a match-string of "*". */ + if (t - match_list == 2 && match_list[0] == '*') { + t = match_list; + per_file_default_level = 0; + } else + per_file_default_level = def_compress_level; + *t++ = '\0'; } - dont = strdup(dont); - fname = strdup(fname); - if (!dont || !fname) + compression_level = per_file_default_level; + + if (!*match_list) return; - strlower(dont); - strlower(fname); + if ((s = strrchr(fname, '/')) != NULL) + fname = s + 1; - for (tok = strtok(dont, " "); tok; tok = strtok(NULL, " ")) { - if (wildmatch(tok, fname)) { + for (s = match_list; *s; s += strlen(s) + 1) { + if (iwildmatch(s, fname)) { compression_level = 0; break; } } - free(dont); - free(fname); } /* non-compressing recv token */ diff -urN --exclude=patches rsync-2.6.6/uidlist.c rsync-2.6.7/uidlist.c --- rsync-2.6.6/uidlist.c 2005-02-13 16:53:44.000000000 -0800 +++ rsync-2.6.7/uidlist.c 2006-01-25 09:10:29.000000000 -0800 @@ -324,7 +324,6 @@ } } - if (preserve_gid && !numeric_ids) { /* read the gid list */ while ((id = read_int(f)) != 0) { @@ -337,8 +336,7 @@ } } - /* now convert the uid/gid of all files in the list to the mapped - * uid/gid */ + /* Now convert all the uids/gids from sender values to our values. */ if (am_root && preserve_uid && !numeric_ids) { for (i = 0; i < flist->count; i++) flist->files[i]->uid = match_uid(flist->files[i]->uid); diff -urN --exclude=patches rsync-2.6.6/util.c rsync-2.6.7/util.c --- rsync-2.6.6/util.c 2005-07-07 12:49:14.000000000 -0700 +++ rsync-2.6.7/util.c 2006-02-24 01:34:44.000000000 -0800 @@ -31,6 +31,9 @@ extern int dry_run; extern int module_id; extern int modify_window; +extern int relative_paths; +extern int human_readable; +extern mode_t orig_umask; extern char *partial_dir; extern struct filter_list_struct server_filter_list; @@ -68,7 +71,6 @@ } } - /** * Create a file descriptor pair - like pipe() but use socketpair if * possible (because of blocking issues on pipes). @@ -93,7 +95,6 @@ return ret; } - void print_child_argv(char **cmd) { rprintf(FINFO, "opening connection using "); @@ -105,15 +106,14 @@ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" ",.-_=+@/") != strlen(*cmd)) { - rprintf(FINFO, "\"%s\" ", safe_fname(*cmd)); + rprintf(FINFO, "\"%s\" ", *cmd); } else { - rprintf(FINFO, "%s ", safe_fname(*cmd)); + rprintf(FINFO, "%s ", *cmd); } } rprintf(FINFO, "\n"); } - void out_of_memory(char *str) { rprintf(FERROR, "ERROR: out of memory in %s\n", str); @@ -126,13 +126,16 @@ exit_cleanup(RERR_MALLOC); } - - -int set_modtime(char *fname, time_t modtime) +int set_modtime(char *fname, time_t modtime, mode_t mode) { +#if !defined HAVE_LUTIMES || !defined HAVE_UTIMES + if (S_ISLNK(mode)) + return 1; +#endif + if (verbose > 2) { rprintf(FINFO, "set modtime of %s to (%ld) %s", - safe_fname(fname), (long)modtime, + fname, (long)modtime, asctime(localtime(&modtime))); } @@ -140,7 +143,18 @@ return 0; { -#ifdef HAVE_UTIMBUF +#ifdef HAVE_UTIMES + struct timeval t[2]; + t[0].tv_sec = time(NULL); + t[0].tv_usec = 0; + t[1].tv_sec = modtime; + t[1].tv_usec = 0; +# ifdef HAVE_LUTIMES + if (S_ISLNK(mode)) + return lutimes(fname, t); +# endif + return utimes(fname, t); +#elif defined HAVE_UTIMBUF struct utimbuf tbuf; tbuf.actime = time(NULL); tbuf.modtime = modtime; @@ -151,41 +165,49 @@ t[1] = modtime; return utime(fname,t); #else - struct timeval t[2]; - t[0].tv_sec = time(NULL); - t[0].tv_usec = 0; - t[1].tv_sec = modtime; - t[1].tv_usec = 0; - return utimes(fname,t); +#error No file-time-modification routine found! #endif } } +/* This creates a new directory with default permissions. Since there + * might be some directory-default permissions affecting this, we can't + * force the permissions directly using the original umask and mkdir(). */ +int mkdir_defmode(char *fname) +{ + int ret; -/** - Create any necessary directories in fname. Unfortunately we don't know - what perms to give the directory when this is called so we need to rely - on the umask -**/ -int create_directory_path(char *fname, int base_umask) + umask(orig_umask); + ret = do_mkdir(fname, ACCESSPERMS); + umask(0); + + return ret; +} + +/* Create any necessary directories in fname. Any missing directories are + * created with default permissions. */ +int create_directory_path(char *fname) { char *p; + int ret = 0; while (*fname == '/') fname++; while (strncmp(fname, "./", 2) == 0) fname += 2; + umask(orig_umask); p = fname; while ((p = strchr(p,'/')) != NULL) { - *p = 0; - do_mkdir(fname, 0777 & ~base_umask); - *p = '/'; - p++; + *p = '\0'; + if (do_mkdir(fname, ACCESSPERMS) < 0 && errno != EEXIST) + ret = -1; + *p++ = '/'; } - return 0; -} + umask(0); + return ret; +} /** * Write @p len bytes at @p ptr to descriptor @p desc, retrying if @@ -216,7 +238,6 @@ return total_written; } - /** * Read @p len bytes at @p ptr from descriptor @p desc, retrying if * interrupted. @@ -242,12 +263,11 @@ return n_chars; } - /** Copy a file. * * This is used in conjunction with the --temp-dir, --backup, and * --copy-dest options. */ -int copy_file(char *source, char *dest, mode_t mode) +int copy_file(const char *source, const char *dest, mode_t mode) { int ifd; int ofd; @@ -316,7 +336,7 @@ * --delete trying to remove old .rsyncNNN files, hence it renames it * each time. **/ -int robust_unlink(char *fname) +int robust_unlink(const char *fname) { #ifndef ETXTBSY return do_unlink(fname); @@ -351,7 +371,7 @@ if (verbose > 0) { rprintf(FINFO,"renaming %s to %s because of text busy\n", - safe_fname(fname), safe_fname(path)); + fname, path); } /* maybe we should return rename()'s exit status? Nah. */ @@ -364,8 +384,11 @@ } /* Returns 0 on successful rename, 1 if we successfully copied the file - * across filesystems, -2 if copy_file() failed, and -1 on other errors. */ -int robust_rename(char *from, char *to, int mode) + * across filesystems, -2 if copy_file() failed, and -1 on other errors. + * If partialptr is not NULL and we need to do a copy, copy the file into + * the active partial-dir instead of over the destination file. */ +int robust_rename(char *from, char *to, char *partialptr, + int mode) { int tries = 4; @@ -381,6 +404,11 @@ break; #endif case EXDEV: + if (partialptr) { + if (!handle_partial_dir(partialptr,PDIR_CREATE)) + return -1; + to = partialptr; + } if (copy_file(from, to, mode) != 0) return -2; do_unlink(from); @@ -392,7 +420,6 @@ return -1; } - static pid_t all_pids[10]; static int num_pids; @@ -436,7 +463,6 @@ } } - /** Turn a user name into a uid */ int name_to_uid(char *name, uid_t *uid) { @@ -465,7 +491,6 @@ return 0; } - /** Lock a byte range in a open file */ int lock_range(int fd, int offset, int len) { @@ -517,7 +542,6 @@ filter_server_path(s); #else glob_t globbuf; - int i; if (maxargs <= argc) return; @@ -542,9 +566,9 @@ if (globbuf.gl_pathc == 0) argv[argc++] = s; else { - int j = globbuf.gl_pathc; + int i; free(s); - for (i = 0; i < j; i++) { + for (i = 0; i < (int)globbuf.gl_pathc; i++) { if (!(argv[argc++] = strdup(globbuf.gl_pathv[i]))) out_of_memory("glob_expand_one"); } @@ -745,7 +769,7 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth) { char *start, *sanp; - int rlen = 0; + int rlen = 0, leave_one_dotdir = relative_paths; if (dest != p) { int plen = strlen(p); @@ -780,9 +804,13 @@ * always be left pointing after a slash */ if (*p == '.' && (p[1] == '/' || p[1] == '\0')) { - /* skip "." component */ - p++; - continue; + if (leave_one_dotdir && p[1]) + leave_one_dotdir = 0; + else { + /* skip "." component */ + p++; + continue; + } } if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) { /* ".." component followed by slash or end */ @@ -877,43 +905,6 @@ return 1; } -/* Return the filename, turning any non-printable characters into escaped - * characters (e.g. \n -> \012, \ -> \\). This ensures that outputting it - * cannot generate an empty line nor corrupt the screen. This function can - * return only MAX_SAFE_NAMES values at a time! The returned value can be - * longer than MAXPATHLEN (because we may be trying to output an error about - * a too-long filename)! */ -char *safe_fname(const char *fname) -{ -#define MAX_SAFE_NAMES 4 - static char fbuf[MAX_SAFE_NAMES][MAXPATHLEN*2]; - static int ndx = 0; - int limit = sizeof fbuf / MAX_SAFE_NAMES - 1; - char *t; - - ndx = (ndx + 1) % MAX_SAFE_NAMES; - for (t = fbuf[ndx]; *fname; fname++) { - if (*fname == '\\') { - if ((limit -= 2) < 0) - break; - *t++ = '\\'; - *t++ = '\\'; - } else if (!isprint(*(uchar*)fname)) { - if ((limit -= 4) < 0) - break; - sprintf(t, "\\%03o", *(uchar*)fname); - t += 4; - } else { - if (--limit < 0) - break; - *t++ = *fname; - } - } - *t = '\0'; - - return fbuf[ndx]; -} - /** * Return a quoted string with the full pathname of the indicated filename. * The string " (in MODNAME)" may also be appended. The returned pointer @@ -928,7 +919,6 @@ if (result) free(result); - fn = safe_fname(fn); if (*fn == '/') p1 = p2 = ""; else { @@ -1023,22 +1013,6 @@ return 1; } -/** We need to supply our own strcmp function for file list comparisons - to ensure that signed/unsigned usage is consistent between machines. */ -int u_strcmp(const char *cs1, const char *cs2) -{ - const uchar *s1 = (const uchar *)cs1; - const uchar *s2 = (const uchar *)cs2; - - while (*s1 && *s2 && (*s1 == *s2)) { - s1++; s2++; - } - - return (int)*s1 - (int)*s2; -} - - - /** * Determine if a symlink points outside the current directory tree. * This is considered "unsafe" because e.g. when mirroring somebody @@ -1103,6 +1077,63 @@ return (depth < 0); } +/* Return the int64 number as a string. If the --human-readable option was + * specified, we may output the number in K, M, or G units. We can return + * up to 4 buffers at a time. */ +char *human_num(int64 num) +{ + static char bufs[4][128]; /* more than enough room */ + static unsigned int n; + char *s; + + n = (n + 1) % (sizeof bufs / sizeof bufs[0]); + + if (human_readable) { + char units = '\0'; + int mult = human_readable == 1 ? 1000 : 1024; + double dnum = 0; + if (num > mult*mult*mult) { + dnum = (double)num / (mult*mult*mult); + units = 'G'; + } else if (num > mult*mult) { + dnum = (double)num / (mult*mult); + units = 'M'; + } else if (num > mult) { + dnum = (double)num / mult; + units = 'K'; + } + if (units) { + sprintf(bufs[n], "%.2f%c", dnum, units); + return bufs[n]; + } + } + + s = bufs[n] + sizeof bufs[0] - 1; + *s = '\0'; + + if (!num) + *--s = '0'; + while (num) { + *--s = (num % 10) + '0'; + num /= 10; + } + return s; +} + +/* Return the double number as a string. If the --human-readable option was + * specified, we may output the number in K, M, or G units. We use a buffer + * from human_num() to return our result. */ +char *human_dnum(double dnum, int decimal_digits) +{ + char *buf = human_num(dnum); + int len = strlen(buf); + if (isdigit(*(uchar*)(buf+len-1))) { + /* There's extra room in buf prior to the start of the num. */ + buf -= decimal_digits + 1; + snprintf(buf, len + decimal_digits + 2, "%.*f", decimal_digits, dnum); + } + return buf; +} /** * Return the date and time as a string @@ -1111,6 +1142,7 @@ { static char TimeBuf[200]; struct tm *tm = localtime(&t); + char *p; #ifdef HAVE_STRFTIME strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm); @@ -1118,14 +1150,12 @@ strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf); #endif - if (TimeBuf[strlen(TimeBuf)-1] == '\n') { - TimeBuf[strlen(TimeBuf)-1] = 0; - } + if ((p = strchr(TimeBuf, '\n')) != NULL) + *p = '\0'; - return(TimeBuf); + return TimeBuf; } - /** * Sleep for a specified number of milliseconds. * @@ -1154,11 +1184,8 @@ return True; } - -/** - * Determine if two file modification times are equivalent (either - * exact or in the modification timestamp window established by - * --modify-window). +/* Determine if two time_t values are equivalent (either exact, or in + * the modification timestamp window established by --modify-window). * * @retval 0 if the times should be treated as the same * @@ -1166,7 +1193,7 @@ * * @retval -1 if the 2nd is later **/ -int cmp_modtime(time_t file1, time_t file2) +int cmp_time(time_t file1, time_t file2) { if (file2 > file1) { if (file2 - file1 <= modify_window) @@ -1212,7 +1239,6 @@ } #endif - #define MALLOC_MAX 0x40000000 void *_new_array(unsigned int size, unsigned long num) @@ -1338,3 +1364,95 @@ return a[len2-1]; } + +#define BB_SLOT_SIZE (16*1024) /* Desired size in bytes */ +#define BB_PER_SLOT_BITS (BB_SLOT_SIZE * 8) /* Number of bits per slot */ +#define BB_PER_SLOT_INTS (BB_SLOT_SIZE / 4) /* Number of int32s per slot */ + +struct bitbag { + uint32 **bits; + int slot_cnt; +}; + +struct bitbag *bitbag_create(int max_ndx) +{ + struct bitbag *bb = new(struct bitbag); + bb->slot_cnt = (max_ndx + BB_PER_SLOT_BITS - 1) / BB_PER_SLOT_BITS; + + if (!(bb->bits = (uint32**)calloc(bb->slot_cnt, sizeof (uint32*)))) + out_of_memory("bitbag_create"); + + return bb; +} + +void bitbag_set_bit(struct bitbag *bb, int ndx) +{ + int slot = ndx / BB_PER_SLOT_BITS; + ndx %= BB_PER_SLOT_BITS; + + if (!bb->bits[slot]) { + if (!(bb->bits[slot] = (uint32*)calloc(BB_PER_SLOT_INTS, 4))) + out_of_memory("bitbag_set_bit"); + } + + bb->bits[slot][ndx/32] |= 1u << (ndx % 32); +} + +#if 0 /* not needed yet */ +void bitbag_clear_bit(struct bitbag *bb, int ndx) +{ + int slot = ndx / BB_PER_SLOT_BITS; + ndx %= BB_PER_SLOT_BITS; + + if (!bb->bits[slot]) + return; + + bb->bits[slot][ndx/32] &= ~(1u << (ndx % 32)); +} + +int bitbag_check_bit(struct bitbag *bb, int ndx) +{ + int slot = ndx / BB_PER_SLOT_BITS; + ndx %= BB_PER_SLOT_BITS; + + if (!bb->bits[slot]) + return 0; + + return bb->bits[slot][ndx/32] & (1u << (ndx % 32)) ? 1 : 0; +} +#endif + +/* Call this with -1 to start checking from 0. Returns -1 at the end. */ +int bitbag_next_bit(struct bitbag *bb, int after) +{ + uint32 bits, mask; + int i, ndx = after + 1; + int slot = ndx / BB_PER_SLOT_BITS; + ndx %= BB_PER_SLOT_BITS; + + mask = (1u << (ndx % 32)) - 1; + for (i = ndx / 32; slot < bb->slot_cnt; slot++, i = mask = 0) { + if (!bb->bits[slot]) + continue; + for ( ; i < BB_PER_SLOT_INTS; i++, mask = 0) { + if (!(bits = bb->bits[slot][i] & ~mask)) + continue; + /* The xor magic figures out the lowest enabled bit in + * bits, and the switch quickly computes log2(bit). */ + switch (bits ^ (bits & (bits-1))) { +#define LOG2(n) case 1u << n: return slot*BB_PER_SLOT_BITS + i*32 + n + LOG2(0); LOG2(1); LOG2(2); LOG2(3); + LOG2(4); LOG2(5); LOG2(6); LOG2(7); + LOG2(8); LOG2(9); LOG2(10); LOG2(11); + LOG2(12); LOG2(13); LOG2(14); LOG2(15); + LOG2(16); LOG2(17); LOG2(18); LOG2(19); + LOG2(20); LOG2(21); LOG2(22); LOG2(23); + LOG2(24); LOG2(25); LOG2(26); LOG2(27); + LOG2(28); LOG2(29); LOG2(30); LOG2(31); + } + return -1; /* impossible... */ + } + } + + return -1; +} diff -urN --exclude=patches rsync-2.6.6/wildtest.c rsync-2.6.7/wildtest.c --- rsync-2.6.6/wildtest.c 2004-02-07 10:40:52.000000000 -0800 +++ rsync-2.6.7/wildtest.c 2006-01-26 14:32:59.000000000 -0800 @@ -7,7 +7,7 @@ #define WILD_TEST_ITERATIONS #include "lib/wildmatch.c" -#include "popt.h" +#include #ifdef COMPARE_WITH_FNMATCH #include @@ -20,10 +20,16 @@ typedef char bool; int output_iterations = 0; +int explode_mod = 0; +int empties_mod = 0; +int empty_at_start = 0; +int empty_at_end = 0; static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ {"iterations", 'i', POPT_ARG_NONE, &output_iterations, 0, 0, 0}, + {"empties", 'e', POPT_ARG_STRING, 0, 'e', 0, 0}, + {"explode", 'x', POPT_ARG_INT, &explode_mod, 0, 0, 0}, {0,0,0,0, 0, 0, 0} }; @@ -40,7 +46,28 @@ same_as_fnmatch = 0; /* Get rid of unused-variable compiler warning. */ #endif - matched = wildmatch(pattern, text); + if (explode_mod) { + char buf[MAXPATHLEN*2], *texts[MAXPATHLEN]; + int pos = 0, cnt = 0, ndx = 0, len = strlen(text); + + if (empty_at_start) + texts[ndx++] = ""; + /* An empty string must turn into at least one empty array item. */ + while (1) { + texts[ndx] = buf + ndx * (explode_mod + 1); + strlcpy(texts[ndx++], text + pos, explode_mod + 1); + if (pos + explode_mod >= len) + break; + pos += explode_mod; + if (!(++cnt % empties_mod)) + texts[ndx++] = ""; + } + if (empty_at_end) + texts[ndx++] = ""; + texts[ndx] = NULL; + matched = wildmatch_array(pattern, (const char**)texts, 0); + } else + matched = wildmatch(pattern, text); #ifdef COMPARE_WITH_FNMATCH fn_matched = !fnmatch(pattern, text, flags); #endif @@ -66,6 +93,7 @@ main(int argc, char **argv) { char buf[2048], *s, *string[2], *end[2]; + const char *arg; FILE *fp; int opt, line, i, flag[2]; poptContext pc = poptGetContext("wildtest", argc, (const char**)argv, @@ -73,6 +101,16 @@ while ((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { + case 'e': + arg = poptGetOptArg(pc); + empties_mod = atoi(arg); + if (strchr(arg, 's')) + empty_at_start = 1; + if (strchr(arg, 'e')) + empty_at_end = 1; + if (!explode_mod) + explode_mod = 1024; + break; default: fprintf(stderr, "%s: %s\n", poptBadOption(pc, POPT_BADOPTION_NOALIAS), @@ -81,9 +119,12 @@ } } + if (explode_mod && !empties_mod) + empties_mod = 1024; + argv = (char**)poptGetArgs(pc); if (!argv || argv[1]) { - fprintf(stderr, "Usage: wildtest TESTFILE\n"); + fprintf(stderr, "Usage: wildtest [OPTIONS] TESTFILE\n"); exit(1); } diff -urN --exclude=patches rsync-2.6.6/wildtest.txt rsync-2.6.7/wildtest.txt --- rsync-2.6.6/wildtest.txt 2003-07-14 08:46:34.000000000 -0700 +++ rsync-2.6.7/wildtest.txt 2005-12-18 18:30:59.000000000 -0800 @@ -50,6 +50,10 @@ 0 1 bar/baz/foo */foo 0 0 foo/bar/baz **/bar* 1 1 deep/foo/bar/baz **/bar/* +0 1 deep/foo/bar/baz/ **/bar/* +1 1 deep/foo/bar/baz/ **/bar/** +0 1 deep/foo/bar **/bar/* +1 1 deep/foo/bar/ **/bar/** 1 1 foo/bar/baz **/bar** 1 1 foo/bar/baz/x */bar/** 0 0 deep/foo/bar/baz/x */bar/**