From 1ed56a05c26b6cb6f3891ce16be89aeab388a293 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Mon, 18 Feb 2008 14:10:13 -0800 Subject: [PATCH] Extended the protocol-30 info-passing code at startup, and use it to 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 | 40 ++++++++++++++++++++++++------ generator.c | 10 +++++--- log.c | 4 ++- options.c | 28 ++++++++++++--------- rsync.c | 2 ++ rsync.h | 3 ++- rsync.yo | 4 +-- testsuite/itemize.test | 56 +++++++++++++++++++++++++++++++----------- 8 files changed, 106 insertions(+), 41 deletions(-) diff --git a/compat.c b/compat.c index 1717a3fa..9cd6bb7c 100644 --- 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, diff --git a/generator.c b/generator.c index eb130150..d192aee6 100644 --- a/generator.c +++ b/generator.c @@ -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 c7007dbb..c5dcbb94 100644 --- 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'; diff --git a/options.c b/options.c index eeb24105..a1cdda20 100644 --- 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 818b3ee2..b0042f7a 100644 --- 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 092cc999..d6a4b18e 100644 --- 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 diff --git a/rsync.yo b/rsync.yo index 024df625..97582123 100644 --- 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 diff --git a/testsuite/itemize.test b/testsuite/itemize.test index 6a5737d9..636751e4 100644 --- a/testsuite/itemize.test +++ b/testsuite/itemize.test @@ -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 <"$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 <"$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 <"$chkfile" +cat - "$chkfile.extra" <"$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 <"$chkfile" +cat - "$chkfile.extra" <"$chkfile" EOT diff $diffopt "$chkfile" "$outfile" || test_fail "test 12 failed" rm -rf "$to2dir" $RSYNC -iplrtH --link-dest=../to "$fromdir/" "$to2dir/" \ | tee "$outfile" -cat <"$chkfile" +cat - "$chkfile.extra" <"$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 <"$chkfile" +cat - "$chkfile.extra" <"$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" -- 2.34.1