Hello,

are there any other opinions about the suggested patch?
It adds an option "--dirs-update", which handles directories in a similar way 
as "--update" handles files. We face a certain situation where this is useful.

Suggestions to improve the patch are welcome.
Arvid


Am Montag, 20. Januar 2014, 16:44:02 schrieb Arvid Requate:
> Hello,
> 
> thanks for your comments. Attached you may find a git patch based on the
> master branch. The patch adds a new rsync option which helps in our use
> case of replicating Samba 4 sysvol directories. In this specific case
> fACLs, xattrs and modification timestamps of subfolders of the target can
> get modified by Windows client tools in between subsequent rsync runs. If
> this happens, they should not be overwritten to avoid inconsistencies and
> client side error messages.
> 
> To achive this, the patch implements an option "--dirs-update" to let rsync
> skip changes to directories in case the target directory has a more recent
> modification timestamp than the source directory *and* the direct content of
> the directories is the same. This option can be regarded as complementary
> to the existing option "--update", which does pretty much the same for
> files. The patch also provides a script for the testsuite.
> 
> Maybe the patch is also useful for others. If it doesn't inflict any
> collateral damage on the general behaviour of rsync (e.g. the incremental
> recursion), maybe it would be worthwile to submit it as an enhancement bug
> to the bugzilla. Any opinions about this or suggestions for imrovement?
> 
> On 16.01.2014 01:31:38, Matthias Schniedermeyer wrote:
> > - rsync doesn't determine if atime/mtime is bigger/smaller, it only
> > determines if they are identical or not.
> 
> From looking at the function cmp_time in util.c and its use in generator.c I
> obtained the impression that time ordering is explicitely considered. My
> patch makes use of this function. Also the description of the option
> "--update" in the manual page ("skip files that are newer") seems to
> support this impression.
> 
> I'm aware of the unidirectional nature of rsync you pointed out. We use
> rsync to let two machines pull the content of a certain directory tree and
> let them take turns at that. Modification time ordering is essential for
> this. Since Samba doesn't implement DFS replication yet we have to rely on
> this.
> 
> Cheers,
> Arvid

-- 
Dr. Arvid Requate
Open Source Software Engineer

Univention GmbH
be open.
Mary-Somerville-Str.1
28359 Bremen
Tel. : +49 421 22232-52
Fax : +49 421 22232-99

requ...@univention.de
http://www.univention.de

Geschäftsführer: Peter H. Ganten
HRB 20755 Amtsgericht Bremen
Steuer-Nr.: 71-597-02876
>From c7ea18e917e2324f495fce1e2259f614da71fded Mon Sep 17 00:00:00 2001
From: Arvid Requate <requ...@univention.de>
Date: Fri, 17 Jan 2014 03:03:33 +0100
Subject: [PATCH] Additional option --dirs-update

---
 generator.c                |   18 ++++++
 options.c                  |    3 +
 rsync.h                    |    1 +
 rsync.yo                   |    7 +++
 testsuite/dirs-update.test |  125 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 154 insertions(+), 0 deletions(-)
 create mode 100644 testsuite/dirs-update.test

diff --git a/generator.c b/generator.c
index 0f77481..6554cfd 100644
--- a/generator.c
+++ b/generator.c
@@ -54,6 +54,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 human_readable;
 extern int ignore_existing;
 extern int ignore_non_existing;
@@ -1372,6 +1373,15 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 				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 (INFO_GTE(SKIP, 1))
+				rprintf(FINFO, "%s is newer\n", fname);
+			file->flags |= FLAG_DONT_TOUCH_UP;
+			goto cleanup;
+		}
+
 		init_stat_x(&real_sx);
 		real_sx.st = sx.st;
 		real_ret = statret;
@@ -2019,6 +2029,14 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
 		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 (INFO_GTE(SKIP, 3)) {
+				rprintf(FINFO, "skipping touch_up_dirs: %s (%d)\n",
+					NS(fname), i);
+			}
+			continue;
+		}
 		if (DEBUG_GTE(TIME, 2)) {
 			fname = f_name(file, NULL);
 			rprintf(FINFO, "touch_up_dirs: %s (%d)\n",
diff --git a/options.c b/options.c
index cc2c1ef..1b142cf 100644
--- a/options.c
+++ b/options.c
@@ -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;
@@ -680,6 +681,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");
@@ -914,6 +916,7 @@ static struct poptOption long_options[] = {
   {"no-one-file-system",0, POPT_ARG_VAL,    &one_file_system, 0, 0, 0 },
   {"no-x",             0,  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 --git a/rsync.h b/rsync.h
index b2869d0..858b8ab 100644
--- a/rsync.h
+++ b/rsync.h
@@ -83,6 +83,7 @@
 #define FLAG_SKIP_GROUP (1<<10)	/* receiver/generator */
 #define FLAG_TIME_FAILED (1<<11)/* generator */
 #define FLAG_MOD_NSEC (1<<12)	/* sender/receiver/generator */
+#define FLAG_DONT_TOUCH_UP (1<<13)/* generator */
 
 /* These flags are passed to functions but not stored. */
 
diff --git a/rsync.yo b/rsync.yo
index a84b7bf..d2f4bc1 100644
--- a/rsync.yo
+++ b/rsync.yo
@@ -346,6 +346,7 @@ to the detailed description below for a complete description.  verb(
      --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
@@ -800,6 +801,12 @@ This option is a transfer rule, not an exclude, so it doesn't affect the
 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. The directory content will be considered for replication
+anyway.
+
 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 --git a/testsuite/dirs-update.test b/testsuite/dirs-update.test
new file mode 100644
index 0000000..0cc855e
--- /dev/null
+++ b/testsuite/dirs-update.test
@@ -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
-- 
1.7.2.5

-- 
Please use reply-all for most replies to avoid omitting the mailing list.
To unsubscribe or change options: https://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html

Reply via email to