Skip to content

Commit

Permalink
Add --crtimes option.
Browse files Browse the repository at this point in the history
  • Loading branch information
WayneD committed Jul 22, 2020
1 parent 9f7506a commit 974f49e
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 20 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ jobs:
- name: info
run: rsync --version
- name: check
run: sudo RSYNC_MAX_SKIPPED=0 make check
run: sudo RSYNC_MAX_SKIPPED=1 make check
- name: check30
run: sudo RSYNC_MAX_SKIPPED=0 make check30
run: sudo RSYNC_MAX_SKIPPED=1 make check30
- name: check29
run: sudo RSYNC_MAX_SKIPPED=0 make check29
run: sudo RSYNC_MAX_SKIPPED=1 make check29
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
Expand Down
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
protocol (so if you used this patch in the past, be sure to update your
converter script to use newlines instead of null chars).

- Added `--crtimes` (`-N`) option for preserving the file's create time (on
an OS that supports that, such as macOS).

- Added the ability to specify "@netgroup" names to the `hosts allow` and
`hosts deny` daemon parameters. This is a finalized version of the
netgroup-auth patch from the patches repo.
Expand Down
9 changes: 8 additions & 1 deletion compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ extern int protect_args;
extern int preserve_uid;
extern int preserve_gid;
extern int preserve_atimes;
extern int preserve_crtimes;
extern int preserve_acls;
extern int preserve_xattrs;
extern int xfer_flags_as_varint;
Expand Down Expand Up @@ -76,7 +77,7 @@ int do_negotiated_strings = 0;
int xmit_id0_names = 0;

/* These index values are for the file-list's extra-attribute array. */
int pathname_ndx, depth_ndx, atimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
int pathname_ndx, depth_ndx, atimes_ndx, crtimes_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 */
Expand Down Expand Up @@ -555,6 +556,8 @@ void setup_protocol(int f_out,int f_in)
* aligned for direct int64-pointer memory access. */
if (preserve_atimes)
atimes_ndx = (file_extra_cnt += EXTRA64_CNT);
if (preserve_crtimes)
crtimes_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
Expand Down Expand Up @@ -719,6 +722,10 @@ void setup_protocol(int f_out,int f_in)
proper_seed_order = compat_flags & CF_CHKSUM_SEED_FIX ? 1 : 0;
xfer_flags_as_varint = compat_flags & CF_VARINT_FLIST_FLAGS ? 1 : 0;
xmit_id0_names = compat_flags & CF_ID0_NAMES ? 1 : 0;
if (!xfer_flags_as_varint && preserve_crtimes) {
fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --crtimes.\n");
exit_cleanup(RERR_PROTOCOL);
}
if (am_sender) {
receiver_symlink_times = am_server
? strchr(client_info, 'L') != NULL
Expand Down
45 changes: 45 additions & 0 deletions flist.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extern int delete_during;
extern int missing_args;
extern int eol_nulls;
extern int atimes_ndx;
extern int crtimes_ndx;
extern int relative_paths;
extern int implied_dirs;
extern int ignore_perishable;
Expand Down Expand Up @@ -378,6 +379,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
int ndx, int first_ndx)
{
static time_t modtime, atime;
#ifdef SUPPORT_CRTIMES
static time_t crtime;
#endif
static mode_t mode;
#ifdef SUPPORT_HARD_LINKS
static int64 dev;
Expand Down Expand Up @@ -483,6 +487,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
else
atime = F_ATIME(file);
}
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx) {
crtime = F_CRTIME(file);
if (crtime == modtime)
xflags |= XMIT_CRTIME_EQ_MTIME;
}
#endif

