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