diff -Nuarp rsync-3.0.7.orig/generator.c rsync-3.0.7/generator.c --- rsync-3.0.7.orig/generator.c 2009-12-23 20:36:27.000000000 +0100 +++ rsync-3.0.7/generator.c 2014-01-15 07:01:21.840000000 +0100 @@ -55,6 +55,7 @@ extern int ignore_errors; extern int remove_source_files; extern int delay_updates; extern int update_only; +extern int dirs_update_only; extern int ignore_existing; extern int ignore_non_existing; extern int inplace; @@ -1434,6 +1435,15 @@ static void recv_generator(char *fname, dry_missing_dir = file; file->flags |= FLAG_MISSING_DIR; } + + if (dirs_update_only > 0 && statret == 0 + && cmp_time(sx.st.st_mtime, file->modtime) > 0) { + if (verbose > 1) + rprintf(FINFO, "%s is newer\n", fname); + file->flags |= FLAG_DONT_TOUCH_UP; + goto cleanup; + } + real_ret = statret; real_sx = sx; if (file->flags & FLAG_DIR_CREATED) @@ -2049,6 +2059,14 @@ static void touch_up_dirs(struct file_li if (!S_ISDIR(file->mode) || (!implied_dirs && file->flags & FLAG_IMPLIED_DIR)) continue; + if (S_ISDIR(file->mode) && file->flags & FLAG_DONT_TOUCH_UP) { + fname = f_name(file, NULL); + if (verbose > 3) { + rprintf(FINFO, "skipping touch_up_dirs: %s (%d)\n", + NS(fname), i); + } + continue; + } if (verbose > 3) { fname = f_name(file, NULL); rprintf(FINFO, "touch_up_dirs: %s (%d)\n", diff -Nuarp rsync-3.0.7.orig/options.c rsync-3.0.7/options.c --- rsync-3.0.7.orig/options.c 2009-12-21 23:40:41.000000000 +0100 +++ rsync-3.0.7/options.c 2014-01-15 07:23:21.152000000 +0100 @@ -60,6 +60,7 @@ int preserve_uid = 0; int preserve_gid = 0; int preserve_times = 0; int update_only = 0; +int dirs_update_only = 0; int cvs_exclude = 0; int dry_run = 0; int do_xfers = 1; @@ -326,6 +327,7 @@ void usage(enum logcode F) 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," --dirs-update skip dirs 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," --append-verify like --append, but with old data in file checksum\n"); @@ -534,6 +536,7 @@ static struct poptOption long_options[] {"no-one-file-system",'x',POPT_ARG_VAL, &one_file_system, 0, 0, 0 }, {"no-x", 'x', POPT_ARG_VAL, &one_file_system, 0, 0, 0 }, {"update", 'u', POPT_ARG_NONE, &update_only, 0, 0, 0 }, + {"dirs-update", 0, POPT_ARG_NONE, &dirs_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 }, diff -Nuarp rsync-3.0.7.orig/rsync.h rsync-3.0.7/rsync.h --- rsync-3.0.7.orig/rsync.h 2009-12-23 20:36:27.000000000 +0100 +++ rsync-3.0.7/rsync.h 2014-01-15 07:13:05.880000000 +0100 @@ -80,6 +80,7 @@ #define FLAG_LENGTH64 (1<<9) /* sender/receiver/generator */ #define FLAG_SKIP_GROUP (1<<10) /* receiver/generator */ #define FLAG_TIME_FAILED (1<<11)/* generator */ +#define FLAG_DONT_TOUCH_UP (1<<12)/* generator */ /* These flags are passed to functions but not stored. */ diff -Nuarp rsync-3.0.7.orig/rsync.yo rsync-3.0.7/rsync.yo --- rsync-3.0.7.orig/rsync.yo 2009-12-31 22:08:07.000000000 +0100 +++ rsync-3.0.7/rsync.yo 2014-01-15 07:38:39.676000000 +0100 @@ -330,6 +330,7 @@ to the detailed description below for a --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 + --dirs-update skip dirs that are newer on the receiver --inplace update destination files in-place --append append data onto shorter files --append-verify --append w/old data in file checksum @@ -708,6 +709,11 @@ This option is a transfer rule, not an e data that goes into the file-lists, and thus it doesn't affect deletions. It just limits the files that the receiver requests to be transferred. +dit(bf(--dirs-update)) This forces rsync to skip modification of the status +of directories (i.e. ownership, posix permissions, fACLs, extended attributes) +which exist on the destination and have a modified time that is newer than +the source directory. Note that the directory content will not be skipped. + dit(bf(--inplace)) This option changes how rsync transfers a file when its data needs to be updated: instead of the default method of creating a new copy of the file and moving it into place when it is complete, rsync diff -Nuarp rsync-3.0.7.orig/testsuite/dirs-update.test rsync-3.0.7/testsuite/dirs-update.test --- rsync-3.0.7.orig/testsuite/dirs-update.test 1970-01-01 01:00:00.000000000 +0100 +++ rsync-3.0.7/testsuite/dirs-update.test 2014-01-17 02:53:56.600000000 +0100 @@ -0,0 +1,125 @@ +#! /bin/sh + +# This program is distributable under the terms of the GNU GPL (see +# COPYING). + +# Test that rsync handles dirctory ACL preservation. + +. $srcdir/testsuite/rsync.fns +set -x + +$RSYNC --version | grep ", ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support" + +makepath "$fromdir/sub1" +makepath "$fromdir/sub2" +makepath "$fromdir/sub3" + +dst_preferred_files='. sub1 sub3' +src_preferred_files='sub2 sub1/file1 sub2/file2' + +case "$setfacl_nodef" in +true) + if ! chmod --help 2>&1 | fgrep +a >/dev/null; then + test_skipped "I don't know how to use setfacl or chmod for ACLs" + fi + chmod +a "root allow read,write,execute" "$fromdir/sub1" || test_skipped "Your filesystem has ACLs disabled" + chmod +a "admin allow read" "$fromdir/sub1" + chmod +a "daemon allow read,write" "$fromdir/sub1" + + chmod +a "root allow read,write,execute" "$fromdir/sub2" + chmod +a "admin allow read" "$fromdir/sub2" + chmod +a "daemon allow read,write" "$fromdir/sub2" + + chmod +a "root allow read,write,execute" "$fromdir/sub3" + chmod +a "admin allow read" "$fromdir/sub3" + chmod +a "daemon allow read,write" "$fromdir/sub3" + + see_acls() { + ls -le "${@}" + } + ;; +*) + setfacl -m u:0:7 "$fromdir/sub1" || test_skipped "Your filesystem has ACLs disabled" + setfacl -m g:1:5 "$fromdir/sub1" + setfacl -m g:2:1 "$fromdir/sub1" + setfacl -m g:0:7 "$fromdir/sub1" + setfacl -m u:2:1 "$fromdir/sub1" + setfacl -m u:1:5 "$fromdir/sub1" + + setfacl -m u:0:7 "$fromdir/sub2" + setfacl -m g:1:5 "$fromdir/sub2" + setfacl -m g:2:1 "$fromdir/sub2" + setfacl -m g:0:7 "$fromdir/sub2" + setfacl -m u:2:1 "$fromdir/sub2" + setfacl -m u:1:5 "$fromdir/sub2" + + setfacl -m u:0:7 "$fromdir/sub3" + setfacl -m g:1:5 "$fromdir/sub3" + setfacl -m g:2:1 "$fromdir/sub3" + setfacl -m g:0:7 "$fromdir/sub3" + setfacl -m u:2:1 "$fromdir/sub3" + setfacl -m u:1:5 "$fromdir/sub3" + + see_acls() { + getfacl "${@}" + } + ;; +esac + +$RSYNC -avvA "$fromdir/" "$todir" + +## add file1 to $fromdir/sub1 +echo else >"$fromdir/sub1/file1" + +sleep 1 ## wait a bit for timestamp difference + +## change $todir and $todir/sub* +case "$setfacl_nodef" in +true) + if ! chmod --help 2>&1 | fgrep +a >/dev/null; then + test_skipped "I don't know how to use setfacl or chmod for ACLs" + fi + chmod +a "nobody allow read,execute" "$todir" + chmod +a "nobody allow read,execute" "$todir/sub1" + chmod +a "nobody allow read,execute" "$todir/sub2" + chmod +a "nobody allow read,execute" "$todir/sub3" + ;; +*) + setfacl -m u:65534:5 "$todir" + setfacl -m u:65534:5 "$todir/sub1" + setfacl -m u:65534:5 "$todir/sub2" + setfacl -m u:65534:5 "$todir/sub3" + ;; +esac + +sleep 1 ## wait a bit for timestamp difference + +## update mtime of $todir and $todir/sub* +chown guest "$todir" +chown guest "$todir/sub1" +chown guest "$todir/sub2" +chown guest "$todir/sub3" +touch "$todir" +touch "$todir/sub1" +touch "$todir/sub2" +touch "$todir/sub3" + +sleep 1 ## wait a bit for timestamp difference + +## add file2 to $fromdir/sub2, this causes the mtime of sub2 to be updated +echo else >"$fromdir/sub2/file2" + +cd "$todir" +see_acls $dst_preferred_files >"$scratchdir/dst_preferred_acls.txt" + +cd "$fromdir" +see_acls $src_preferred_files >"$scratchdir/src_preferred_acls.txt" + +$RSYNC -avvA --update --dirs-update "$fromdir/" "$todir/" + +cd "$todir" +see_acls $dst_preferred_files | diff $diffopt "$scratchdir/dst_preferred_acls.txt" - +see_acls $src_preferred_files | diff $diffopt "$scratchdir/src_preferred_acls.txt" - + +# The script would have aborted on error, so getting here means we've won. +exit 0