#ifdef SUPPORT_HARD_LINKS
if (tmp_dev != -1) {
Expand Down Expand Up @@ -570,6 +581,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
}
if (xflags & XMIT_MOD_NSEC)
write_varint(f, F_MOD_NSEC(file));
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx && !(xflags & XMIT_CRTIME_EQ_MTIME))
write_varlong(f, crtime, 4);
#endif
if (!(xflags & XMIT_SAME_MODE))
write_int(f, to_wire_mode(mode));
if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
Expand Down Expand Up @@ -662,6 +677,9 @@ 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, atime;
#ifdef SUPPORT_CRTIMES
static time_t crtime;
#endif
static mode_t mode;
#ifdef SUPPORT_HARD_LINKS
static int64 dev;
Expand Down Expand Up @@ -776,6 +794,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
mode = first->mode;
if (atimes_ndx && !S_ISDIR(mode))
atime = F_ATIME(first);
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx)
crtime = F_CRTIME(first);
#endif
if (preserve_uid)
uid = F_OWNER(first);
if (preserve_gid)
Expand Down Expand Up @@ -815,6 +837,21 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
modtime_nsec = read_varint(f);
else
modtime_nsec = 0;
#endif
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx) {
if (xflags & XMIT_CRTIME_EQ_MTIME)
crtime = modtime;
else
crtime = read_varlong(f, 4);
#if SIZEOF_TIME_T < SIZEOF_INT64
if (!am_generator && (int64)(time_t)crtime != crtime) {
rprintf(FERROR_XFER,
"Create time value of %s truncated on receiver.\n",
lastname);
}
#endif
}
#endif
if (!(xflags & XMIT_SAME_MODE))
mode = from_wire_mode(read_int(f));
Expand Down Expand Up @@ -997,6 +1034,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
}
if (atimes_ndx && !S_ISDIR(mode))
F_ATIME(file) = atime;
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx)
F_CRTIME(file) = crtime;
#endif
if (unsort_ndx)
F_NDX(file) = flist->used + flist->ndx_start;

Expand Down Expand Up @@ -1394,6 +1435,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
file->flags |= FLAG_OWNED_BY_US;
if (atimes_ndx && !S_ISDIR(file->mode))
F_ATIME(file) = st.st_atime;
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx)
F_CRTIME(file) = get_create_time(fname);
#endif

if (basename != thisname)
file->dirname = lastdir;
Expand Down
34 changes: 29 additions & 5 deletions generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,19 @@ static inline int mtime_differs(STRUCT_STAT *stp, struct file_struct *file)
#endif
}

static inline int any_time_differs(stat_x *sxp, struct file_struct *file, UNUSED(const char *fname))
{
int differs = mtime_differs(&sxp->st, file);
#ifdef SUPPORT_CRTIMES
if (!differs && crtimes_ndx) {
if (sxp->crtime == 0)
sxp->crtime = get_create_time(fname);
differs = !same_time(sxp->crtime, 0, F_CRTIME(file), 0);
}
#endif
return differs;
}

