diff options
author | Wayne Davison <wayne@opencoder.net> | 2020-04-23 13:17:41 -0700 |
---|---|---|
committer | Wayne Davison <wayne@opencoder.net> | 2020-04-23 13:24:15 -0700 |
commit | b936741032fbfcd41b74ae4ca76ea718df25c64b (patch) | |
tree | 783e982e10171d8815cb8d1a607fb4c37daf49ab | |
parent | 2b2a3c87b6390ffef87311eb6551117a312d4f61 (diff) | |
download | rsync-b936741032fbfcd41b74ae4ca76ea718df25c64b.tar.gz |
Added --atimes and --set-noatime options.
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | compat.c | 16 | ||||
-rw-r--r-- | flist.c | 27 | ||||
-rw-r--r-- | generator.c | 53 | ||||
-rw-r--r-- | log.c | 3 | ||||
-rw-r--r-- | options.c | 22 | ||||
-rw-r--r-- | rsync.c | 53 | ||||
-rw-r--r-- | rsync.h | 43 | ||||
-rw-r--r-- | rsync.yo | 26 | ||||
-rw-r--r-- | syscall.c | 66 | ||||
-rw-r--r-- | t_stub.c | 1 | ||||
-rw-r--r-- | testsuite/atimes.test | 17 | ||||
-rw-r--r-- | testsuite/daemon.test | 11 | ||||
-rw-r--r-- | testsuite/rsync.fns | 17 | ||||
-rw-r--r-- | tls.c | 76 | ||||
-rw-r--r-- | trimslash.c | 1 | ||||
-rw-r--r-- | util.c | 35 |
17 files changed, 363 insertions, 108 deletions
@@ -30,6 +30,10 @@ Changes since 3.1.3: ENHANCEMENTS: + - Improved the --atimes patch and promoted it to be in the release. + + - Added --set-noatime option to open files using O_NOATIME. + - Improved the --write-devices patch and promoted it to be in the release. - Added openssl support to the rsync-ssl script via its renamed helper @@ -48,6 +48,7 @@ extern int protocol_version; extern int protect_args; extern int preserve_uid; extern int preserve_gid; +extern int preserve_atimes; extern int preserve_acls; extern int preserve_xattrs; extern int need_messages_from_generator; @@ -65,7 +66,7 @@ extern char *iconv_opt; #endif /* These index values are for the file-list's extra-attribute array. */ -int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx; +int pathname_ndx, depth_ndx, atimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx; int receiver_symlink_times = 0; /* receiver can set the time on a symlink */ int sender_symlink_iconv = 0; /* sender should convert symlink content */ @@ -136,10 +137,17 @@ void set_allow_inc_recurse(void) void setup_protocol(int f_out,int f_in) { - if (am_sender) - file_extra_cnt += PTR_EXTRA_CNT; + assert(file_extra_cnt == 0); + assert(EXTRA64_CNT == 2 || EXTRA64_CNT == 1); + + /* All int64 values must be set first so that they are guaranteed to be + * aligned for direct int64-pointer memory access. */ + if (preserve_atimes) + atimes_ndx = (file_extra_cnt += EXTRA64_CNT); + if (am_sender) /* This is most likely in the in64 union as well. */ + pathname_ndx = (file_extra_cnt += PTR_EXTRA_CNT); else - file_extra_cnt++; + depth_ndx = ++file_extra_cnt; if (preserve_uid) uid_ndx = ++file_extra_cnt; if (preserve_gid) @@ -55,6 +55,7 @@ extern int preserve_specials; extern int delete_during; extern int missing_args; extern int eol_nulls; +extern int atimes_ndx; extern int relative_paths; extern int implied_dirs; extern int ignore_perishable; @@ -379,7 +380,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, #endif int ndx, int first_ndx) { - static time_t modtime; + static time_t modtime, atime; static mode_t mode; #ifdef SUPPORT_HARD_LINKS static int64 dev; @@ -479,6 +480,12 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, modtime = file->modtime; if (NSEC_BUMP(file) && protocol_version >= 31) xflags |= XMIT_MOD_NSEC; + if (atimes_ndx && !S_ISDIR(mode)) { + if (F_ATIME(file) == atime) + xflags |= XMIT_SAME_ATIME; + else + atime = F_ATIME(file); + } #ifdef SUPPORT_HARD_LINKS if (tmp_dev != -1) { @@ -565,6 +572,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, write_varint(f, F_MOD_NSEC(file)); if (!(xflags & XMIT_SAME_MODE)) write_int(f, to_wire_mode(mode)); + if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) + write_varlong(f, atime, 4); if (preserve_uid && !(xflags & XMIT_SAME_UID)) { if (protocol_version < 30) write_int(f, uid); @@ -652,7 +661,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags) { - static int64 modtime; + static int64 modtime, atime; static mode_t mode; #ifdef SUPPORT_HARD_LINKS static int64 dev; @@ -799,6 +808,16 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x modtime_nsec = 0; if (!(xflags & XMIT_SAME_MODE)) mode = from_wire_mode(read_int(f)); + if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) { + atime = read_varlong(f, 4); +#if SIZEOF_TIME_T < SIZEOF_INT64 + if (!am_generator && (int64)(time_t)atime != atime) { + rprintf(FERROR_XFER, + "Access time value of %s truncated on receiver.\n", + lastname); + } +#endif + } if (chmod_modes && !S_ISLNK(mode) && mode) mode = tweak_mode(mode, chmod_modes); @@ -966,6 +985,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x F_GROUP(file) = gid; file->flags |= gid_flags; } + if (atimes_ndx) + F_ATIME(file) = atime; if (unsort_ndx) F_NDX(file) = flist->used + flist->ndx_start; @@ -1363,6 +1384,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, F_GROUP(file) = st.st_gid; if (am_generator && st.st_uid == our_uid) file->flags |= FLAG_OWNED_BY_US; + if (atimes_ndx) + F_ATIME(file) = st.st_atime; if (basename != thisname) file->dirname = lastdir; diff --git a/generator.c b/generator.c index 7ec924cf..3c50f63f 100644 --- a/generator.c +++ b/generator.c @@ -506,6 +506,9 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED) && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname)) iflags |= ITEM_REPORT_TIME; + if (atimes_ndx && !S_ISDIR(file->mode) && !S_ISLNK(file->mode) + && cmp_time(F_ATIME(file), 0, sxp->st.st_atime, 0) != 0) + iflags |= ITEM_REPORT_ATIME; #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST if (S_ISLNK(file->mode)) { ; @@ -916,6 +919,8 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx, if (link_dest) { if (!hard_link_one(file, fname, cmpbuf, 1)) goto try_a_copy; + if (atimes_ndx) + set_file_attrs(fname, file, sxp, NULL, 0); if (preserve_hard_links && F_IS_HLINKED(file)) finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j); if (!maybe_ATTRS_REPORT && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) { @@ -1120,35 +1125,40 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx, static void list_file_entry(struct file_struct *f) { char permbuf[PERMSTRING_SIZE]; - int64 len; - int colwidth = human_readable ? 14 : 11; + const char *mtime_str = timestring(f->modtime); + int size_width = human_readable ? 14 : 11; + int mtime_width = 1 + strlen(mtime_str); + int atime_width = atimes_ndx ? mtime_width : 0; if (!F_IS_ACTIVE(f)) { /* this can happen if duplicate names were removed */ return; } - permstring(permbuf, f->mode); - len = F_LENGTH(f); - /* TODO: indicate '+' if the entry has an ACL. */ -#ifdef SUPPORT_LINKS - if (preserve_links && S_ISLNK(f->mode)) { - rprintf(FINFO, "%s %*s %s %s -> %s\n", - permbuf, colwidth, human_num(len), - timestring(f->modtime), f_name(f, NULL), - F_SYMLINK(f)); - } else -#endif if (missing_args == 2 && f->mode == 0) { rprintf(FINFO, "%-*s %s\n", - colwidth + 31, "*missing", + 10 + 1 + size_width + mtime_width + atime_width, "*missing", f_name(f, NULL)); } else { - rprintf(FINFO, "%s %*s %s %s\n", - permbuf, colwidth, human_num(len), - timestring(f->modtime), f_name(f, NULL)); + const char *atime_str = atimes_ndx && !S_ISDIR(f->mode) ? timestring(F_ATIME(f)) : ""; + const char *arrow, *lnk; + + permstring(permbuf, f->mode); + +#ifdef SUPPORT_LINKS + if (preserve_links && S_ISLNK(f->mode)) { + arrow = " -> "; + lnk = F_SYMLINK(f); + } else +#endif + arrow = lnk = ""; + + rprintf(FINFO, "%s %*s %s%*s %s%s%s\n", + permbuf, size_width, human_num(F_LENGTH(f)), + timestring(f->modtime), atime_width, atime_str, + f_name(f, NULL), arrow, lnk); } } @@ -2064,8 +2074,13 @@ static void touch_up_dirs(struct file_list *flist, int ndx) do_chmod(fname, file->mode); if (need_retouch_dir_times) { STRUCT_STAT st; - if (link_stat(fname, &st, 0) == 0 && time_diff(&st, file)) - set_modtime(fname, file->modtime, F_MOD_NSEC_or_0(file), file->mode); + if (link_stat(fname, &st, 0) == 0 && time_diff(&st, file)) { + st.st_mtime = file->modtime; +#ifdef ST_MTIME_NSEC + st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file); +#endif + set_times(fname, &st); + } } if (counter >= loopchk_limit) { if (allowed_lull) @@ -716,7 +716,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op, c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p'; c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o'; c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g'; - c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u'; + c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' + : S_ISLNK(file->mode) ? 'U' : 'u'; c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a'; c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x'; c[11] = '\0'; @@ -63,7 +63,9 @@ int preserve_specials = 0; int preserve_uid = 0; int preserve_gid = 0; int preserve_times = 0; +int preserve_atimes = 0; int update_only = 0; +int set_noatime = 0; int cvs_exclude = 0; int dry_run = 0; int do_xfers = 1; @@ -711,6 +713,8 @@ void usage(enum logcode F) rprintf(F," --specials preserve special files\n"); rprintf(F," -D same as --devices --specials\n"); rprintf(F," -t, --times preserve modification times\n"); + rprintf(F," -U, --atimes preserve access (last-used) times\n"); + rprintf(F," --set-noatime avoid changing the atime on accessed files\n"); rprintf(F," -O, --omit-dir-times omit directories from --times\n"); rprintf(F," -J, --omit-link-times omit symlinks from --times\n"); rprintf(F," --super receiver attempts super-user activities\n"); @@ -872,6 +876,11 @@ static struct poptOption long_options[] = { {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 }, {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, + {"atimes", 'U', POPT_ARG_NONE, 0, 'U', 0, 0 }, + {"no-atimes", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 }, + {"no-U", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 }, + {"set-noatime", 0, POPT_ARG_VAL, &set_noatime, 1, 0, 0 }, + {"no-set-noatime", 0, POPT_ARG_VAL, &set_noatime, 0, 0, 0 }, {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 1, 0, 0 }, {"no-omit-dir-times",0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 }, {"no-O", 0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 }, @@ -1543,6 +1552,11 @@ int parse_arguments(int *argc_p, const char ***argv_p) itemize_changes++; break; + case 'U': + if (++preserve_atimes > 1) + set_noatime = 1; + break; + case 'v': verbose++; break; @@ -2493,6 +2507,11 @@ void server_options(char **args, int *argc_p) argstr[x++] = 'D'; if (preserve_times) argstr[x++] = 't'; + if (preserve_atimes) { + argstr[x++] = 'U'; + if (preserve_atimes > 1) + argstr[x++] = 'U'; + } if (preserve_perms) argstr[x++] = 'p'; else if (preserve_executability && am_sender) @@ -2831,6 +2850,9 @@ void server_options(char **args, int *argc_p) if (preallocate_files && am_sender) args[ac++] = "--preallocate"; + if (set_noatime && preserve_atimes <= 1) + args[ac++] = "--set-noatime"; + if (ac > MAX_SERVER_ARGS) { /* Not possible... */ rprintf(FERROR, "argc overflow in server_options().\n"); exit_cleanup(RERR_MALLOC); @@ -63,6 +63,15 @@ iconv_t ic_chck = (iconv_t)-1; iconv_t ic_send = (iconv_t)-1, ic_recv = (iconv_t)-1; # endif +#define UPDATED_OWNER (1<<0) +#define UPDATED_GROUP (1<<1) +#define UPDATED_MTIME (1<<2) +#define UPDATED_ATIME (1<<3) +#define UPDATED_ACLS (1<<4) +#define UPDATED_MODE (1<<5) + +#define UPDATED_TIMES (UPDATED_MTIME|UPDATED_ATIME) + static const char *default_charset(void) { # if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET @@ -540,7 +549,10 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, keep_dirlinks && S_ISDIR(sxp->st.st_mode)); } } - updated = 1; + if (change_uid) + updated |= UPDATED_OWNER; + if (change_gid) + updated |= UPDATED_GROUP; } #ifdef SUPPORT_XATTRS @@ -553,23 +565,44 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, if (!preserve_times || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode)) || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode))) - flags |= ATTRS_SKIP_MTIME; + flags |= ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME; + else if (sxp != &sx2) + memcpy(&sx2.st, &sxp->st, sizeof (sx2.st)); + if (!atimes_ndx || S_ISDIR(sxp->st.st_mode)) + flags |= ATTRS_SKIP_ATIME; if (!(flags & ATTRS_SKIP_MTIME) && (sxp->st.st_mtime != file->modtime #ifdef ST_MTIME_NSEC || (flags & ATTRS_SET_NANO && NSEC_BUMP(file) && (uint32)sxp->st.ST_MTIME_NSEC != F_MOD_NSEC(file)) #endif )) { - int ret = set_modtime(fname, file->modtime, F_MOD_NSEC_or_0(file), sxp->st.st_mode); + sx2.st.st_mtime = file->modtime; +#ifdef ST_MTIME_NSEC + sx2.st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file); +#endif + updated |= UPDATED_MTIME; + } + if (!(flags & ATTRS_SKIP_ATIME)) { + time_t file_atime = F_ATIME(file); + if (cmp_time(sxp->st.st_atime, 0, file_atime, 0) != 0) { + sx2.st.st_atime = file_atime; +#ifdef ST_ATIME_NSEC + sx2.st.ST_ATIME_NSEC = 0; +#endif + updated |= UPDATED_ATIME; + } + } + if (updated & UPDATED_TIMES) { + int ret = set_times(fname, &sx2.st); if (ret < 0) { rsyserr(FERROR_XFER, errno, "failed to set times on %s", full_fname(fname)); goto cleanup; } - if (ret == 0) /* ret == 1 if symlink could not be set */ - updated = 1; - else + if (ret > 0) { /* ret == 1 if symlink could not be set */ + updated &= ~UPDATED_TIMES; file->flags |= FLAG_TIME_FAILED; + } } #ifdef SUPPORT_ACLS @@ -581,7 +614,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, * need to chmod(). */ if (preserve_acls && !S_ISLNK(new_mode)) { if (set_acl(fname, file, sxp, new_mode) > 0) - updated = 1; + updated |= UPDATED_ACLS; } #endif @@ -595,7 +628,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, goto cleanup; } if (ret == 0) /* ret == 1 if symlink could not be set */ - updated = 1; + updated |= UPDATED_MODE; } #endif @@ -676,7 +709,7 @@ int finish_transfer(const char *fname, const char *fnametmp, /* Change permissions before putting the file into place. */ set_file_attrs(fnametmp, file, NULL, fnamecmp, - ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME); + ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME); /* move tmp file over real file */ if (DEBUG_GTE(RECV, 1)) @@ -701,7 +734,7 @@ int finish_transfer(const char *fname, const char *fnametmp, do_set_file_attrs: set_file_attrs(fnametmp, file, NULL, fnamecmp, - ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME); + ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME); if (temp_copy_name) { if (do_rename(fnametmp, fname) < 0) { @@ -62,6 +62,7 @@ #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */ #define XMIT_IO_ERROR_ENDLIST (1<<12) /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */ #define XMIT_MOD_NSEC (1<<13) /* protocols 31 - now */ +#define XMIT_SAME_ATIME (1<<14) /* protocols ?? - now */ /* These flags are used in the live flist data. */ @@ -168,6 +169,7 @@ #define ATTRS_REPORT (1<<0) #define ATTRS_SKIP_MTIME (1<<1) #define ATTRS_SET_NANO (1<<2) +#define ATTRS_SKIP_ATIME (1<<3) #define FULL_FLUSH 1 #define NORMAL_FLUSH 0 @@ -394,10 +396,13 @@ enum delret { #ifdef CAN_SET_NSEC #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC #define ST_MTIME_NSEC st_mtim.tv_nsec +#define ST_ATIME_NSEC st_atim.tv_nsec #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC) #define ST_MTIME_NSEC st_mtimensec +#define ST_ATIME_NSEC st_atimensec #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC) #define ST_MTIME_NSEC st_mtimespec.tv_nsec +#define ST_ATIME_NSEC st_atimespec.tv_nsec #endif #endif @@ -700,9 +705,29 @@ struct ht_int64_node { #endif #endif +#if SIZEOF_CHARP == 4 +# define PTRS_ARE_32 1 +# define PTR_EXTRA_CNT 1 +#elif SIZEOF_CHARP == 8 +# define PTRS_ARE_64 1 +# define PTR_EXTRA_CNT EXTRA64_CNT +#else +# error Character pointers are not 4 or 8 bytes. +#endif + union file_extras { int32 num; uint32 unum; +#ifdef PTRS_ARE_32 + const char* ptr; +#endif +}; + +union file_extras64 { + int64 num; +#ifdef PTRS_ARE_64 + const char* ptr; +#endif }; struct file_struct { @@ -716,6 +741,9 @@ struct file_struct { extern int file_extra_cnt; extern int inc_recurse; +extern int atimes_ndx; +extern int pathname_ndx; +extern int depth_ndx; extern int uid_ndx; extern int gid_ndx; extern int acls_ndx; @@ -723,14 +751,18 @@ extern int xattrs_ndx; #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename)) #define EXTRA_LEN (sizeof (union file_extras)) -#define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN) #define DEV_EXTRA_CNT 2 #define DIRNODE_EXTRA_CNT 3 +#define EXTRA64_CNT ((sizeof (union file_extras64) + EXTRA_LEN - 1) / EXTRA_LEN) #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN) #define REQ_EXTRA(f,ndx) ((union file_extras*)(f) - (ndx)) #define OPT_EXTRA(f,bump) ((union file_extras*)(f) - file_extra_cnt - 1 - (bump)) +/* These are guaranteed to be allocated first in the array so that they + * are aligned for direct int64-pointer access. */ +#define REQ_EXTRA64(f,ndx) ((union file_extras64*)REQ_EXTRA(f,ndx)) + #define NSEC_BUMP(f) ((f)->flags & FLAG_MOD_NSEC ? 1 : 0) #define LEN64_BUMP(f) ((f)->flags & FLAG_LENGTH64 ? 1 : 0) #define START_BUMP(f) (NSEC_BUMP(f) + LEN64_BUMP(f)) @@ -752,10 +784,14 @@ extern int xattrs_ndx; #define F_SYMLINK(f) ((f)->basename + strlen((f)->basename) + 1) /* The sending side always has this available: */ -#define F_PATHNAME(f) (*(const char**)REQ_EXTRA(f, PTR_EXTRA_CNT)) +#ifdef PTRS_ARE_32 +#define F_PATHNAME(f) REQ_EXTRA(f, pathname_ndx)->ptr +#else +#define F_PATHNAME(f) REQ_EXTRA64(f, pathname_ndx)->ptr +#endif /* The receiving side always has this available: */ -#define F_DEPTH(f) REQ_EXTRA(f, 1)->num +#define F_DEPTH(f) REQ_EXTRA(f, depth_ndx)->num /* When the associated option is on, all entries will have these present: */ #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum @@ -763,6 +799,7 @@ extern int xattrs_ndx; #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num #define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num +#define F_ATIME(f) REQ_EXTRA64(f, atimes_ndx)->num /* These items are per-entry optional: */ #define F_HL_GNUM(f) OPT_EXTRA(f, START_BUMP(f))->num /* non-dirs */ @@ -378,6 +378,8 @@ to the detailed description below for a complete description. verb( --specials preserve special files -D same as --devices --specials -t, --times preserve modification times + -U, --atimes preserve access (use) times + --set-noatime avoid changing the atime on accessed files -O, --omit-dir-times omit directories from --times -J, --omit-link-times omit symlinks from --times --super receiver attempts super-user activities @@ -1240,6 +1242,25 @@ cause the next transfer to behave as if it used bf(-I), causing all files to be updated (though rsync's delta-transfer algorithm will make the update fairly efficient if the files haven't actually changed, you're much better off using bf(-t)). +dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the +destination files to the same value as the source files. + +If repeated, it also sets the bf(--set-noatime) option, which can help you +to make the sending and receiving systems have the same access times on the +transferred files without needing to run rsync an extra time after a file is +transferred. + +Note that some older rsync versions (prior to 3.1.4) may have been built with +a pre-release bf(--atimes) patch that does not imply bf(--set-noatime) when +this option is repeated. + +dit(bf(--set-noatime)) This tells rsync to open files with the O_NOATIME +flag (on systems that support it) to avoid changing the access time of the +files that are being transferred. If your OS does not support the O_NOATIME +flag then rsync will silently ignore this option. Note also that some +filesystems are mounted to avoid updating the atime on read access even +without the O_NOATIME flag being set. + dit(bf(-O, --omit-dir-times)) This tells rsync to omit directories when it is preserving modification times (see bf(--times)). If NFS is sharing the directories on the receiving side, it is a good idea to use bf(-O). @@ -2263,7 +2284,10 @@ quote(itemization( sender's value (requires bf(--owner) and super-user privileges). it() A bf(g) means the group is different and is being updated to the sender's value (requires bf(--group) and the authority to set the group). - it() The bf(u) slot is reserved for future use. + it() A bf(u) means the access (use) time is different and is being updated to + the sender's value (requires bf(--atimes)). An alternate value of bf(U) + means that the access time will be set to the transfer time, which happens + when a symlink or directory is updated. it() The bf(a) means that the ACL information changed. it() The bf(x) means that the extended attribute information changed. )) @@ -42,6 +42,7 @@ extern int inplace; extern int preallocate_files; extern int preserve_perms; extern int preserve_executability; +extern int set_noatime; #ifndef S_BLKSIZE # if defined hpux || defined __hpux__ || defined __hpux @@ -202,6 +203,11 @@ int do_open(const char *pathname, int flags, mode_t mode) RETURN_ERROR_IF_RO_OR_LO; } +#ifdef O_NOATIME + if (set_noatime) + flags |= O_NOATIME; +#endif + return open(pathname, flags | O_BINARY, mode); } @@ -380,54 +386,78 @@ int do_setattrlist_times(const char *fname, time_t modtime, uint32 mod_nsec) #endif #ifdef HAVE_UTIMENSAT -int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec) +int do_utimensat(const char *fname, STRUCT_STAT *stp) { struct timespec t[2]; if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; - t[0].tv_sec = 0; - t[0].tv_nsec = UTIME_NOW; - t[1].tv_sec = modtime; - t[1].tv_nsec = mod_nsec; + t[0].tv_sec = stp->st_atime; +#ifdef ST_ATIME_NSEC + t[0].tv_nsec = stp->ST_ATIME_NSEC; +#else + t[0].tv_nsec = 0; +#endif + t[1].tv_sec = stp->st_mtime; +#ifdef ST_MTIME_NSEC + t[1].tv_nsec = stp->ST_MTIME_NSEC; +#else + t[1].tv_nsec = 0; +#endif return utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW); } #endif #ifdef HAVE_LUTIMES -int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec) +int do_lutimes(const char *fname, STRUCT_STAT *stp) { struct timeval t[2]; if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; - t[0].tv_sec = time(NULL); + t[0].tv_sec = stp->st_atime; +#ifdef ST_ATIME_NSEC + t[0].tv_usec = stp->ST_ATIME_NSEC / 1000; +#else t[0].tv_usec = 0; - t[1].tv_sec = modtime; - t[1].tv_usec = mod_nsec / 1000; +#endif + t[1].tv_sec = stp->st_mtime; +#ifdef ST_MTIME_NSEC + t[1].tv_usec = stp->ST_MTIME_NSEC / 1000; +#else + t[1].tv_usec = 0; +#endif return lutimes(fname, t); } #endif #ifdef HAVE_UTIMES -int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec) +int do_utimes(const char *fname, STRUCT_STAT *stp) { struct timeval t[2]; if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; - t[0].tv_sec = time(NULL); + t[0].tv_sec = stp->st_atime; +#ifdef ST_ATIME_NSEC + t[0].tv_usec = stp->ST_ATIME_NSEC / 1000; +#else t[0].tv_usec = 0; - t[1].tv_sec = modtime; - t[1].tv_usec = mod_nsec / 1000; +#endif + t[1].tv_sec = stp->st_mtime; +#ifdef ST_MTIME_NSEC + t[1].tv_usec = stp->ST_MTIME_NSEC / 1000; +#else + t[1].tv_usec = 0; +#endif return utimes(fname, t); } #elif defined HAVE_UTIME -int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec)) +int do_utime(const char *fname, STRUCT_STAT *stp) { #ifdef HAVE_STRUCT_UTIMBUF struct utimbuf tbuf; @@ -439,12 +469,12 @@ int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec)) RETURN_ERROR_IF_RO_OR_LO; # ifdef HAVE_STRUCT_UTIMBUF - tbuf.actime = time(NULL); - tbuf.modtime = modtime; + tbuf.actime = stp->st_atime; + tbuf.modtime = stp->st_mtime; return utime(fname, &tbuf); # else - t[0] = time(NULL); - t[1] = modtime; + t[0] = stp->st_atime; + t[1] = stp->st_mtime; return utime(fname, t); # endif } @@ -31,6 +31,7 @@ int module_dirlen = 0; int preserve_acls = 0; int preserve_times = 0; int preserve_xattrs = 0; +int set_noatime = 0; char *partial_dir; char *module_dir; filter_rule_list daemon_filter_list; diff --git a/testsuite/atimes.test b/testsuite/atimes.test new file mode 100644 index 00000000..bd3f2927 --- /dev/null +++ b/testsuite/atimes.test @@ -0,0 +1,17 @@ +#! /bin/sh + +# Test rsync copying atimes + +. "$suitedir/rsync.fns" + +mkdir "$fromdir" + +touch "$fromdir/foo" +touch -a -t 200102031717.42 "$fromdir/foo" + +TLS_ARGS=--atimes + +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir" + +# The script would have aborted on error, so getting here means we've won. +exit 0 diff --git a/testsuite/daemon.test b/testsuite/daemon.test index 06728e26..a8acd04e 100644 --- a/testsuite/daemon.test +++ b/testsuite/daemon.test @@ -27,7 +27,7 @@ outfile="$scratchdir/rsync.out" SSH="src/support/lsh.sh --no-cd" FILE_REPL='s/^\([^d][^ ]*\) *\(..........[0-9]\) /\1 \2 /' DIR_REPL='s/^\(d[^ ]*\) *[0-9][.,0-9]* /\1 DIR /' -LS_REPL='s;[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9];####/##/## ##:##:##;' +LS_REPL='s;[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ;####/##/## ##:##:## ;g' build_rsyncd_conf @@ -91,3 +91,12 @@ drwxr-xr-x DIR ####/##/## ##:##:## foo EOT diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed" +$RSYNC -rU localhost::test-from/f* \ + | sed "$FILE_REPL" | sed "$DIR_REPL" | sed "$LS_REPL" \ + | tee "$outfile" +cat <<EOT >"$chkfile" +drwxr-xr-x DIR ####/##/## ##:##:## foo +-rw-r--r-- 4 ####/##/## ##:##:## ####/##/## ##:##:## foo/one +EOT +diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed" + diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns index bd5768b9..65d071ca 100644 --- a/testsuite/rsync.fns +++ b/testsuite/rsync.fns @@ -219,6 +219,14 @@ checkit() { # We can just write everything to stdout/stderr, because the # wrapper hides it unless there is a problem. + case "x$TLS_ARGS" in + *--atimes*) + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from" + ;; + *) + ;; + esac + echo "Running: \"$1\"" eval "$1" status=$? @@ -226,10 +234,17 @@ checkit() { failed="$failed status=$status" fi + case "x$TLS_ARGS" in + *--atimes*) + ;; + *) + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from" + ;; + esac + echo "-------------" echo "check how the directory listings compare with diff:" echo "" - ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from" ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to" diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed="$failed dir-diff" @@ -52,6 +52,7 @@ int nsec_times = 0; int preserve_perms = 0; int preserve_executability = 0; int preallocate_files = 0; +int set_noatime = 0; int inplace = 0; #ifdef SUPPORT_XATTRS @@ -111,6 +112,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst) #endif +static int display_atimes = 0; + static void failed(char const *what, char const *where) { fprintf(stderr, PROGRAM ": %s %s: %s\n", @@ -118,13 +121,38 @@ static void failed(char const *what, char const *where) exit(1); } +static void storetime(char *dest, size_t destsize, time_t t, int nsecs) +{ + if (t) { + int len; + struct tm *mt = gmtime(&t); + + len = snprintf(dest, destsize, + " %04d-%02d-%02d %02d:%02d:%02d", + (int)mt->tm_year + 1900, + (int)mt->tm_mon + 1, + (int)mt->tm_mday, + (int)mt->tm_hour, + (int)mt->tm_min, + (int)mt->tm_sec); + if (nsecs >= 0 && len >= 0) + snprintf(dest + len, destsize - len, ".%09d", nsecs); + } else { + int has_nsecs = nsecs >= 0 ? 1 : 0; + int len = MIN(20 + 10*has_nsecs, (int)destsize - 1); + memset(dest, ' ', len); + dest[len] = '\0'; + } +} + static void list_file(const char *fname) { STRUCT_STAT buf; char permbuf[PERMSTRING_SIZE]; - struct tm *mt; - char datebuf[50]; + char mtimebuf[50]; + char atimebuf[50]; char linkbuf[4096]; + int nsecs; if (do_lstat(fname, &buf) < 0) failed("stat", fname); @@ -150,58 +178,47 @@ static void list_file(const char *fname) buf.st_uid = buf.st_gid = 0; strlcpy(linkbuf, " -> ", sizeof linkbuf); /* const-cast required for silly UNICOS headers */ - len = do_readlink((char *) fname, linkbuf+4, sizeof(linkbuf) - 4); + len = do_readlink((char*)fname, linkbuf+4, sizeof linkbuf - 4); if (len == -1) failed("do_readlink", fname); else /* it's not nul-terminated */ linkbuf[4+len] = 0; } else { - linkbuf[0] = 0; + linkbuf[0] = '\0'; } permstring(permbuf, buf.st_mode); - - if (buf.st_mtime) { - int len; - mt = gmtime(&buf.st_mtime); - - len = snprintf(datebuf, sizeof datebuf, - "%04d-%02d-%02d %02d:%02d:%02d", - (int)mt->tm_year + 1900, - (int)mt->tm_mon + 1, - (int)mt->tm_mday, - (int)mt->tm_hour, - (int)mt->tm_min, - (int)mt->tm_sec); #ifdef ST_MTIME_NSEC - if (nsec_times) { - snprintf(datebuf + len, sizeof datebuf - len, - ".%09d", (int)buf.ST_MTIME_NSEC); - } + if (nsec_times) + nsecs = (int)buf.ST_MTIME_NSEC; + else #endif - } else { - int len = MIN(19 + 9*nsec_times, (int)sizeof datebuf - 1); - memset(datebuf, ' ', len); - datebuf[len] = '\0'; - } + nsecs = -1; + storetime(mtimebuf, sizeof mtimebuf, buf.st_mtime, nsecs); + if (display_atimes) + storetime(atimebuf, sizeof atimebuf, S_ISDIR(buf.st_mode) ? 0 : buf.st_atime, -1); + else + atimebuf[0] = '\0'; /* TODO: Perhaps escape special characters in fname? */ - printf("%s ", permbuf); + if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) { printf("%5ld,%6ld", (long)major(buf.st_rdev), (long)minor(buf.st_rdev)); } else printf("%15s", do_big_num(buf.st_size, 1, NULL)); - printf(" %6ld.%-6ld %6ld %s %s%s\n", + + printf(" %6ld.%-6ld %6ld%s%s %s%s\n", (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink, - datebuf, fname, linkbuf); + mtimebuf, atimebuf, fname, linkbuf); } static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"atimes", 'U', POPT_ARG_NONE, &display_atimes, 0, 0, 0}, {"link-times", 'l', POPT_ARG_NONE, &link_times, 0, 0, 0 }, {"link-owner", 'L', POPT_ARG_NONE, &link_owner, 0, 0, 0 }, #ifdef SUPPORT_XATTRS @@ -220,6 +237,7 @@ static void NORETURN tls_usage(int ret) fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n"); fprintf(F,"Trivial file listing program for portably checking rsync\n"); fprintf(F,"\nOptions:\n"); + fprintf(F," -U, --atimes display access (last-used) times\n"); fprintf(F," -l, --link-times display the time on a symlink\n"); fprintf(F," -L, --link-owner display the owner+group on a symlink\n"); #ifdef SUPPORT_XATTRS diff --git a/trimslash.c b/trimslash.c index 49331fad..2891279e 100644 --- a/trimslash.c +++ b/trimslash.c @@ -29,6 +29,7 @@ int list_only = 0; int preserve_perms = 0; int preserve_executability = 0; int preallocate_files = 0; +int set_noatime = 0; int inplace = 0; int @@ -117,14 +117,15 @@ void print_child_argv(const char *prefix, char **cmd) /* This returns 0 for success, 1 for a symlink if symlink time-setting * is not possible, or -1 for any other error. */ -int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) +int set_times(const char *fname, STRUCT_STAT *stp) { static int switch_step = 0; if (DEBUG_GTE(TIME, 1)) { - rprintf(FINFO, "set modtime of %s to (%ld) %s", - fname, (long)modtime, - asctime(localtime(&modtime))); + rprintf(FINFO, + "set modtime, atime of %s to (%ld) %s, (%ld) %s\n", + fname, (long)stp->st_mtime, + timestring(stp->st_mtime), (long)stp->st_atime, timestring(stp->st_atime)); } switch (switch_step) { @@ -139,7 +140,7 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) #ifdef HAVE_UTIMENSAT #include "case_N.h" - if (do_utimensat(fname, modtime, mod_nsec) == 0) + if (do_utimensat(fname, stp) == 0) break; if (errno != ENOSYS) return -1; @@ -148,7 +149,7 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) #ifdef HAVE_LUTIMES #include "case_N.h" - if (do_lutimes(fname, modtime, mod_nsec) == 0) + if (do_lutimes(fname, stp) == 0) break; if (errno != ENOSYS) return -1; @@ -159,16 +160,16 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) switch_step++; if (preserve_times & PRESERVE_LINK_TIMES) { preserve_times &= ~PRESERVE_LINK_TIMES; - if (S_ISLNK(mode)) + if (S_ISLNK(stp->st_mode)) return 1; } #include "case_N.h" #ifdef HAVE_UTIMES - if (do_utimes(fname, modtime, mod_nsec) == 0) + if (do_utimes(fname, stp) == 0) break; #else - if (do_utime(fname, modtime, mod_nsec) == 0) + if (do_utime(fname, stp) == 0) break; #endif @@ -1332,18 +1333,14 @@ int unsafe_symlink(const char *dest, const char *src) /* Return the date and time as a string. Some callers tweak returned buf. */ char *timestring(time_t t) { - static char TimeBuf[200]; + static int ndx = 0; + static char buffers[4][20]; /* We support 4 simultaneous timestring results. */ + char *TimeBuf = buffers[ndx = (ndx + 1) % 4]; struct tm *tm = localtime(&t); - char *p; -#ifdef HAVE_STRFTIME - strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm); -#else - strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf); -#endif - - if ((p = strchr(TimeBuf, '\n')) != NULL) - *p = '\0'; + snprintf(TimeBuf, sizeof buffers[0], "%4d/%02d/%02d %02d:%02d:%02d", + (int)tm->tm_year + 1900, (int)tm->tm_mon + 1, (int)tm->tm_mday, + (int)tm->tm_hour, (int)tm->tm_min, (int)tm->tm_sec); return TimeBuf; } |