Extended the protocol-30 info-passing code at startup, and use it to
authorWayne Davison <wayned@samba.org>
Mon, 18 Feb 2008 22:10:13 +0000 (14:10 -0800)
committerWayne Davison <wayned@samba.org>
Mon, 18 Feb 2008 23:57:59 +0000 (15:57 -0800)
tell the client if the server can set the times on a symlink (both
the server->client byte and the client->server use of -e).  Make use
of this info to allow the proper output of the 't' flag when rsync
can set the time on a symlink (and we're talking protocol >= 30).
Added output of "[no] symtimes" info in the --version message.
Fixed the itemize.test so that it works when rsync believes that it
can set the time of a symlink, but it can't really do it.

compat.c
generator.c
log.c
options.c
rsync.c
rsync.h
rsync.yo
testsuite/itemize.test

index 1717a3f..9cd6bb7 100644 (file)
--- a/compat.c
+++ b/compat.c
@@ -48,7 +48,7 @@ extern int preserve_acls;
 extern int preserve_xattrs;
 extern int need_messages_from_generator;
 extern int delete_mode, delete_before, delete_during, delete_after;
-extern char *shell_cmd; /* contains VER.SUB string if client is a pre-release */
+extern char *shell_cmd;
 extern char *partial_dir;
 extern char *dest_option;
 extern char *files_from;
@@ -62,10 +62,17 @@ extern iconv_t ic_send, ic_recv;
 /* These index values are for the file-list's extra-attribute array. */
 int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
 
+int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
+
 #ifdef ICONV_OPTION
 int filesfrom_convert = 0;
 #endif
 
+#define CF_INC_RECURSE  (1<<0)
+#define CF_SYMLINK_TIMES (1<<1)
+
+static const char *client_info;
+
 /* The server makes sure that if either side only supports a pre-release
  * version of a protocol, that both sides must speak a compatible version
  * of that protocol for it to be advertised as available. */
@@ -79,8 +86,9 @@ static void check_sub_protocol(void)
        int our_sub = 0;
 #endif
 
-       if (!shell_cmd || !(dot = strchr(shell_cmd, '.'))
-        || !(their_protocol = atoi(shell_cmd))
+       /* client_info starts with VER.SUB string if client is a pre-release. */
+       if (!(their_protocol = atoi(client_info))
+        || !(dot = strchr(client_info, '.'))
         || !(their_sub = atoi(dot+1))) {
 #if SUBPROTOCOL_VERSION != 0
                if (our_sub)
@@ -103,6 +111,8 @@ static void check_sub_protocol(void)
 
 void set_allow_inc_recurse(void)
 {
+       client_info = shell_cmd ? shell_cmd : "";
+
        if (!recurse || use_qsort)
                allow_inc_recurse = 0;
        else if (!am_sender
@@ -110,7 +120,7 @@ void set_allow_inc_recurse(void)
          || delay_updates || prune_empty_dirs))
                allow_inc_recurse = 0;
        else if (am_server && !local_server
-        && (!shell_cmd || strchr(shell_cmd, 'i') == NULL))
+        && (strchr(client_info, 'i') == NULL))
                allow_inc_recurse = 0;
 }
 
@@ -233,12 +243,26 @@ void setup_protocol(int f_out,int f_in)
                        exit_cleanup(RERR_PROTOCOL);
                }
        } else if (protocol_version >= 30) {
-               /* The inc_recurse var MUST be set to 0 or 1. */
+               int compat_flags;
                if (am_server) {
-                       inc_recurse = allow_inc_recurse ? 1 : 0;
-                       write_byte(f_out, inc_recurse);
+                       compat_flags = allow_inc_recurse ? CF_INC_RECURSE : 0;
+#if defined HAVE_LUTIMES && defined HAVE_UTIMES
+                       compat_flags |= CF_SYMLINK_TIMES;
+#endif
+                       write_byte(f_out, compat_flags);
                } else
-                       inc_recurse = read_byte(f_in) ? 1 : 0;
+                       compat_flags = read_byte(f_in);
+               /* The inc_recurse var MUST be set to 0 or 1. */
+               inc_recurse = compat_flags & CF_INC_RECURSE ? 1 : 0;
+               if (am_sender) {
+                       receiver_symlink_times = am_server
+                           ? strchr(client_info, 'L') != NULL
+                           : !!(compat_flags & CF_SYMLINK_TIMES);
+               }
+#if defined HAVE_LUTIMES && defined HAVE_UTIMES
+               else
+                       receiver_symlink_times = 1;
+#endif
                if (inc_recurse && !allow_inc_recurse) {
                        /* This should only be able to happen in a batch. */
                        fprintf(stderr,
index eb13015..d192aee 100644 (file)
@@ -27,6 +27,7 @@ extern int dry_run;
 extern int do_xfers;
 extern int stdout_format_has_i;
 extern int logfile_format_has_i;
+extern int receiver_symlink_times;
 extern int am_root;
 extern int am_server;
 extern int am_daemon;
@@ -553,7 +554,7 @@ static void do_delete_pass(void)
 
 int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
 {
-#ifndef HAVE_LUTIMES
+#if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
        if (S_ISLNK(file->mode)) {
                ;
        } else
@@ -599,8 +600,11 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
 {
        if (statret >= 0) { /* A from-dest-dir statret can == 1! */
                int keep_time = !preserve_times ? 0
-                   : S_ISDIR(file->mode) ? preserve_times > 1
-                   : !S_ISLNK(file->mode);
+                   : S_ISDIR(file->mode) ? preserve_times > 1 :
+#if defined HAVE_LUTIMES && defined HAVE_UTIMES
+                   (receiver_symlink_times && !(file->flags & FLAG_TIME_FAILED)) ||
+#endif
+                   !S_ISLNK(file->mode);
 
                if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
                        iflags |= ITEM_REPORT_SIZE;
diff --git a/log.c b/log.c
index c7007db..c5dcbb9 100644 (file)
--- a/log.c
+++ b/log.c
@@ -41,6 +41,7 @@ extern int stdout_format_has_i;
 extern int stdout_format_has_o_or_i;
 extern int logfile_format_has_i;
 extern int logfile_format_has_o_or_i;
+extern int receiver_symlink_times;
 extern mode_t orig_umask;
 extern char *auth_user;
 extern char *stdout_format;
@@ -638,7 +639,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
                        c[2] = !(iflags & ITEM_REPORT_CHECKSUM) ? '.' : 'c';
                        c[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's';
                        c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
-                            : !preserve_times || S_ISLNK(file->mode) ? 'T' : 't';
+                            : !preserve_times || (!receiver_symlink_times && S_ISLNK(file->mode))
+                            ? 'T' : 't';
                        c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
                        c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
                        c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
index eeb2410..a1cdda2 100644 (file)
--- a/options.c
+++ b/options.c
@@ -218,6 +218,7 @@ static void print_rsync_version(enum logcode f)
        char const *got_socketpair = "no ";
        char const *have_inplace = "no ";
        char const *hardlinks = "no ";
+       char const *symtimes = "no ";
        char const *acls = "no ";
        char const *xattrs = "no ";
        char const *links = "no ";
@@ -252,6 +253,9 @@ static void print_rsync_version(enum logcode f)
 #ifdef ICONV_OPTION
        iconv = "";
 #endif
+#if defined HAVE_LUTIMES && defined HAVE_UTIMES
+       symtimes = "";
+#endif
 
        rprintf(f, "%s  version %s  protocol version %d%s\n",
                RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
@@ -265,8 +269,8 @@ static void print_rsync_version(enum logcode f)
                (int)(sizeof (int64) * 8));
        rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
                got_socketpair, hardlinks, links, ipv6, have_inplace);
-       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv\n",
-               have_inplace, acls, xattrs, iconv);
+       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes\n",
+               have_inplace, acls, xattrs, iconv, symtimes);
 
 #ifdef MAINTAINER_MODE
        rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
@@ -1777,24 +1781,24 @@ void server_options(char **args, int *argc_p)
                argstr[x++] = 'z';
 
        /* We make use of the -e option to let the server know about any
-        * pre-release protocol version && our allow_inc_recurse status. */
-       set_allow_inc_recurse();
+        * pre-release protocol version && some behavior flags. */
+       argstr[x++] = 'e';
 #if SUBPROTOCOL_VERSION != 0
        if (protocol_version == PROTOCOL_VERSION) {
                x += snprintf(argstr+x, sizeof argstr - x,
-                             "e%d.%d%s", PROTOCOL_VERSION, SUBPROTOCOL_VERSION,
-                             allow_inc_recurse ? "i" : "");
+                             "%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
        } else
 #endif
-       if (allow_inc_recurse) {
-               argstr[x++] = 'e';
+               argstr[x++] = '.';
+       set_allow_inc_recurse();
+       if (allow_inc_recurse)
                argstr[x++] = 'i';
-       }
-
+#if defined HAVE_LUTIMES && defined HAVE_UTIMES
+       argstr[x++] = 'L';
+#endif
        argstr[x] = '\0';
 
-       if (x != 1)
-               args[ac++] = argstr;
+       args[ac++] = argstr;
 
 #ifdef ICONV_OPTION
        if (iconv_opt) {
diff --git a/rsync.c b/rsync.c
index 818b3ee..b0042f7 100644 (file)
--- a/rsync.c
+++ b/rsync.c
@@ -399,6 +399,8 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
                }
                if (ret == 0) /* ret == 1 if symlink could not be set */
                        updated = 1;
+               else
+                       file->flags |= FLAG_TIME_FAILED;
        }
 
        change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
diff --git a/rsync.h b/rsync.h
index 092cc99..d6a4b18 100644 (file)
--- a/rsync.h
+++ b/rsync.h
@@ -77,6 +77,7 @@
 #define FLAG_HLINK_DONE (1<<8) /* receiver/generator (checked on all types) */
 #define FLAG_LENGTH64 (1<<9)   /* sender/receiver/generator */
 #define FLAG_SKIP_GROUP (1<<10)        /* receiver/generator */
+#define FLAG_TIME_FAILED (1<<11)/* generator */
 
 /* These flags are passed to functions but not stored. */
 
@@ -93,7 +94,7 @@
 /* This is used when working on a new protocol version in CVS, and should
  * be a new non-zero value for each CVS change that affects the protocol.
  * It must ALWAYS be 0 when the protocol goes final! */
-#define SUBPROTOCOL_VERSION 16
+#define SUBPROTOCOL_VERSION 17
 
 /* We refuse to interoperate with versions that are not in this range.
  * Note that we assume we'll work with later versions: the onus is on
index 024df62..9758212 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -1681,8 +1681,8 @@ quote(itemization(
   it() A bf(t) means the modification time is different and is being updated
   to the sender's value (requires bf(--times)).  An alternate value of bf(T)
   means that the modification time will be set to the transfer time, which happens
-  anytime a symlink is transferred, or when a regular file or device is
-  transferred without bf(--times).
+  when a file/symlink/device is updated without bf(--times) and when a
+  symlink is changed and the receiver can't set its time.
   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
index 6a5737d..636751e 100644 (file)
@@ -27,13 +27,26 @@ umask 022
 ln "$fromdir/foo/config1" "$fromdir/foo/extra"
 
 # Check if the OS can hard-link symlinks or not
-ln -s no-such-dir "$to2dir"
-if ln "$to2dir" "$to2dir.test" 2>/dev/null; then
+if ln "$fromdir/foo/sym" "$to2dir" 2>/dev/null; then
     L=hL
 else
     L=cL
 fi
-rm -f "$to2dir" "$to2dir.test"
+rm -f "$to2dir"
+
+# Check if rsync can preserve time on symlinks
+case "$RSYNC" in
+*protocol=2*)
+    T=.T
+    ;;
+*)
+    if $RSYNC --version | grep ", symtimes" >/dev/null; then
+       T=.t
+    else
+       T=.T
+    fi
+    ;;
+esac
 
 $RSYNC -iplr "$fromdir/" "$todir/" \
     | tee "$outfile"
@@ -82,7 +95,7 @@ cat <<EOT >"$chkfile"
 .d..t...... foo/
 .f..t...... foo/config1
 >fcstp..... foo/config2
-cL..T...... foo/sym -> ../bar/baz/rsync
+cL.$T...... foo/sym -> ../bar/baz/rsync
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
 
@@ -142,6 +155,21 @@ diff $diffopt "$chkfile" "$outfile" || test_fail "test 7 failed"
 $RSYNC -ivvplrtH --copy-dest=../to "$fromdir/" "$to2dir/" \
     | tee "$outfile"
 filter_outfile
+case `tail -1 "$outfile"` in
+cL..t*)
+    sym_dots='..t......'
+    L_sym_dots='cL..t......'
+    is_uptodate='-> ../bar/baz/rsync'
+    echo "cL$sym_dots foo/sym $is_uptodate" >"$chkfile.extra"
+    L=cL
+    ;;
+*)
+    sym_dots='         '
+    L_sym_dots='.L         '
+    is_uptodate='is uptodate'
+    touch "$chkfile.extra"
+    ;;
+esac
 cat <<EOT >"$chkfile"
 cd          ./
 cd          bar/
@@ -151,14 +179,14 @@ cd          foo/
 cf          foo/config1
 cf          foo/config2
 hf          foo/extra => foo/config1
-cL          foo/sym -> ../bar/baz/rsync
+cL$sym_dots foo/sym -> ../bar/baz/rsync
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 8 failed"
 
 rm -rf "$to2dir"
 $RSYNC -iplrtH --copy-dest=../to "$fromdir/" "$to2dir/" \
     | tee "$outfile"
-cat <<EOT >"$chkfile"
+cat - "$chkfile.extra" <<EOT >"$chkfile"
 hf          foo/extra => foo/config1
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 9 failed"
@@ -176,7 +204,7 @@ foo/ is uptodate
 foo/config1 is uptodate
 foo/config2 is uptodate
 foo/extra => foo/config1
-foo/sym is uptodate
+foo/sym $is_uptodate
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 10 failed"
 
@@ -193,21 +221,21 @@ cd          foo/
 hf          foo/config1
 hf          foo/config2
 hf          foo/extra => foo/config1
-$L          foo/sym -> ../bar/baz/rsync
+$L$sym_dots foo/sym -> ../bar/baz/rsync
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 11 failed"
 
 rm -rf "$to2dir"
 $RSYNC -iplrtH --dry-run --link-dest=../to "$fromdir/" "$to2dir/" \
     | tee "$outfile"
-cat <<EOT >"$chkfile"
+cat - "$chkfile.extra" <<EOT >"$chkfile"
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 12 failed"
 
 rm -rf "$to2dir"
 $RSYNC -iplrtH --link-dest=../to "$fromdir/" "$to2dir/" \
     | tee "$outfile"
-cat <<EOT >"$chkfile"
+cat - "$chkfile.extra" <<EOT >"$chkfile"
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 13 failed"
 
@@ -224,7 +252,7 @@ foo/ is uptodate
 foo/config1 is uptodate
 foo/config2 is uptodate
 foo/extra is uptodate
-foo/sym is uptodate
+foo/sym $is_uptodate
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 14 failed"
 
@@ -241,14 +269,14 @@ cd          foo/
 .f          foo/config1
 .f          foo/config2
 .f          foo/extra
-.L          foo/sym -> ../bar/baz/rsync
+$L_sym_dots foo/sym -> ../bar/baz/rsync
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 15 failed"
 
 rm -rf "$to2dir"
 $RSYNC -iplrtH --compare-dest="$todir" "$fromdir/" "$to2dir/" \
     | tee "$outfile"
-cat <<EOT >"$chkfile"
+cat - "$chkfile.extra" <<EOT >"$chkfile"
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 16 failed"
 
@@ -265,7 +293,7 @@ foo/ is uptodate
 foo/config1 is uptodate
 foo/config2 is uptodate
 foo/extra is uptodate
-foo/sym is uptodate
+foo/sym $is_uptodate
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 17 failed"