static inline int perms_differ(struct file_struct *file, stat_x *sxp)
{
if (preserve_perms)
Expand Down Expand Up @@ -450,7 +463,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
{
if (S_ISLNK(file->mode)) {
#ifdef CAN_SET_SYMLINK_TIMES
if (preserve_times & PRESERVE_LINK_TIMES && mtime_differs(&sxp->st, file))
if (preserve_times & PRESERVE_LINK_TIMES && any_time_differs(sxp, file, fname))
return 0;
#endif
#ifdef CAN_CHMOD_SYMLINK
Expand All @@ -470,7 +483,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
return 0;
#endif
} else {
if (preserve_times && mtime_differs(&sxp->st, file))
if (preserve_times && any_time_differs(sxp, file, fname))
return 0;
if (perms_differ(file, sxp))
return 0;
Expand Down Expand Up @@ -512,6 +525,14 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
if (atimes_ndx && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
&& !same_time(F_ATIME(file), 0, sxp->st.st_atime, 0))
iflags |= ITEM_REPORT_ATIME;
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx) {
if (sxp->crtime == 0)
sxp->crtime = get_create_time(fnamecmp);
if (!same_time(sxp->crtime, 0, F_CRTIME(file), 0))
iflags |= ITEM_REPORT_CRTIME;
}
#endif
#if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
if (S_ISLNK(file->mode)) {
;
Expand Down Expand Up @@ -1131,6 +1152,7 @@ static void list_file_entry(struct file_struct *f)
int size_width = human_readable ? 14 : 11;
int mtime_width = 1 + strlen(mtime_str);
int atime_width = atimes_ndx ? mtime_width : 0;
int crtime_width = crtimes_ndx ? mtime_width : 0;

if (!F_IS_ACTIVE(f)) {
/* this can happen if duplicate names were removed */
Expand All @@ -1141,10 +1163,11 @@ static void list_file_entry(struct file_struct *f)

if (missing_args == 2 && f->mode == 0) {
rprintf(FINFO, "%-*s %s\n",
10 + 1 + size_width + mtime_width + atime_width, "*missing",
10 + 1 + size_width + mtime_width + atime_width + crtime_width, "*missing",
f_name(f, NULL));
} else {
const char *atime_str = atimes_ndx && !S_ISDIR(f->mode) ? timestring(F_ATIME(f)) : "";
const char *crtime_str = crtimes_ndx ? timestring(F_CRTIME(f)) : "";
const char *arrow, *lnk;

permstring(permbuf, f->mode);
Expand All @@ -1157,9 +1180,9 @@ static void list_file_entry(struct file_struct *f)
#endif
arrow = lnk = "";

rprintf(FINFO, "%s %*s %s%*s %s%s%s\n",
rprintf(FINFO, "%s %*s %s%*s%*s %s%s%s\n",
permbuf, size_width, human_num(F_LENGTH(f)),
timestring(f->modtime), atime_width, atime_str,
timestring(f->modtime), atime_width, atime_str, crtime_width, crtime_str,
f_name(f, NULL), arrow, lnk);
}
}
Expand Down Expand Up @@ -1255,6 +1278,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
return;
}
}
sx.crtime = 0;

if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
int i;
Expand Down
5 changes: 3 additions & 2 deletions log.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,8 +720,9 @@ 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) ? '.'
: S_ISLNK(file->mode) ? 'U' : 'u';
c[8] = !(iflags & (ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME)) ? '.'
: BITS_SET(iflags, ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME) ? 'b'
: iflags & ITEM_REPORT_ATIME ? 'u' : 'n';
c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
c[11] = '\0';
Expand Down
16 changes: 16 additions & 0 deletions options.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ int preserve_uid = 0;
int preserve_gid = 0;
int preserve_times = 0;
int preserve_atimes = 0;
int preserve_crtimes = 0;
int update_only = 0;
int open_noatime = 0;
int cvs_exclude = 0;
Expand Down Expand Up @@ -670,6 +671,11 @@ static void print_info_flags(enum logcode f)
#endif
"stop-at",

#ifndef SUPPORT_CRTIMES
"no "
#endif
"crtimes",

"*Optimizations",

#ifndef HAVE_SIMD
Expand Down Expand Up @@ -838,6 +844,9 @@ static struct poptOption long_options[] = {
{"no-U", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
{"open-noatime", 0, POPT_ARG_VAL, &open_noatime, 1, 0, 0 },
{"no-open-noatime", 0, POPT_ARG_VAL, &open_noatime, 0, 0, 0 },
{"crtimes", 'N', POPT_ARG_VAL, &preserve_crtimes, 1, 0, 0 },
{"no-crtimes", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 },
{"no-N", 0, POPT_ARG_VAL, &preserve_crtimes, 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 },
Expand Down Expand Up @@ -1211,6 +1220,9 @@ static void set_refuse_options(void)
#ifndef HAVE_SETVBUF
parse_one_refuse_match(0, "outbuf", list_end);
#endif
#ifndef SUPPORT_CRTIMES
parse_one_refuse_match(0, "crtimes", list_end);
#endif

/* Now we use the descrip values to actually mark the options for refusal. */
for (op = long_options; op != list_end; op++) {
Expand Down Expand Up @@ -2738,6 +2750,10 @@ void server_options(char **args, int *argc_p)
if (preserve_atimes > 1)
argstr[x++] = 'U';
}
#ifdef SUPPORT_CRTIMES
if (preserve_crtimes)
argstr[x++] = 'N';
#endif
if (preserve_perms)
argstr[x++] = 'p';
else if (preserve_executability && am_sender)
Expand Down
15 changes: 11 additions & 4 deletions rsync.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ detailed description below for a complete description.
--times, -t preserve modification times
--atimes, -U preserve access (use) times
--open-noatime avoid changing the atime on opened files
--crtimes, -N preserve create times (newness)
--omit-dir-times, -O omit directories from --times
--omit-link-times, -J omit symlinks from --times
--super receiver attempts super-user activities
Expand Down Expand Up @@ -1341,6 +1342,11 @@ your home directory (remove the '=' for that).
mounted to avoid updating the atime on read access even without the
O_NOATIME flag being set.

0. `--crtimes`, `-N,`

This tells rsync to set the create times (newness) of +the destination
files to the same value as the source files.

0. `--omit-dir-times`, `-O`

This tells rsync to omit directories when it is preserving modification
Expand Down Expand Up @@ -2673,10 +2679,11 @@ your home directory (remove the '=' for that).
value (requires `--owner` and super-user privileges).
- A `g` means the group is different and is being updated to the sender's
value (requires `--group` and the authority to set the group).
- A `u` means the access (use) time is different and is being updated to
the sender's value (requires `--atimes`). An alternate value of `U`
means that the access time will be set to the transfer time, which
happens when a symlink or directory is updated.
- A `u`|`n`|`b` indicates the following information: `u` means the access
(use) time is different and is being updated to the sender's value
(requires `--atimes`); `n` means the create time (newness) is different
and is being updated to the sender's value (requires `--crtimes`); `b`
means that both the access and create times are being updated.
- The `a` means that the ACL information is being changed.
- The `x` means that the extended attribute information is being changed.

Expand Down
Loading

2 comments on commit 974f49e

@benrubson
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WayneD, for info, on FreeBSD, interesting things about birth time in the last few lines :

UTIMES(2)                 FreeBSD System Calls Manual                UTIMES(2)

NAME
     utimes, lutimes, futimes, futimesat - set file access and modification
     times

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <sys/time.h>

     int
     utimes(const char *path, const struct timeval *times);

     int
     lutimes(const char *path, const struct timeval *times);

     int
     futimes(int fd, const struct timeval *times);

     int
     futimesat(int fd, const char *path, const struct timeval times[2]);

DESCRIPTION
     These interfaces are obsoleted by futimens(2) and utimensat(2) because
     they are not accurate to nanoseconds.

     The access and modification times of the file named by path or referenced
     by fd are changed as specified by the argument times.

     If times is NULL, the access and modification times are set to the
     current time.  The caller must be the owner of the file, have permission
     to write the file, or be the super-user.

     If times is non-NULL, it is assumed to point to an array of two timeval
     structures.  The access time is set to the value of the first element,
     and the modification time is set to the value of the second element.  For
     file systems that support file birth (creation) times (such as UFS2), the
     birth time will be set to the value of the second element if the second
     element is older than the currently set birth time.  To set both a birth
     time and a modification time, two calls are required; the first to set
     the birth time and the second to set the (presumably newer) modification
     time.  Ideally a new system call will be added that allows the setting of
     all three times at once.  The caller must be the owner of the file or be
     the super-user.

@WayneD
Copy link
Member Author

@WayneD WayneD commented on 974f49e Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm inclined to wait until FreeBSD adds a call to actually set the birth time before adding --crtimes support for them. Without that extra call it's only possible to set the birth time earlier, and only in a double-utime sequence of calls. It is nice that the birth time is present in the stat info, though, which is better than OS X's extra crtime query.

Please sign in to comment.