In the past there have been discussions about adding a switch to rsync to preserve the atime on files being copied by rsync. I needed this function for a project I'm working on and decided to invent it. I've attached the diffs. Note that this has the limitations describe in previous emails, namely that preserving atime causes ctime to not be preserved. *** Patch follows *** *** backup.c@@/main/original/1 Tue Apr 9 14:02:14 2002 --- backup.c Tue Apr 9 15:21:56 2002 *************** *** 110,116 **** rprintf(FERROR,"make_bak_dir stat %s : %s\n",fullpath,strerror(errno)); } else { st2=&st; ! set_modtime(fullpath,st2->st_mtime); if(do_lchown(fullpath,st2->st_uid,st2->st_gid)!=0) { rprintf(FERROR,"make_bak_dir chown %s : %s\n",fullpath,strerror(errno)); }; --- 110,117 ---- rprintf(FERROR,"make_bak_dir stat %s : %s\n",fullpath,strerror(errno)); } else { st2=&st; ! set_modtime(fullpath,st2->st_mtime, ! st2->st_atime); if(do_lchown(fullpath,st2->st_uid,st2->st_gid)!=0) { rprintf(FERROR,"make_bak_dir chown %s : %s\n",fullpath,strerror(errno)); }; *** sender.c@@/main/original/1 Tue Apr 9 14:03:44 2002 --- sender.c Fri Apr 12 11:09:52 2002 *************** *** 26,33 **** extern int io_error; extern int dry_run; extern int am_server; - /* receive the checksums for a buffer */ --- 26,36 ---- extern int io_error; extern int dry_run; extern int am_server; + extern int preserve_atime; /* receive the checksums for a buffer */ *************** *** 184,189 **** --- 197,204 ---- rprintf(FERROR,"fstat failed : %s\n",strerror(errno)); free_sums(s); close(fd); + if (preserve_atime) + set_modtime(fname, file->modtime, file->acctime); return; } *************** *** 266,271 **** --- 281,288 ---- if (!read_batch) { /* dw */ if (buf) unmap_file(buf); close(fd); + if (preserve_atime) + set_modtime(fname, file->modtime, file->acctime); } free_sums(s); *** util.c@@/main/original/1 Tue Apr 9 14:04:04 2002 --- util.c Tue Apr 9 15:22:00 2002 *************** *** 240,246 **** ! int set_modtime(char *fname, time_t modtime) { extern int dry_run; if (dry_run) --- 240,246 ---- ! int set_modtime(char *fname,time_t modtime, time_t acctime) { extern int dry_run; if (dry_run) *************** *** 255,271 **** { #ifdef HAVE_UTIMBUF struct utimbuf tbuf; ! tbuf.actime = time(NULL); tbuf.modtime = modtime; return utime(fname,&tbuf); #elif defined(HAVE_UTIME) time_t t[2]; ! t[0] = time(NULL); t[1] = modtime; return utime(fname,t); #else struct timeval t[2]; ! t[0].tv_sec = time(NULL); t[0].tv_usec = 0; t[1].tv_sec = modtime; t[1].tv_usec = 0; --- 255,271 ---- { #ifdef HAVE_UTIMBUF struct utimbuf tbuf; ! tbuf.actime = acctime; tbuf.modtime = modtime; return utime(fname,&tbuf); #elif defined(HAVE_UTIME) time_t t[2]; ! t[0] = acctime; t[1] = modtime; return utime(fname,t); #else struct timeval t[2]; ! t[0].tv_sec = acctime; t[0].tv_usec = 0; t[1].tv_sec = modtime; t[1].tv_usec = 0; *** rsync.c@@/main/original/1 Tue Apr 9 14:03:28 2002 --- rsync.c Fri Apr 12 11:00:16 2002 *************** *** 165,171 **** cmp_modtime(st->st_mtime, file->modtime) != 0) { /* don't complain about not setting times on directories because some filesystems can't do it */ ! if (set_modtime(fname,file->modtime) != 0 && !S_ISDIR(st->st_mode)) { rprintf(FERROR,"failed to set times on %s : %s\n", fname,strerror(errno)); --- 165,173 ---- cmp_modtime(st->st_mtime, file->modtime) != 0) { /* don't complain about not setting times on directories because some filesystems can't do it */ ! time_t now; ! now = time(NULL); ! if (set_modtime(fname,file->modtime, now) != 0 && !S_ISDIR(st->st_mode)) { rprintf(FERROR,"failed to set times on %s : %s\n", fname,strerror(errno)); *** rsync.h@@/main/original/1 Tue Apr 9 14:03:30 2002 --- rsync.h Tue Apr 9 15:22:00 2002 *************** *** 333,338 **** --- 333,339 ---- struct file_struct { unsigned flags; time_t modtime; + time_t acctime; OFF_T length; mode_t mode; *** proto.h@@/main/original/1 Tue Apr 9 14:03:22 2002 --- proto.h Tue Apr 9 15:21:58 2002 *************** *** 233,239 **** pid_t local_child(int argc, char **argv,int *f_in,int *f_out); void out_of_memory(char *str); void overflow(char *str); ! int set_modtime(char *fname, time_t modtime); int create_directory_path(char *fname); int copy_file(char *source, char *dest, mode_t mode); int robust_unlink(char *fname); --- 233,239 ---- pid_t local_child(int argc, char **argv,int *f_in,int *f_out); void out_of_memory(char *str); void overflow(char *str); ! int set_modtime(char *fname,time_t modtime,time_t acctime); int create_directory_path(char *fname); int copy_file(char *source, char *dest, mode_t mode); int robust_unlink(char *fname); *** options.c@@/main/original/1 Tue Apr 9 14:03:18 2002 --- options.c Tue Apr 9 15:59:10 2002 *************** *** 44,49 **** --- 44,50 ---- int preserve_uid = 0; int preserve_gid = 0; int preserve_times = 0; + int preserve_atime = 0; int update_only = 0; int cvs_exclude = 0; int dry_run=0; *************** *** 220,225 **** --- 221,227 ---- rprintf(F," -g, --group preserve group\n"); rprintf(F," -D, --devices preserve devices (root only)\n"); rprintf(F," -t, --times preserve times\n"); + rprintf(F," --preserve-atime preserve atime for source\n"); rprintf(F," -S, --sparse handle sparse files efficiently\n"); rprintf(F," -n, --dry-run show what would have been transferred\n"); rprintf(F," -W, --whole-file copy whole files, no incremental checks\n"); *************** *** 287,293 **** OPT_DELETE_AFTER, OPT_EXISTING, OPT_MAX_DELETE, OPT_BACKUP_DIR, OPT_IGNORE_ERRORS, OPT_BWLIMIT, OPT_BLOCKING_IO, OPT_NO_BLOCKING_IO, OPT_WHOLE_FILE, OPT_NO_WHOLE_FILE, ! OPT_MODIFY_WINDOW, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_IGNORE_EXISTING}; static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ --- 289,296 ---- OPT_DELETE_AFTER, OPT_EXISTING, OPT_MAX_DELETE, OPT_BACKUP_DIR, OPT_IGNORE_ERRORS, OPT_BWLIMIT, OPT_BLOCKING_IO, OPT_NO_BLOCKING_IO, OPT_WHOLE_FILE, OPT_NO_WHOLE_FILE, ! OPT_MODIFY_WINDOW, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_IGNORE_EXISTING, ! OPT_PRESERVE_ATIME}; static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ *************** *** 361,366 **** --- 364,370 ---- {"hard-links", 'H', POPT_ARG_NONE, &preserve_hard_links , 0, 0, 0 }, {"read-batch", 0, POPT_ARG_STRING, &batch_prefix, OPT_READ_BATCH, 0, 0 }, {"write-batch", 0, POPT_ARG_STRING, &batch_prefix, OPT_WRITE_BATCH, 0, 0 }, + {"preserve-atime", 0, POPT_ARG_NONE, &preserve_atime, 0, 0, 0 }, #ifdef INET6 {0, '4', POPT_ARG_VAL, &default_af_hint, AF_INET , 0, 0 }, {0, '6', POPT_ARG_VAL, &default_af_hint, AF_INET6 , 0, 0 }, *** flist.c@@/main/original/1 Tue Apr 9 14:02:48 2002 --- flist.c Tue Jun 18 09:40:46 2002 *************** *** 706,711 **** --- 706,712 ---- } file->modtime = st.st_mtime; + file->acctime = st.st_atime; file->length = st.st_size; file->mode = st.st_mode; file->uid = st.st_uid; *** rsync.1@@/main/original/1 Tue Apr 9 14:03:26 2002 --- rsync.1 Fri May 10 09:34:44 2002 *************** *** 311,316 **** --- 311,317 ---- --bwlimit=KBPS limit I/O bandwidth, KBytes per second --read-batch=PREFIX read batch fileset starting with PREFIX --write-batch=PREFIX write batch fileset starting with PREFIX + --preserve-atime preserve atime for source -h, --help show this help screen *************** *** 825,830 **** --- 826,837 ---- Apply a previously generated change batch, using the fileset whose filenames start with PREFIX\&. See the "BATCH MODE" section for details\&. + .IP + .IP "\fB--preserve-atime\fP" + Preserve atime while reading source filesystem. This works by reading and + resetting the atime for each file accessed. It has the side effect of + updating the ctime for the file. This may or may not be the desired + behavior; consider this before using. .IP .PP .SH "EXCLUDE PATTERNS"