[38280] trunk/dports/net/rsync
simon at macports.org
simon at macports.org
Mon Jul 14 13:10:51 PDT 2008
Revision: 38280
http://trac.macosforge.org/projects/macports/changeset/38280
Author: simon at macports.org
Date: 2008-07-14 13:10:51 -0700 (Mon, 14 Jul 2008)
Log Message:
-----------
net/rsync: Fixed patch files, closes #15973.
Modified Paths:
--------------
trunk/dports/net/rsync/Portfile
Added Paths:
-----------
trunk/dports/net/rsync/files/crtimes.diff
trunk/dports/net/rsync/files/fileflags.diff
Modified: trunk/dports/net/rsync/Portfile
===================================================================
--- trunk/dports/net/rsync/Portfile 2008-07-14 19:52:03 UTC (rev 38279)
+++ trunk/dports/net/rsync/Portfile 2008-07-14 20:10:51 UTC (rev 38280)
@@ -4,7 +4,7 @@
name rsync
version 3.0.3
-revision 1
+revision 2
categories net
platforms darwin freebsd sunos
maintainers simon openmaintainer
@@ -25,20 +25,11 @@
checksums ${distname}${extract.suffix} \
md5 16d41aab9ece435198af222c5415a304 \
sha1 c12668eb888e386511299616f6972bec300ed346 \
- rmd160 9997a18cb5577bb127b26bcc55e50382b6e9f68d \
- fileflags.diff \
- md5 201757ade9b8d8077f6aaa348f579d5a \
- sha1 ad6ff8da7c1466564ec793f0fd9c8ac1d67a0c8a \
- rmd160 c99605939aa36663c73a6e7a07fa2f751235239b \
- crtimes.diff \
- md5 7941d2487b12fda621794ddae687e115 \
- sha1 19ea5a3a14373839776c58aba6c6b21ca0b7c816 \
- rmd160 70c0a00a15fbf2d64bd7b0a406119fec976dc7ca
+ rmd160 9997a18cb5577bb127b26bcc55e50382b6e9f68d
distname rsync-${version}
depends_lib port:popt port:libiconv
-patch_sites http://rsync.samba.org/ftp/rsync/patches/
patchfiles fileflags.diff \
crtimes.diff
patch.pre_args -p1
Added: trunk/dports/net/rsync/files/crtimes.diff
===================================================================
--- trunk/dports/net/rsync/files/crtimes.diff (rev 0)
+++ trunk/dports/net/rsync/files/crtimes.diff 2008-07-14 20:10:51 UTC (rev 38280)
@@ -0,0 +1,615 @@
+This patch adds a --crtimes (-N) option that will preserve
+create times on OS X.
+
+To use this patch, run these commands for a successful build:
+
+ patch -p1 <patches/fileflags.diff
+ patch -p1 <patches/crtimes.diff
+ ./configure (optional if already run)
+ make
+
+diff --git a/compat.c b/compat.c
+--- a/compat.c
++++ b/compat.c
+@@ -44,6 +44,7 @@ extern int force_change;
+ extern int protect_args;
+ extern int preserve_uid;
+ extern int preserve_gid;
++extern int preserve_crtimes;
+ extern int preserve_fileflags;
+ extern int preserve_acls;
+ extern int preserve_xattrs;
+@@ -61,7 +62,7 @@ extern iconv_t ic_send, ic_recv;
+ #endif
+
+ /* These index values are for the file-list's extra-attribute array. */
+-int uid_ndx, gid_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
++int uid_ndx, gid_ndx, crtimes_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
+
+ int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
+
+@@ -135,6 +136,8 @@ void setup_protocol(int f_out,int f_in)
+ uid_ndx = ++file_extra_cnt;
+ if (preserve_gid)
+ gid_ndx = ++file_extra_cnt;
++ if (preserve_crtimes)
++ crtimes_ndx = (file_extra_cnt += TIME_EXTRA_CNT);
+ if (preserve_fileflags || (force_change && !am_sender))
+ fileflags_ndx = ++file_extra_cnt;
+ if (preserve_acls && !am_sender)
+diff --git a/flist.c b/flist.c
+--- a/flist.c
++++ b/flist.c
+@@ -54,6 +54,7 @@ extern int preserve_fileflags;
+ extern int uid_ndx;
+ extern int gid_ndx;
+ extern int eol_nulls;
++extern int crtimes_ndx;
+ extern int relative_paths;
+ extern int implied_dirs;
+ extern int file_extra_cnt;
+@@ -389,7 +390,7 @@ int change_pathname(struct file_struct *file, const char *dir, int dirlen)
+
+ static void send_file_entry(int f, const char *fname, struct file_struct *file, int ndx, int first_ndx)
+ {
+- static time_t modtime;
++ static time_t modtime, crtime;
+ static mode_t mode;
+ #ifdef SUPPORT_FILEFLAGS
+ static uint32 fileflags;
+@@ -474,6 +475,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+ xflags |= XMIT_SAME_TIME;
+ else
+ modtime = file->modtime;
++ if (crtimes_ndx) {
++ time_t file_crtime = f_crtime(file);
++ if (file_crtime == modtime)
++ xflags |= XMIT_CRTIME_EQ_MTIME;
++ else
++ crtime = file_crtime;
++ }
+
+ #ifdef SUPPORT_HARD_LINKS
+ if (tmp_dev != 0) {
+@@ -543,6 +551,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+ else
+ write_int(f, modtime);
+ }
++ if (crtimes_ndx && !(xflags & XMIT_CRTIME_EQ_MTIME))
++ write_varlong(f, crtime, 4);
+ if (!(xflags & XMIT_SAME_MODE))
+ write_int(f, to_wire_mode(mode));
+ #ifdef SUPPORT_FILEFLAGS
+@@ -635,7 +645,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+ static struct file_struct *recv_file_entry(struct file_list *flist,
+ int xflags, int f)
+ {
+- static int64 modtime;
++ static int64 modtime, crtime;
+ static mode_t mode;
+ #ifdef SUPPORT_FILEFLAGS
+ static uint32 fileflags;
+@@ -770,6 +780,19 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
+ } else
+ modtime = read_int(f);
+ }
++ if (crtimes_ndx) {
++ if (!(xflags & XMIT_CRTIME_EQ_MTIME)) {
++ 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
++ } else
++ crtime = modtime;
++ }
+ if (!(xflags & XMIT_SAME_MODE))
+ mode = from_wire_mode(read_int(f));
+
+@@ -922,6 +945,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
+ F_GROUP(file) = gid;
+ file->flags |= gid_flags;
+ }
++ if (crtimes_ndx)
++ f_crtime_set(file, (time_t)crtime);
+ if (unsort_ndx)
+ F_NDX(file) = flist->used + flist->ndx_start;
+
+@@ -1272,6 +1297,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+ F_OWNER(file) = st.st_uid;
+ if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */
+ F_GROUP(file) = st.st_gid;
++ if (crtimes_ndx)
++ f_crtime_set(file, get_create_time(fname));
+
+ if (basename != thisname)
+ file->dirname = lastdir;
+diff --git a/generator.c b/generator.c
+--- a/generator.c
++++ b/generator.c
+@@ -21,6 +21,7 @@
+ */
+
+ #include "rsync.h"
++#include "ifuncs.h"
+
+ extern int dry_run;
+ extern int do_xfers;
+@@ -38,6 +39,7 @@ extern int preserve_xattrs;
+ extern int preserve_links;
+ extern int preserve_devices;
+ extern int preserve_specials;
++extern int preserve_fileflags;
+ extern int preserve_hard_links;
+ extern int preserve_executability;
+ extern int preserve_fileflags;
+@@ -618,6 +620,13 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
+ if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file))
+ return 0;
+
++ if (crtimes_ndx) {
++ if (sxp->crtime == 0)
++ sxp->crtime = get_create_time(fname);
++ if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
++ return 0;
++ }
++
+ #ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ if (!ACL_READY(*sxp))
+@@ -661,6 +670,12 @@ 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 (crtimes_ndx) {
++ if (sxp->crtime == 0)
++ sxp->crtime = get_create_time(fnamecmp);
++ if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
++ iflags |= ITEM_REPORT_CRTIME;
++ }
+ #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
+ if (S_ISLNK(file->mode)) {
+ ;
+@@ -1221,6 +1236,7 @@ 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];
++ time_t crtime = crtimes_ndx ? f_crtime(f) : 0;
+ double len;
+
+ if (!F_IS_ACTIVE(f)) {
+@@ -1235,14 +1251,16 @@ static void list_file_entry(struct file_struct *f)
+
+ #ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(f->mode)) {
+- rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
++ rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
+ permbuf, len, timestring(f->modtime),
++ crtimes_ndx ? timestring(crtime) : "",
+ f_name(f, NULL), F_SYMLINK(f));
+ } else
+ #endif
+ {
+- rprintf(FINFO, "%s %11.0f %s %s\n",
++ rprintf(FINFO, "%s %11.0f %s %s %s\n",
+ permbuf, len, timestring(f->modtime),
++ crtimes_ndx ? timestring(crtime) : "",
+ f_name(f, NULL));
+ }
+ }
+@@ -1334,6 +1352,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ return;
+ }
+ }
++ sx.crtime = 0;
+
+ #ifdef SUPPORT_ACLS
+ sx.acc_acl = sx.def_acl = NULL;
+diff --git a/ifuncs.h b/ifuncs.h
+--- a/ifuncs.h
++++ b/ifuncs.h
+@@ -67,6 +67,28 @@ d_name(struct dirent *di)
+ #endif
+ }
+
++static inline time_t
++f_crtime(struct file_struct *fp)
++{
++#if SIZEOF_TIME_T > 4
++ time_t crtime;
++ memcpy(&crtime, &REQ_EXTRA(fp, crtimes_ndx)->unum, SIZEOF_TIME_T);
++ return crtime;
++#else
++ return REQ_EXTRA(fp, crtimes_ndx)->unum;
++#endif
++}
++
++static inline void
++f_crtime_set(struct file_struct *fp, time_t crtime)
++{
++#if SIZEOF_TIME_T > 4
++ memcpy(&REQ_EXTRA(fp, crtimes_ndx)->unum, &crtime, SIZEOF_TIME_T);
++#else
++ REQ_EXTRA(fp, crtimes_ndx)->unum = (uint32)crtime;
++#endif
++}
++
+ static inline int
+ isDigit(const char *ptr)
+ {
+diff --git a/log.c b/log.c
+--- a/log.c
++++ b/log.c
+@@ -664,7 +664,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
+ c[8] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
+ c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
+ c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
+- c[11] = '\0';
++ c[11] = !(iflags & ITEM_REPORT_CRTIME) ? '.' : 'n';
++ c[12] = '\0';
+
+ if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
+ char ch = iflags & ITEM_IS_NEW ? '+' : '?';
+diff --git a/options.c b/options.c
+--- a/options.c
++++ b/options.c
+@@ -60,6 +60,7 @@ int preserve_specials = 0;
+ int preserve_uid = 0;
+ int preserve_gid = 0;
+ int preserve_times = 0;
++int preserve_crtimes = 0;
+ int update_only = 0;
+ int cvs_exclude = 0;
+ int dry_run = 0;
+@@ -698,6 +699,7 @@ void usage(enum logcode F)
+ rprintf(F," -D same as --devices --specials\n");
+ rprintf(F," -t, --times preserve modification times\n");
+ rprintf(F," -O, --omit-dir-times omit directories from --times\n");
++ rprintf(F," -N, --crtimes preserve create times (newness)\n");
+ rprintf(F," --super receiver attempts super-user activities\n");
+ #ifdef SUPPORT_XATTRS
+ rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
+@@ -847,6 +849,9 @@ static struct poptOption long_options[] = {
+ {"times", 't', POPT_ARG_VAL, &preserve_times, 2, 0, 0 },
+ {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
+ {"no-t", 0, POPT_ARG_VAL, &preserve_times, 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 },
+@@ -2180,6 +2185,8 @@ void server_options(char **args, int *argc_p)
+ argstr[x++] = 'D';
+ if (preserve_times)
+ argstr[x++] = 't';
++ if (preserve_crtimes)
++ argstr[x++] = 'N';
+ if (preserve_perms)
+ argstr[x++] = 'p';
+ else if (preserve_executability && am_sender)
+diff --git a/rsync.c b/rsync.c
+--- a/rsync.c
++++ b/rsync.c
+@@ -471,6 +471,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ else
+ file->flags |= FLAG_TIME_FAILED;
+ }
++ if (crtimes_ndx && !(flags & ATTRS_SKIP_CRTIME)) {
++ time_t file_crtime = f_crtime(file);
++ if (sxp->crtime == 0)
++ sxp->crtime = get_create_time(fname);
++ if (cmp_time(sxp->crtime, file_crtime) != 0
++ && set_create_time(fname, file_crtime) == 0)
++ updated = 1;
++ }
+
+ change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
+ change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
+@@ -618,7 +626,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,
+ ATTRS_DELAY_IMMUTABLE
+- | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME));
++ | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME));
+
+ /* move tmp file over real file */
+ if (DEBUG_GTE(RECV, 1))
+@@ -649,7 +657,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 ? 0 : ATTRS_SKIP_MTIME);
++ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME);
+
+ if (temp_copy_name) {
+ if (do_rename(fnametmp, fname) < 0) {
+diff --git a/rsync.h b/rsync.h
+--- a/rsync.h
++++ b/rsync.h
+@@ -60,6 +60,7 @@
+ #define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
+ #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
+ #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
++#define XMIT_CRTIME_EQ_MTIME (1<<13) /* protocols ?? - now */
+ #define XMIT_SAME_FLAGS (1<<14) /* protocols ?? - now */
+
+ /* These flags are used in the live flist data. */
+@@ -156,6 +157,7 @@
+ #define ATTRS_REPORT (1<<0)
+ #define ATTRS_SKIP_MTIME (1<<1)
+ #define ATTRS_DELAY_IMMUTABLE (1<<2)
++#define ATTRS_SKIP_CRTIME (1<<3)
+
+ #define FULL_FLUSH 1
+ #define NORMAL_FLUSH 0
+@@ -172,7 +174,7 @@
+ #define FNAMECMP_FUZZY 0x83
+
+ /* For use by the itemize_changes code */
+-#define ITEM_REPORT_ATIME (1<<0)
++#define ITEM_REPORT_CRTIME (1<<0)
+ #define ITEM_REPORT_CHANGE (1<<1)
+ #define ITEM_REPORT_SIZE (1<<2) /* regular files only */
+ #define ITEM_REPORT_TIMEFAIL (1<<2) /* symlinks only */
+@@ -655,6 +657,7 @@ extern int file_extra_cnt;
+ extern int inc_recurse;
+ extern int uid_ndx;
+ extern int gid_ndx;
++extern int crtimes_ndx;
+ extern int fileflags_ndx;
+ extern int acls_ndx;
+ extern int xattrs_ndx;
+@@ -662,6 +665,7 @@ 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 TIME_EXTRA_CNT ((SIZEOF_TIME_T + EXTRA_LEN - 1) / EXTRA_LEN)
+ #define DEV_EXTRA_CNT 2
+ #define DIRNODE_EXTRA_CNT 3
+ #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
+@@ -920,6 +924,7 @@ typedef struct {
+
+ typedef struct {
+ STRUCT_STAT st;
++ time_t crtime;
+ #ifdef SUPPORT_ACLS
+ struct rsync_acl *acc_acl; /* access ACL */
+ struct rsync_acl *def_acl; /* default ACL */
+diff --git a/rsync.yo b/rsync.yo
+--- a/rsync.yo
++++ b/rsync.yo
+@@ -352,6 +352,7 @@ to the detailed description below for a complete description. verb(
+ -D same as --devices --specials
+ -t, --times preserve modification times
+ -O, --omit-dir-times omit directories from --times
++ -N, --crtimes preserve create times (newness)
+ --super receiver attempts super-user activities
+ --fake-super store/recover privileged attrs using xattrs
+ -S, --sparse handle sparse files efficiently
+@@ -1069,6 +1070,9 @@ 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).
+ This option is inferred if you use bf(--backup) without bf(--backup-dir).
+
++dit(bf(-N, --crtimes)) This tells rsync to set the create times (newness) of
++the destination files to the same value as the source files.
++
+ dit(bf(--super)) This tells the receiving side to attempt super-user
+ activities even if the receiving rsync wasn't run by the super-user. These
+ activities include: preserving users via the bf(--owner) option, preserving
+@@ -1758,7 +1762,7 @@ with older versions of rsync, but that also turns on the output of other
+ verbose messages).
+
+ The "%i" escape has a cryptic output that is 11 letters long. The general
+-format is like the string bf(YXcstpogfax), where bf(Y) is replaced by the
++format is like the string bf(YXcstpogfaxn), where bf(Y) is replaced by the
+ type of update being done, bf(X) is replaced by the file-type, and the
+ other letters represent attributes that may be output if they are being
+ modified.
+@@ -1817,6 +1821,8 @@ quote(itemization(
+ it() The bf(f) means that the fileflags information changed.
+ it() The bf(a) means that the ACL information changed.
+ it() The bf(x) means that the extended attribute information changed.
++ it() A bf(n) means the create time (newness) is different and is being
++ updated to the sender's value (requires bf(--crtimes)).
+ ))
+
+ One other output is possible: when deleting files, the "%i" will output
+diff --git a/syscall.c b/syscall.c
+--- a/syscall.c
++++ b/syscall.c
+@@ -37,6 +37,11 @@ extern int force_change;
+ extern int preserve_perms;
+ extern int preserve_executability;
+
++struct create_time {
++ unsigned long length;
++ struct timespec crtime;
++};
++
+ #define RETURN_ERROR_IF(x,e) \
+ do { \
+ if (x) { \
+@@ -394,3 +399,33 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence)
+ return lseek(fd, offset, whence);
+ #endif
+ }
++
++time_t get_create_time(const char *path)
++{
++ static struct create_time attrBuf;
++ struct attrlist attrList;
++
++ memset(&attrList, 0, sizeof attrList);
++ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
++ attrList.commonattr = ATTR_CMN_CRTIME;
++ if (getattrlist(path, &attrList, &attrBuf, sizeof attrBuf, FSOPT_NOFOLLOW) < 0)
++ return 0;
++ return attrBuf.crtime.tv_sec;
++}
++
++int set_create_time(const char *path, time_t crtime)
++{
++ struct attrlist attrList;
++ struct timespec ts;
++
++ if (dry_run) return 0;
++ RETURN_ERROR_IF_RO_OR_LO;
++
++ ts.tv_sec = crtime;
++ ts.tv_nsec = 0;
++
++ memset(&attrList, 0, sizeof attrList);
++ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
++ attrList.commonattr = ATTR_CMN_CRTIME;
++ return setattrlist(path, &attrList, &ts, sizeof ts, FSOPT_NOFOLLOW);
++}
+diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
+new file mode 100644
+--- /dev/null
++++ b/testsuite/crtimes.test
+@@ -0,0 +1,24 @@
++#! /bin/sh
++
++# Test rsync copying create times
++
++. "$suitedir/rsync.fns"
++
++# Setting an older time via touch sets the create time to the mtime.
++# Setting it to a newer time affects just the mtime.
++
++mkdir "$fromdir"
++echo hiho "$fromdir/foo"
++
++touch -t 200101011111.11 "$fromdir"
++touch -t 200202022222.22 "$fromdir"
++
++touch -t 200111111111.11 "$fromdir/foo"
++touch -t 200212122222.22 "$fromdir/foo"
++
++TLS_ARGS=--crtimes
++
++checkit "$RSYNC -rtgvvv --crtimes \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
++
++# The script would have aborted on error, so getting here means we've won.
++exit 0
+diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
+--- a/testsuite/rsync.fns
++++ b/testsuite/rsync.fns
+@@ -24,9 +24,9 @@ todir="$tmpdir/to"
+ chkdir="$tmpdir/chk"
+
+ # For itemized output:
+-all_plus='+++++++++'
+-allspace=' '
+-dots='.....' # trailing dots after changes
++all_plus='++++++++++'
++allspace=' '
++dots='......' # trailing dots after changes
+
+ # Berkley's nice.
+ PATH="$PATH:/usr/ucb"
+diff --git a/tls.c b/tls.c
+--- a/tls.c
++++ b/tls.c
+@@ -107,6 +107,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
+
+ #endif
+
++static int display_crtimes = 0;
++
+ static void failed(char const *what, char const *where)
+ {
+ fprintf(stderr, PROGRAM ": %s %s: %s\n",
+@@ -114,16 +116,36 @@ static void failed(char const *what, char const *where)
+ exit(1);
+ }
+
++static void storetime(char *dest, time_t t, size_t destsize)
++{
++ if (t) {
++ struct tm *mt = gmtime(&t);
++
++ 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);
++ } else
++ strlcpy(dest, " ", destsize);
++}
++
+ static void list_file(const char *fname)
+ {
+ STRUCT_STAT buf;
++ time_t crtime = 0;
+ char permbuf[PERMSTRING_SIZE];
+- struct tm *mt;
+- char datebuf[50];
++ char mtimebuf[50];
++ char crtimebuf[50];
+ char linkbuf[4096];
+
+ if (do_lstat(fname, &buf) < 0)
+ failed("stat", fname);
++ if (display_crtimes && (crtime = get_create_time(fname)) == 0)
++ failed("get_create_time", fname);
+ #ifdef SUPPORT_XATTRS
+ if (am_root < 0)
+ stat_xattr(fname, &buf);
+@@ -158,19 +180,11 @@ static void list_file(const char *fname)
+
+ permstring(permbuf, buf.st_mode);
+
+- if (buf.st_mtime) {
+- mt = gmtime(&buf.st_mtime);
+-
+- 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);
+- } else
+- strlcpy(datebuf, " ", sizeof datebuf);
++ storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
++ if (display_crtimes)
++ storetime(crtimebuf, crtime, sizeof crtimebuf);
++ else
++ crtimebuf[0] = '\0';
+
+ /* TODO: Perhaps escape special characters in fname? */
+
+@@ -181,13 +195,14 @@ static void list_file(const char *fname)
+ (long)minor(buf.st_rdev));
+ } else /* NB: use double for size since it might not fit in a long. */
+ printf("%12.0f", (double)buf.st_size);
+- 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, crtimebuf, fname, linkbuf);
+ }
+
+ static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
++ {"crtimes", 'N', POPT_ARG_NONE, &display_crtimes, 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
+@@ -203,6 +218,7 @@ static void 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," -N, --crtimes display create times (newness)\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
Added: trunk/dports/net/rsync/files/fileflags.diff
===================================================================
--- trunk/dports/net/rsync/files/fileflags.diff (rev 0)
+++ trunk/dports/net/rsync/files/fileflags.diff 2008-07-14 20:10:51 UTC (rev 38280)
@@ -0,0 +1,1262 @@
+This patch provides --fileflags, which preserves the st_flags stat() field.
+Modified from a patch that was written by Rolf Grossmann.
+
+To use this patch, run these commands for a successful build:
+
+ patch -p1 <patches/fileflags.diff
+ ./prepare-source
+ ./configure
+ make
+
+diff --git a/Makefile.in b/Makefile.in
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -42,7 +42,7 @@ popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
+ popt/popthelp.o popt/poptparse.o
+ OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@
+
+-TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
++TLS_OBJ = tls.o syscall.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
+
+ # Programs we must have to run the test cases
+ CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
+@@ -107,7 +107,7 @@ getgroups$(EXEEXT): getgroups.o
+ getfsdev$(EXEEXT): getfsdev.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
+
+-TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o
++TRIMSLASH_OBJ = trimslash.o syscall.o t_stub.o lib/compat.o lib/snprintf.o
+ trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
+
+diff --git a/compat.c b/compat.c
+--- a/compat.c
++++ b/compat.c
+@@ -41,9 +41,11 @@ extern int checksum_seed;
+ extern int basis_dir_cnt;
+ extern int prune_empty_dirs;
+ extern int protocol_version;
++extern int force_change;
+ extern int protect_args;
+ extern int preserve_uid;
+ extern int preserve_gid;
++extern int preserve_fileflags;
+ extern int preserve_acls;
+ extern int preserve_xattrs;
+ extern int need_messages_from_generator;
+@@ -60,7 +62,7 @@ extern iconv_t ic_send, ic_recv;
+ #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 uid_ndx, gid_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
+
+ int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
+
+@@ -134,6 +136,8 @@ void setup_protocol(int f_out,int f_in)
+ uid_ndx = ++file_extra_cnt;
+ if (preserve_gid)
+ gid_ndx = ++file_extra_cnt;
++ if (preserve_fileflags || (force_change && !am_sender))
++ fileflags_ndx = ++file_extra_cnt;
+ if (preserve_acls && !am_sender)
+ acls_ndx = ++file_extra_cnt;
+ if (preserve_xattrs)
+diff --git a/configure.in b/configure.in
+--- a/configure.in
++++ b/configure.in
+@@ -553,7 +553,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
+ memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
+ strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
+ setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
+- strerror putenv iconv_open locale_charset nl_langinfo getxattr \
++ chflags strerror putenv iconv_open locale_charset nl_langinfo getxattr \
+ extattr_get_link sigaction sigprocmask setattrlist)
+
+ dnl cygwin iconv.h defines iconv_open as libiconv_open
+diff --git a/flist.c b/flist.c
+--- a/flist.c
++++ b/flist.c
+@@ -52,6 +52,7 @@ extern int preserve_links;
+ extern int preserve_hard_links;
+ extern int preserve_devices;
+ extern int preserve_specials;
++extern int preserve_fileflags;
+ extern int uid_ndx;
+ extern int gid_ndx;
+ extern int eol_nulls;
+@@ -390,6 +391,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+ {
+ static time_t modtime;
+ static mode_t mode;
++#ifdef SUPPORT_FILEFLAGS
++ static uint32 fileflags;
++#endif
+ #ifdef SUPPORT_HARD_LINKS
+ static int64 dev;
+ #endif
+@@ -419,6 +423,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+ xflags |= XMIT_SAME_MODE;
+ else
+ mode = file->mode;
++#ifdef SUPPORT_FILEFLAGS
++ if (preserve_fileflags) {
++ if (F_FFLAGS(file) == fileflags)
++ xflags |= XMIT_SAME_FLAGS;
++ else
++ fileflags = F_FFLAGS(file);
++ }
++#endif
+
+ if ((preserve_devices && IS_DEVICE(mode))
+ || (preserve_specials && IS_SPECIAL(mode))) {
+@@ -533,6 +545,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+ }
+ if (!(xflags & XMIT_SAME_MODE))
+ write_int(f, to_wire_mode(mode));
++#ifdef SUPPORT_FILEFLAGS
++ if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
++ write_int(f, (int)fileflags);
++#endif
+ if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
+ if (protocol_version < 30)
+ write_int(f, uid);
+@@ -621,6 +637,9 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
+ {
+ static int64 modtime;
+ static mode_t mode;
++#ifdef SUPPORT_FILEFLAGS
++ static uint32 fileflags;
++#endif
+ #ifdef SUPPORT_HARD_LINKS
+ static int64 dev;
+ #endif
+@@ -756,6 +775,10 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
+
+ if (chmod_modes && !S_ISLNK(mode))
+ mode = tweak_mode(mode, chmod_modes);
++#ifdef SUPPORT_FILEFLAGS
++ if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
++ fileflags = (uint32)read_int(f);
++#endif
+
+ if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
+ if (protocol_version < 30)
+@@ -889,6 +912,10 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
+ }
+ #endif
+ file->mode = mode;
++#ifdef SUPPORT_FILEFLAGS
++ if (preserve_fileflags)
++ F_FFLAGS(file) = fileflags;
++#endif
+ if (preserve_uid)
+ F_OWNER(file) = uid;
+ if (preserve_gid) {
+@@ -1237,6 +1264,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+ }
+ #endif
+ file->mode = st.st_mode;
++#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
++ if (fileflags_ndx)
++ F_FFLAGS(file) = st.st_flags;
++#endif
+ if (uid_ndx) /* Check uid_ndx instead of preserve_uid for del support */
+ F_OWNER(file) = st.st_uid;
+ if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */
+@@ -1355,6 +1386,7 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
+ #endif
+ #ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
++ sx.st.st_mode = file->mode;
+ sx.xattr = NULL;
+ if (get_xattr(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
+diff --git a/generator.c b/generator.c
+--- a/generator.c
++++ b/generator.c
+@@ -42,8 +42,10 @@ extern int preserve_devices;
+ extern int preserve_specials;
+ extern int preserve_hard_links;
+ extern int preserve_executability;
++extern int preserve_fileflags;
+ extern int preserve_perms;
+ extern int preserve_times;
++extern int force_change;
+ extern int uid_ndx;
+ extern int gid_ndx;
+ extern int delete_mode;
+@@ -166,7 +168,7 @@ static enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
+ }
+
+ if (flags & DEL_NO_UID_WRITE)
+- do_chmod(fbuf, mode | S_IWUSR);
++ do_chmod(fbuf, mode | S_IWUSR, NO_FFLAGS);
+
+ if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
+ int save_uid_ndx = uid_ndx;
+@@ -174,6 +176,13 @@ static enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
+ * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
+ if (!uid_ndx)
+ uid_ndx = ++file_extra_cnt;
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change) {
++ STRUCT_STAT st;
++ if (x_lstat(fbuf, &st, NULL) == 0)
++ make_mutable(fbuf, st.st_mode, st.st_flags, force_change);
++ }
++#endif
+ ignore_perishable = 1;
+ /* If DEL_RECURSE is not set, this just reports emptiness. */
+ ret = delete_dir_contents(fbuf, flags);
+@@ -294,8 +303,12 @@ static enum delret delete_dir_contents(char *fname, uint16 flags)
+ }
+
+ strlcpy(p, fp->basename, remainder);
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change)
++ make_mutable(fname, fp->mode, F_FFLAGS(fp), force_change);
++#endif
+ if (!(fp->mode & S_IWUSR) && !am_root && (uid_t)F_OWNER(fp) == our_uid)
+- do_chmod(fname, fp->mode | S_IWUSR);
++ do_chmod(fname, fp->mode | S_IWUSR, NO_FFLAGS);
+ /* Save stack by recursing to ourself directly. */
+ if (S_ISDIR(fp->mode)) {
+ if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
+@@ -596,6 +609,11 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
+ && ((sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0)))
+ return 0;
+
++#ifdef SUPPORT_FILEFLAGS
++ if (preserve_fileflags && !S_ISLNK(file->mode) && sxp->st.st_flags != F_FFLAGS(file))
++ return 0;
++#endif
++
+ if (am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file))
+ return 0;
+
+@@ -661,6 +679,11 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
+ if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
+ && sxp->st.st_gid != (gid_t)F_GROUP(file))
+ iflags |= ITEM_REPORT_GROUP;
++#ifdef SUPPORT_FILEFLAGS
++ if (preserve_fileflags && !S_ISLNK(file->mode)
++ && sxp->st.st_flags != F_FFLAGS(file))
++ iflags |= ITEM_REPORT_FFLAGS;
++#endif
+ #ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ if (!ACL_READY(*sxp))
+@@ -1439,6 +1462,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ file->mode = dest_mode(file->mode, sx.st.st_mode,
+ dflt_perms, statret == 0);
+ }
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && !preserve_fileflags)
++ F_FFLAGS(file) = sx.st.st_flags;
++#endif
+ if (statret != 0 && basis_dir[0] != NULL) {
+ int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
+ itemizing, code);
+@@ -1479,10 +1506,15 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ /* We need to ensure that the dirs in the transfer have writable
+ * permissions during the time we are putting files within them.
+ * This is then fixed after the transfer is done. */
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && F_FFLAGS(file) & force_change
++ && make_mutable(fname, file->mode, F_FFLAGS(file), force_change))
++ need_retouch_dir_perms = 1;
++#endif
+ #ifdef HAVE_CHMOD
+ if (!am_root && !(file->mode & S_IWUSR) && dir_tweaking) {
+ mode_t mode = file->mode | S_IWUSR;
+- if (do_chmod(fname, mode) < 0) {
++ if (do_chmod(fname, mode, 0) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to modify permissions on %s",
+ full_fname(fname));
+@@ -1517,6 +1549,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms,
+ exists);
+ }
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && !preserve_fileflags)
++ F_FFLAGS(file) = sx.st.st_flags;
++#endif
+
+ #ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
+@@ -2051,13 +2087,17 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
+ continue;
+ fname = f_name(file, NULL);
+ if (!(file->mode & S_IWUSR))
+- do_chmod(fname, file->mode);
++ do_chmod(fname, file->mode, 0);
+ if (need_retouch_dir_times) {
+ STRUCT_STAT st;
+ if (link_stat(fname, &st, 0) == 0
+ && cmp_time(st.st_mtime, file->modtime) != 0)
+- set_modtime(fname, file->modtime, file->mode);
++ set_modtime(fname, file->modtime, file->mode, 0);
+ }
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && F_FFLAGS(file) & force_change)
++ undo_make_mutable(fname, F_FFLAGS(file));
++#endif
+ if (allowed_lull && !(counter % lull_mod))
+ maybe_send_keepalive();
+ else if (!(counter & 0xFF))
+diff --git a/log.c b/log.c
+--- a/log.c
++++ b/log.c
+@@ -656,7 +656,7 @@ 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_FFLAGS) ? '.' : 'f';
+ c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
+ c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
+ c[11] = '\0';
+diff --git a/options.c b/options.c
+--- a/options.c
++++ b/options.c
+@@ -53,6 +53,7 @@ int preserve_hard_links = 0;
+ int preserve_acls = 0;
+ int preserve_xattrs = 0;
+ int preserve_perms = 0;
++int preserve_fileflags = 0;
+ int preserve_executability = 0;
+ int preserve_devices = 0;
+ int preserve_specials = 0;
+@@ -85,6 +86,7 @@ int implied_dirs = 1;
+ int numeric_ids = 0;
+ int allow_8bit_chars = 0;
+ int force_delete = 0;
++int force_change = 0;
+ int io_timeout = 0;
+ int allowed_lull = 0;
+ int prune_empty_dirs = 0;
+@@ -225,6 +227,7 @@ static void print_rsync_version(enum logcode f)
+ char const *links = "no ";
+ char const *iconv = "no ";
+ char const *ipv6 = "no ";
++ char const *fileflags = "no ";
+ STRUCT_STAT *dumstat;
+
+ #if SUBPROTOCOL_VERSION != 0
+@@ -257,6 +260,9 @@ static void print_rsync_version(enum logcode f)
+ #if defined HAVE_LUTIMES && defined HAVE_UTIMES
+ symtimes = "";
+ #endif
++#ifdef SUPPORT_FILEFLAGS
++ fileflags = "";
++#endif
+
+ rprintf(f, "%s version %s protocol version %d%s\n",
+ RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
+@@ -270,8 +276,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, %ssymtimes\n",
+- have_inplace, acls, xattrs, iconv, symtimes);
++ rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sfile-flags\n",
++ have_inplace, acls, xattrs, iconv, symtimes, fileflags);
+
+ #ifdef MAINTAINER_MODE
+ rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
+@@ -338,6 +344,9 @@ void usage(enum logcode F)
+ rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n");
+ rprintf(F," -H, --hard-links preserve hard links\n");
+ rprintf(F," -p, --perms preserve permissions\n");
++#ifdef SUPPORT_FILEFLAGS
++ rprintf(F," --fileflags preserve file-flags (aka chflags)\n");
++#endif
+ rprintf(F," -E, --executability preserve the file's executability\n");
+ rprintf(F," --chmod=CHMOD affect file and/or directory permissions\n");
+ #ifdef SUPPORT_ACLS
+@@ -375,7 +384,12 @@ void usage(enum logcode F)
+ rprintf(F," --delete-after receiver deletes after transfer, not during\n");
+ rprintf(F," --delete-excluded also delete excluded files from destination dirs\n");
+ rprintf(F," --ignore-errors delete even if there are I/O errors\n");
+- rprintf(F," --force force deletion of directories even if not empty\n");
++ rprintf(F," --force-delete force deletion of directories even if not empty\n");
++#ifdef SUPPORT_FORCE_CHANGE
++ rprintf(F," --force-change affect user-/system-immutable files/dirs\n");
++ rprintf(F," --force-uchange affect user-immutable files/dirs\n");
++ rprintf(F," --force-schange affect system-immutable files/dirs\n");
++#endif
+ rprintf(F," --max-delete=NUM don't delete more than NUM files\n");
+ rprintf(F," --max-size=SIZE don't transfer any file larger than SIZE\n");
+ rprintf(F," --min-size=SIZE don't transfer any file smaller than SIZE\n");
+@@ -480,6 +494,10 @@ static struct poptOption long_options[] = {
+ {"perms", 'p', POPT_ARG_VAL, &preserve_perms, 1, 0, 0 },
+ {"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
+ {"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
++#ifdef SUPPORT_FILEFLAGS
++ {"fileflags", 0, POPT_ARG_VAL, &preserve_fileflags, 1, 0, 0 },
++ {"no-fileflags", 0, POPT_ARG_VAL, &preserve_fileflags, 0, 0, 0 },
++#endif
+ {"executability", 'E', POPT_ARG_NONE, &preserve_executability, 0, 0, 0 },
+ {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
+ {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
+@@ -558,6 +576,14 @@ static struct poptOption long_options[] = {
+ {"remove-source-files",0,POPT_ARG_VAL, &remove_source_files, 1, 0, 0 },
+ {"force", 0, POPT_ARG_VAL, &force_delete, 1, 0, 0 },
+ {"no-force", 0, POPT_ARG_VAL, &force_delete, 0, 0, 0 },
++ {"force-delete", 0, POPT_ARG_VAL, &force_delete, 1, 0, 0 },
++ {"no-force-delete", 0, POPT_ARG_VAL, &force_delete, 0, 0, 0 },
++#ifdef SUPPORT_FORCE_CHANGE
++ {"force-change", 0, POPT_ARG_VAL, &force_change, ALL_IMMUTABLE, 0, 0 },
++ {"no-force-change", 0, POPT_ARG_VAL, &force_change, 0, 0, 0 },
++ {"force-uchange", 0, POPT_ARG_VAL, &force_change, USR_IMMUTABLE, 0, 0 },
++ {"force-schange", 0, POPT_ARG_VAL, &force_change, SYS_IMMUTABLE, 0, 0 },
++#endif
+ {"ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 1, 0, 0 },
+ {"no-ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 0, 0, 0 },
+ {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 },
+@@ -1852,6 +1878,9 @@ void server_options(char **args, int *argc_p)
+ if (xfer_dirs && !recurse && delete_mode && am_sender)
+ args[ac++] = "--no-r";
+
++ if (preserve_fileflags)
++ args[ac++] = "--fileflags";
++
+ if (do_compression && def_compress_level != Z_DEFAULT_COMPRESSION) {
+ if (asprintf(&arg, "--compress-level=%d", def_compress_level) < 0)
+ goto oom;
+@@ -1939,6 +1968,16 @@ void server_options(char **args, int *argc_p)
+ args[ac++] = "--delete-excluded";
+ if (force_delete)
+ args[ac++] = "--force";
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change) {
++ if (force_change == ALL_IMMUTABLE)
++ args[ac++] = "--force-change";
++ else if (force_change == USR_IMMUTABLE)
++ args[ac++] = "--force-uchange";
++ else if (force_change == SYS_IMMUTABLE)
++ args[ac++] = "--force-schange";
++ }
++#endif
+ if (write_batch < 0)
+ args[ac++] = "--only-write-batch=X";
+ if (am_root > 1)
+diff --git a/rsync.c b/rsync.c
+--- a/rsync.c
++++ b/rsync.c
+@@ -32,6 +32,7 @@ extern int dry_run;
+ extern int preserve_acls;
+ extern int preserve_xattrs;
+ extern int preserve_perms;
++extern int preserve_fileflags;
+ extern int preserve_executability;
+ extern int preserve_times;
+ extern int am_root;
+@@ -374,6 +375,39 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
+ return new_mode;
+ }
+
++#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
++/* Set a file's st_flags. */
++static int set_fileflags(const char *fname, uint32 fileflags)
++{
++ if (do_chflags(fname, fileflags) != 0) {
++ rsyserr(FERROR_XFER, errno,
++ "failed to set file flags on %s",
++ full_fname(fname));
++ return 0;
++ }
++
++ return 1;
++}
++
++/* Remove immutable flags from an object, so it can be altered/removed. */
++int make_mutable(const char *fname, mode_t mode, uint32 fileflags, uint32 iflags)
++{
++ if (S_ISLNK(mode) || !(fileflags & iflags))
++ return 0;
++ if (!set_fileflags(fname, fileflags & ~iflags))
++ return -1;
++ return 1;
++}
++
++/* Undo a prior make_mutable() call that returned a 1. */
++int undo_make_mutable(const char *fname, uint32 fileflags)
++{
++ if (!set_fileflags(fname, fileflags))
++ return -1;
++ return 1;
++}
++#endif
++
+ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ const char *fnamecmp, int flags)
+ {
+@@ -427,7 +461,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ flags |= ATTRS_SKIP_MTIME;
+ if (!(flags & ATTRS_SKIP_MTIME)
+ && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
+- int ret = set_modtime(fname, file->modtime, sxp->st.st_mode);
++ int ret = set_modtime(fname, file->modtime, sxp->st.st_mode, ST_FLAGS(sxp->st));
+ if (ret < 0) {
+ rsyserr(FERROR_XFER, errno, "failed to set times on %s",
+ full_fname(fname));
+@@ -463,7 +497,8 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ if (am_root >= 0) {
+ if (do_lchown(fname,
+ change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid,
+- change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid) != 0) {
++ change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid,
++ sxp->st.st_mode, ST_FLAGS(sxp->st)) != 0) {
+ /* We shouldn't have attempted to change uid
+ * or gid unless have the privilege. */
+ rsyserr(FERROR_XFER, errno, "%s %s failed",
+@@ -495,7 +530,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+
+ #ifdef HAVE_CHMOD
+ if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
+- int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
++ int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode, ST_FLAGS(sxp->st));
+ if (ret < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to set permissions on %s",
+@@ -507,6 +542,19 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ }
+ #endif
+
++#ifdef SUPPORT_FILEFLAGS
++ if (preserve_fileflags && !S_ISLNK(sxp->st.st_mode)
++ && sxp->st.st_flags != F_FFLAGS(file)) {
++ uint32 fileflags = F_FFLAGS(file);
++ if (flags & ATTRS_DELAY_IMMUTABLE)
++ fileflags &= ~ALL_IMMUTABLE;
++ if (sxp->st.st_flags != fileflags
++ && !set_fileflags(fname, fileflags))
++ goto cleanup;
++ updated = 1;
++ }
++#endif
++
+ if (verbose > 1 && flags & ATTRS_REPORT) {
+ if (updated)
+ rprintf(FCLIENT, "%s\n", fname);
+@@ -570,7 +618,8 @@ 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 ? 0 : ATTRS_SKIP_MTIME);
++ ATTRS_DELAY_IMMUTABLE
++ | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME));
+
+ /* move tmp file over real file */
+ if (verbose > 2)
+@@ -589,6 +638,10 @@ int finish_transfer(const char *fname, const char *fnametmp,
+ }
+ if (ret == 0) {
+ /* The file was moved into place (not copied), so it's done. */
++#ifdef SUPPORT_FILEFLAGS
++ if (preserve_fileflags && F_FFLAGS(file) & ALL_IMMUTABLE)
++ set_fileflags(fname, F_FFLAGS(file));
++#endif
+ return 1;
+ }
+ /* The file was copied, so tweak the perms of the copied file. If it
+diff --git a/rsync.h b/rsync.h
+--- a/rsync.h
++++ b/rsync.h
+@@ -60,6 +60,7 @@
+ #define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
+ #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
+ #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
++#define XMIT_SAME_FLAGS (1<<14) /* protocols ?? - now */
+
+ /* These flags are used in the live flist data. */
+
+@@ -154,6 +155,7 @@
+
+ #define ATTRS_REPORT (1<<0)
+ #define ATTRS_SKIP_MTIME (1<<1)
++#define ATTRS_DELAY_IMMUTABLE (1<<2)
+
+ #define FULL_FLUSH 1
+ #define NORMAL_FLUSH 0
+@@ -180,6 +182,7 @@
+ #define ITEM_REPORT_GROUP (1<<6)
+ #define ITEM_REPORT_ACL (1<<7)
+ #define ITEM_REPORT_XATTR (1<<8)
++#define ITEM_REPORT_FFLAGS (1<<9)
+ #define ITEM_BASIS_TYPE_FOLLOWS (1<<11)
+ #define ITEM_XNAME_FOLLOWS (1<<12)
+ #define ITEM_IS_NEW (1<<13)
+@@ -460,6 +463,28 @@ typedef unsigned int size_t;
+ #endif
+ #endif
+
++#define NO_FFLAGS ((uint32)-1)
++
++#ifdef HAVE_CHFLAGS
++#define SUPPORT_FILEFLAGS 1
++#define SUPPORT_FORCE_CHANGE 1
++#endif
++
++#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
++#ifndef UF_NOUNLINK
++#define UF_NOUNLINK 0
++#endif
++#ifndef SF_NOUNLINK
++#define SF_NOUNLINK 0
++#endif
++#define USR_IMMUTABLE (UF_IMMUTABLE|UF_NOUNLINK|UF_APPEND)
++#define SYS_IMMUTABLE (SF_IMMUTABLE|SF_NOUNLINK|SF_APPEND)
++#define ALL_IMMUTABLE (USR_IMMUTABLE|SYS_IMMUTABLE)
++#define ST_FLAGS(st) (st.st_flags)
++#else
++#define ST_FLAGS(st) NO_FFLAGS
++#endif
++
+ /* Find a variable that is either exactly 32-bits or longer.
+ * If some code depends on 32-bit truncation, it will need to
+ * take special action in a "#if SIZEOF_INT32 > 4" section. */
+@@ -630,6 +655,7 @@ extern int file_extra_cnt;
+ extern int inc_recurse;
+ extern int uid_ndx;
+ extern int gid_ndx;
++extern int fileflags_ndx;
+ extern int acls_ndx;
+ extern int xattrs_ndx;
+
+@@ -667,6 +693,11 @@ extern int xattrs_ndx;
+ /* When the associated option is on, all entries will have these present: */
+ #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
+ #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
++#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
++#define F_FFLAGS(f) REQ_EXTRA(f, fileflags_ndx)->unum
++#else
++#define F_FFLAGS(f) NO_FFLAGS
++#endif
+ #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
+diff --git a/rsync.yo b/rsync.yo
+--- a/rsync.yo
++++ b/rsync.yo
+@@ -338,6 +338,7 @@ to the detailed description below for a complete description. verb(
+ -K, --keep-dirlinks treat symlinked dir on receiver as dir
+ -H, --hard-links preserve hard links
+ -p, --perms preserve permissions
++ --fileflags preserve file-flags (aka chflags)
+ -E, --executability preserve executability
+ --chmod=CHMOD affect file and/or directory permissions
+ -A, --acls preserve ACLs (implies -p)
+@@ -369,7 +370,10 @@ to the detailed description below for a complete description. verb(
+ --delete-after receiver deletes after transfer, not before
+ --delete-excluded also delete excluded files from dest dirs
+ --ignore-errors delete even if there are I/O errors
+- --force force deletion of dirs even if not empty
++ --force-delete force deletion of dirs even if not empty
++ --force-change affect user/system immutable files/dirs
++ --force-uchange affect user-immutable files/dirs
++ --force-schange affect system-immutable files/dirs
+ --max-delete=NUM don't delete more than NUM files
+ --max-size=SIZE don't transfer any file larger than SIZE
+ --min-size=SIZE don't transfer any file smaller than SIZE
+@@ -540,7 +544,8 @@ specified, in which case bf(-r) is not implied.
+
+ Note that bf(-a) bf(does not preserve hardlinks), because
+ finding multiply-linked files is expensive. You must separately
+-specify bf(-H).
++specify bf(-H). Note also that for backward compatibility, bf(-a)
++currently does bf(not) imply the bf(--fileflags) option.
+
+ dit(--no-OPTION) You may turn off one or more implied options by prefixing
+ the option name with "no-". Not all options may be prefixed with a "no-":
+@@ -798,7 +803,7 @@ they would be using bf(--copy-links).
+ Without this option, if the sending side has replaced a directory with a
+ symlink to a directory, the receiving side will delete anything that is in
+ the way of the new symlink, including a directory hierarchy (as long as
+-bf(--force) or bf(--delete) is in effect).
++bf(--force-delete) or bf(--delete) is in effect).
+
+ See also bf(--keep-dirlinks) for an analogous option for the receiving
+ side.
+@@ -935,6 +940,29 @@ super-user copies all namespaces except system.*. A normal user only copies
+ the user.* namespace. To be able to backup and restore non-user namespaces as
+ a normal user, see the bf(--fake-super) option.
+
++dit(bf(--fileflags)) This option causes rsync to update the file-flags to be
++the same as the source files and directories (if your OS supports the
++bf(chflags)(2) system call). Some flags can only be altered by the super-user
++and some might only be unset below a certain secure-level (usually single-user
++mode). It will not make files alterable that are set to immutable on the
++receiver. To do that, see bf(--force-change), bf(--force-uchange), and
++bf(--force-schange).
++
++dit(bf(--force-change)) This option causes rsync to disable both user-immutable
++and system-immutable flags on files and directories that are being updated or
++deleted on the receiving side. This option overrides bf(--force-uchange) and
++bf(--force-schange).
++
++dit(bf(--force-uchange)) This option causes rsync to disable user-immutable
++flags on files and directories that are being updated or deleted on the
++receiving side. It does not try to affect system flags. This option overrides
++bf(--force-change) and bf(--force-schange).
++
++dit(bf(--force-schange)) This option causes rsync to disable system-immutable
++flags on files and directories that are being updated or deleted on the
++receiving side. It does not try to affect user flags. This option overrides
++bf(--force-change) and bf(--force-schange).
++
+ dit(bf(--chmod)) This option tells rsync to apply one or more
+ comma-separated "chmod" strings to the permission of the files in the
+ transfer. The resulting value is treated as though it was the permissions
+@@ -1190,12 +1218,13 @@ See bf(--delete) (which is implied) for more details on file-deletion.
+ dit(bf(--ignore-errors)) Tells bf(--delete) to go ahead and delete files
+ even when there are I/O errors.
+
+-dit(bf(--force)) This option tells rsync to delete a non-empty directory
++dit(bf(--force-delete)) This option tells rsync to delete a non-empty directory
+ when it is to be replaced by a non-directory. This is only relevant if
+ deletions are not active (see bf(--delete) for details).
+
+-Note for older rsync versions: bf(--force) used to still be required when
+-using bf(--delete-after), and it used to be non-functional unless the
++This option can be abbreviated bf(--force) for backward compatibility.
++Note that some older rsync versions used to still require bf(--force)
++when using bf(--delete-after), and it used to be non-functional unless the
+ bf(--recursive) option was also enabled.
+
+ dit(bf(--max-delete=NUM)) This tells rsync not to delete more than NUM
+@@ -1651,7 +1680,7 @@ with older versions of rsync, but that also turns on the output of other
+ verbose messages).
+
+ The "%i" escape has a cryptic output that is 11 letters long. The general
+-format is like the string bf(YXcstpoguax), where bf(Y) is replaced by the
++format is like the string bf(YXcstpogfax), where bf(Y) is replaced by the
+ type of update being done, bf(X) is replaced by the file-type, and the
+ other letters represent attributes that may be output if they are being
+ modified.
+@@ -1707,7 +1736,7 @@ 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() The bf(f) means that the fileflags information changed.
+ it() The bf(a) means that the ACL information changed.
+ it() The bf(x) means that the extended attribute information changed.
+ ))
+diff --git a/syscall.c b/syscall.c
+--- a/syscall.c
++++ b/syscall.c
+@@ -33,6 +33,7 @@ extern int dry_run;
+ extern int am_root;
+ extern int read_only;
+ extern int list_only;
++extern int force_change;
+ extern int preserve_perms;
+ extern int preserve_executability;
+
+@@ -50,7 +51,23 @@ int do_unlink(const char *fname)
+ {
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+- return unlink(fname);
++ if (unlink(fname) == 0)
++ return 0;
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && errno == EPERM) {
++ STRUCT_STAT st;
++
++ if (x_lstat(fname, &st, NULL) == 0
++ && make_mutable(fname, st.st_mode, st.st_flags, force_change) > 0) {
++ if (unlink(fname) == 0)
++ return 0;
++ undo_make_mutable(fname, st.st_flags);
++ }
++ /* TODO: handle immutable directories */
++ errno = EPERM;
++ }
++#endif
++ return -1;
+ }
+
+ int do_symlink(const char *fname1, const char *fname2)
+@@ -69,14 +86,37 @@ int do_link(const char *fname1, const char *fname2)
+ }
+ #endif
+
+-int do_lchown(const char *path, uid_t owner, gid_t group)
++int do_lchown(const char *path, uid_t owner, gid_t group, mode_t mode, uint32 fileflags)
+ {
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+ #ifndef HAVE_LCHOWN
+ #define lchown chown
+ #endif
+- return lchown(path, owner, group);
++ if (lchown(path, owner, group) == 0)
++ return 0;
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && errno == EPERM) {
++ if (fileflags == NO_FFLAGS) {
++ STRUCT_STAT st;
++ if (x_lstat(path, &st, NULL) == 0) {
++ mode = st.st_mode;
++ fileflags = st.st_flags;
++ }
++ }
++ if (fileflags != NO_FFLAGS
++ && make_mutable(path, mode, fileflags, force_change) > 0) {
++ int ret = lchown(path, owner, group);
++ undo_make_mutable(path, fileflags);
++ if (ret == 0)
++ return 0;
++ }
++ errno = EPERM;
++ }
++#else
++ mode = fileflags = 0; /* avoid compiler warning */
++#endif
++ return -1;
+ }
+
+ int do_mknod(const char *pathname, mode_t mode, dev_t dev)
+@@ -116,7 +156,7 @@ int do_mknod(const char *pathname, mode_t mode, dev_t dev)
+ return -1;
+ close(sock);
+ #ifdef HAVE_CHMOD
+- return do_chmod(pathname, mode);
++ return do_chmod(pathname, mode, 0);
+ #else
+ return 0;
+ #endif
+@@ -133,7 +173,22 @@ int do_rmdir(const char *pathname)
+ {
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+- return rmdir(pathname);
++ if (rmdir(pathname) == 0)
++ return 0;
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && errno == EPERM) {
++ STRUCT_STAT st;
++
++ if (x_lstat(pathname, &st, NULL) == 0
++ && make_mutable(pathname, st.st_mode, st.st_flags, force_change) > 0) {
++ if (rmdir(pathname) == 0)
++ return 0;
++ undo_make_mutable(pathname, st.st_flags);
++ }
++ errno = EPERM;
++ }
++#endif
++ return -1;
+ }
+
+ int do_open(const char *pathname, int flags, mode_t mode)
+@@ -147,7 +202,7 @@ int do_open(const char *pathname, int flags, mode_t mode)
+ }
+
+ #ifdef HAVE_CHMOD
+-int do_chmod(const char *path, mode_t mode)
++int do_chmod(const char *path, mode_t mode, uint32 fileflags)
+ {
+ int code;
+ if (dry_run) return 0;
+@@ -168,17 +223,74 @@ int do_chmod(const char *path, mode_t mode)
+ #endif
+ } else
+ code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */
++#ifdef SUPPORT_FORCE_CHANGE
++ if (code < 0 && force_change && errno == EPERM && !S_ISLNK(mode)) {
++ if (fileflags == NO_FFLAGS) {
++ STRUCT_STAT st;
++ if (x_lstat(path, &st, NULL) == 0)
++ fileflags = st.st_flags;
++ }
++ if (fileflags != NO_FFLAGS
++ && make_mutable(path, mode, fileflags, force_change) > 0) {
++ code = chmod(path, mode & CHMOD_BITS);
++ undo_make_mutable(path, fileflags);
++ if (code == 0)
++ return 0;
++ }
++ errno = EPERM;
++ }
++#else
++ fileflags = 0; /* avoid compiler warning */
++#endif
+ if (code != 0 && (preserve_perms || preserve_executability))
+ return code;
+ return 0;
+ }
+ #endif
+
++#ifdef HAVE_CHFLAGS
++int do_chflags(const char *path, uint32 fileflags)
++{
++ if (dry_run) return 0;
++ RETURN_ERROR_IF_RO_OR_LO;
++ return chflags(path, fileflags);
++}
++#endif
++
+ int do_rename(const char *fname1, const char *fname2)
+ {
+ if (dry_run) return 0;
+ RETURN_ERROR_IF_RO_OR_LO;
+- return rename(fname1, fname2);
++ if (rename(fname1, fname2) == 0)
++ return 0;
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change && errno == EPERM) {
++ STRUCT_STAT st1, st2;
++ int became_mutable;
++
++ if (x_lstat(fname1, &st1, NULL) != 0)
++ goto failed;
++ became_mutable = make_mutable(fname1, st1.st_mode, st1.st_flags, force_change) > 0;
++ if (became_mutable && rename(fname1, fname2) == 0)
++ goto success;
++ if (x_lstat(fname2, &st2, NULL) == 0
++ && make_mutable(fname2, st2.st_mode, st2.st_flags, force_change) > 0) {
++ if (rename(fname1, fname2) == 0) {
++ success:
++ if (became_mutable) /* Yes, use fname2 and st1! */
++ undo_make_mutable(fname2, st1.st_flags);
++ return 0;
++ }
++ undo_make_mutable(fname2, st2.st_flags);
++ }
++ /* TODO: handle immutable directories */
++ if (became_mutable)
++ undo_make_mutable(fname1, st1.st_flags);
++ failed:
++ errno = EPERM;
++ }
++#endif
++ return -1;
+ }
+
+ void trim_trailing_slashes(char *name)
+diff --git a/t_stub.c b/t_stub.c
+--- a/t_stub.c
++++ b/t_stub.c
+@@ -26,6 +26,7 @@ int module_id = -1;
+ int relative_paths = 0;
+ int human_readable = 0;
+ int module_dirlen = 0;
++int force_change = 0;
+ int preserve_xattrs = 0;
+ mode_t orig_umask = 002;
+ char *partial_dir;
+@@ -89,3 +90,23 @@ struct filter_list_struct daemon_filter_list;
+ {
+ return "tester";
+ }
++
++#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
++ int make_mutable(UNUSED(const char *fname), UNUSED(mode_t mode), UNUSED(uint32 fileflags), UNUSED(uint32 iflags))
++{
++ return 0;
++}
++
++/* Undo a prior make_mutable() call that returned a 1. */
++ int undo_make_mutable(UNUSED(const char *fname), UNUSED(uint32 fileflags))
++{
++ return 0;
++}
++#endif
++
++#ifdef SUPPORT_XATTRS
++ int x_lstat(UNUSED(const char *fname), UNUSED(STRUCT_STAT *fst), UNUSED(STRUCT_STAT *xst))
++{
++ return -1;
++}
++#endif
+diff --git a/util.c b/util.c
+--- a/util.c
++++ b/util.c
+@@ -29,6 +29,7 @@ extern int module_id;
+ extern int modify_window;
+ extern int relative_paths;
+ extern int human_readable;
++extern int force_change;
+ extern int preserve_xattrs;
+ extern char *module_dir;
+ extern unsigned int module_dirlen;
+@@ -123,7 +124,7 @@ NORETURN void overflow_exit(const char *str)
+ exit_cleanup(RERR_MALLOC);
+ }
+
+-int set_modtime(const char *fname, time_t modtime, mode_t mode)
++int set_modtime(const char *fname, time_t modtime, mode_t mode, uint32 fileflags)
+ {
+ #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
+ if (S_ISLNK(mode))
+@@ -140,6 +141,7 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
+ return 0;
+
+ {
++ int ret;
+ #ifdef HAVE_UTIMES
+ struct timeval t[2];
+ t[0].tv_sec = time(NULL);
+@@ -153,20 +155,39 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
+ return 0;
+ }
+ # endif
+- return utimes(fname, t);
++#define SET_THE_TIME(fn) utimes(fn, t)
+ #elif defined HAVE_STRUCT_UTIMBUF
+ struct utimbuf tbuf;
+ tbuf.actime = time(NULL);
+ tbuf.modtime = modtime;
+- return utime(fname,&tbuf);
++#define SET_THE_TIME(fn) utime(fn, &tbuf)
+ #elif defined HAVE_UTIME
+ time_t t[2];
+ t[0] = time(NULL);
+ t[1] = modtime;
+- return utime(fname,t);
++#define SET_THE_TIME(fn) utime(fn, t)
+ #else
+ #error No file-time-modification routine found!
+ #endif
++ ret = SET_THE_TIME(fname);
++#ifdef SUPPORT_FORCE_CHANGE
++ if (ret != 0 && force_change && errno == EPERM) {
++ if (fileflags == NO_FFLAGS) {
++ STRUCT_STAT st;
++ if (x_lstat(fname, &st, NULL) == 0)
++ fileflags = st.st_flags;
++ }
++ if (fileflags != NO_FFLAGS
++ && make_mutable(fname, mode, fileflags, force_change) > 0) {
++ ret = SET_THE_TIME(fname);
++ undo_make_mutable(fname, fileflags);
++ }
++ errno = EPERM;
++ }
++#else
++ fileflags = 0; /* avoid compiler warning */
++#endif
++ return ret;
+ }
+ }
+
+diff --git a/xattrs.c b/xattrs.c
+--- a/xattrs.c
++++ b/xattrs.c
+@@ -281,6 +281,10 @@ int get_xattr(const char *fname, stat_x *sxp)
+ {
+ sxp->xattr = new(item_list);
+ *sxp->xattr = empty_xattr;
++
++ if (IS_SPECIAL(sxp->st.st_mode) || IS_DEVICE(sxp->st.st_mode))
++ return 0;
++
+ if (rsync_xal_get(fname, sxp->xattr) < 0) {
+ free_xattr(sxp);
+ return -1;
+@@ -861,6 +865,11 @@ int set_xattr(const char *fname, const struct file_struct *file,
+ return -1;
+ }
+
++ if (IS_SPECIAL(sxp->st.st_mode) || IS_DEVICE(sxp->st.st_mode)) {
++ errno = ENOTSUP;
++ return -1;
++ }
++
+ ndx = F_XATTR(file);
+ return rsync_xal_set(fname, lst + ndx, fnamecmp, sxp);
+ }
+@@ -977,7 +986,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
+ mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
+ | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
+ if (fst.st_mode != mode)
+- do_chmod(fname, mode);
++ do_chmod(fname, mode, ST_FLAGS(fst));
+ if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
+ fst.st_rdev = 0; /* just in case */
+
+diff -up a/config.h.in b/config.h.in
+--- a/config.h.in
++++ b/config.h.in
+@@ -67,6 +67,9 @@
+ /* Define to 1 if vsprintf has a C99-compatible return value */
+ #undef HAVE_C99_VSNPRINTF
+
++/* Define to 1 if you have the `chflags' function. */
++#undef HAVE_CHFLAGS
++
+ /* Define to 1 if you have the `chmod' function. */
+ #undef HAVE_CHMOD
+
+diff -up a/configure.sh b/configure.sh
+--- a/configure.sh
++++ b/configure.sh
+@@ -14796,12 +14796,13 @@ fi
+
+
+
++
+ for ac_func in waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
+ fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
+ memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
+ strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
+ setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
+- strerror putenv iconv_open locale_charset nl_langinfo getxattr \
++ chflags strerror putenv iconv_open locale_charset nl_langinfo getxattr \
+ extattr_get_link sigaction sigprocmask setattrlist
+ do
+ as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+diff -up a/proto.h b/proto.h
+--- a/proto.h
++++ b/proto.h
+@@ -272,6 +272,8 @@ int read_ndx_and_attrs(int f_in, int *if
+ void free_sums(struct sum_struct *s);
+ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
+ int exists);
++int make_mutable(const char *fname, mode_t mode, uint32 fileflags, uint32 iflags);
++int undo_make_mutable(const char *fname, uint32 fileflags);
+ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
+ const char *fnamecmp, int flags);
+ RETSIGTYPE sig_int(UNUSED(int val));
+@@ -296,11 +298,12 @@ int sock_exec(const char *prog);
+ int do_unlink(const char *fname);
+ int do_symlink(const char *fname1, const char *fname2);
+ int do_link(const char *fname1, const char *fname2);
+-int do_lchown(const char *path, uid_t owner, gid_t group);
++int do_lchown(const char *path, uid_t owner, gid_t group, mode_t mode, uint32 fileflags);
+ int do_mknod(const char *pathname, mode_t mode, dev_t dev);
+ int do_rmdir(const char *pathname);
+ int do_open(const char *pathname, int flags, mode_t mode);
+-int do_chmod(const char *path, mode_t mode);
++int do_chmod(const char *path, mode_t mode, uint32 fileflags);
++int do_chflags(const char *path, uint32 fileflags);
+ int do_rename(const char *fname1, const char *fname2);
+ void trim_trailing_slashes(char *name);
+ int do_mkdir(char *fname, mode_t mode);
+@@ -328,7 +331,7 @@ int fd_pair(int fd[2]);
+ void print_child_argv(const char *prefix, char **cmd);
+ NORETURN void out_of_memory(const char *str);
+ NORETURN void overflow_exit(const char *str);
+-int set_modtime(const char *fname, time_t modtime, mode_t mode);
++int set_modtime(const char *fname, time_t modtime, mode_t mode, uint32 fileflags);
+ int mkdir_defmode(char *fname);
+ int create_directory_path(char *fname);
+ int full_write(int desc, const char *ptr, size_t len);
+diff -up a/rsync.1 b/rsync.1
+--- a/rsync.1
++++ b/rsync.1
+@@ -413,6 +413,7 @@ to the detailed description below for a
+ \-K, \-\-keep\-dirlinks treat symlinked dir on receiver as dir
+ \-H, \-\-hard\-links preserve hard links
+ \-p, \-\-perms preserve permissions
++ \-\-fileflags preserve file-flags (aka chflags)
+ \-E, \-\-executability preserve executability
+ \-\-chmod=CHMOD affect file and/or directory permissions
+ \-A, \-\-acls preserve ACLs (implies \-p)
+@@ -444,7 +445,10 @@ to the detailed description below for a
+ \-\-delete\-after receiver deletes after transfer, not before
+ \-\-delete\-excluded also delete excluded files from dest dirs
+ \-\-ignore\-errors delete even if there are I/O errors
+- \-\-force force deletion of dirs even if not empty
++ \-\-force\-delete force deletion of dirs even if not empty
++ \-\-force\-change affect user/system immutable files/dirs
++ \-\-force\-uchange affect user-immutable files/dirs
++ \-\-force\-schange affect system-immutable files/dirs
+ \-\-max\-delete=NUM don't delete more than NUM files
+ \-\-max\-size=SIZE don't transfer any file larger than SIZE
+ \-\-min\-size=SIZE don't transfer any file smaller than SIZE
+@@ -631,7 +635,8 @@ specified, in which case \fB\-r\fP is no
+ .IP
+ Note that \fB\-a\fP \fBdoes not preserve hardlinks\fP, because
+ finding multiply-linked files is expensive. You must separately
+-specify \fB\-H\fP.
++specify \fB\-H\fP. Note also that for backward compatibility, \fB\-a\fP
++currently does \fBnot\fP imply the \fB\-\-fileflags\fP option.
+ .IP
+ .IP "\-\-no\-OPTION"
+ You may turn off one or more implied options by prefixing
+@@ -920,7 +925,7 @@ they would be using \fB\-\-copy\-links\f
+ Without this option, if the sending side has replaced a directory with a
+ symlink to a directory, the receiving side will delete anything that is in
+ the way of the new symlink, including a directory hierarchy (as long as
+-\fB\-\-force\fP or \fB\-\-delete\fP is in effect).
++\fB\-\-force\-delete\fP or \fB\-\-delete\fP is in effect).
+ .IP
+ See also \fB\-\-keep\-dirlinks\fP for an analogous option for the receiving
+ side.
+@@ -1075,6 +1080,33 @@ super-user copies all namespaces except
+ the user.* namespace. To be able to backup and restore non-user namespaces as
+ a normal user, see the \fB\-\-fake\-super\fP option.
+ .IP
++.IP "\fB\-\-fileflags\fP"
++This option causes rsync to update the file-flags to be
++the same as the source files and directories (if your OS supports the
++\fBchflags\fP(2) system call). Some flags can only be altered by the super-user
++and some might only be unset below a certain secure-level (usually single-user
++mode). It will not make files alterable that are set to immutable on the
++receiver. To do that, see \fB\-\-force\-change\fP, \fB\-\-force\-uchange\fP, and
++\fB\-\-force\-schange\fP.
++.IP
++.IP "\fB\-\-force\-change\fP"
++This option causes rsync to disable both user-immutable
++and system-immutable flags on files and directories that are being updated or
++deleted on the receiving side. This option overrides \fB\-\-force\-uchange\fP and
++\fB\-\-force\-schange\fP.
++.IP
++.IP "\fB\-\-force\-uchange\fP"
++This option causes rsync to disable user-immutable
++flags on files and directories that are being updated or deleted on the
++receiving side. It does not try to affect system flags. This option overrides
++\fB\-\-force\-change\fP and \fB\-\-force\-schange\fP.
++.IP
++.IP "\fB\-\-force\-schange\fP"
++This option causes rsync to disable system-immutable
++flags on files and directories that are being updated or deleted on the
++receiving side. It does not try to affect user flags. This option overrides
++\fB\-\-force\-change\fP and \fB\-\-force\-schange\fP.
++.IP
+ .IP "\fB\-\-chmod\fP"
+ This option tells rsync to apply one or more
+ comma-separated \(lqchmod\(rq strings to the permission of the files in the
+@@ -1360,13 +1392,14 @@ See \fB\-\-delete\fP (which is implied)
+ Tells \fB\-\-delete\fP to go ahead and delete files
+ even when there are I/O errors.
+ .IP
+-.IP "\fB\-\-force\fP"
++.IP "\fB\-\-force\-delete\fP"
+ This option tells rsync to delete a non-empty directory
+ when it is to be replaced by a non-directory. This is only relevant if
+ deletions are not active (see \fB\-\-delete\fP for details).
+ .IP
+-Note for older rsync versions: \fB\-\-force\fP used to still be required when
+-using \fB\-\-delete\-after\fP, and it used to be non-functional unless the
++This option can be abbreviated \fB\-\-force\fP for backward compatibility.
++Note that some older rsync versions used to still require \fB\-\-force\fP
++when using \fB\-\-delete\-after\fP, and it used to be non-functional unless the
+ \fB\-\-recursive\fP option was also enabled.
+ .IP
+ .IP "\fB\-\-max\-delete=NUM\fP"
+@@ -1893,7 +1926,7 @@ with older versions of rsync, but that a
+ verbose messages).
+ .IP
+ The \(lq%i\(rq escape has a cryptic output that is 11 letters long. The general
+-format is like the string \fBYXcstpoguax\fP, where \fBY\fP is replaced by the
++format is like the string \fBYXcstpogfax\fP, where \fBY\fP is replaced by the
+ type of update being done, \fBX\fP is replaced by the file-type, and the
+ other letters represent attributes that may be output if they are being
+ modified.
+@@ -1963,7 +1996,7 @@ sender's value (requires \fB\-\-owner\fP
+ A \fBg\fP means the group is different and is being updated to the
+ sender's value (requires \fB\-\-group\fP and the authority to set the group).
+ .IP o
+-The \fBu\fP slot is reserved for future use.
++The \fBf\fP means that the fileflags information changed.
+ .IP o
+ The \fBa\fP means that the ACL information changed.
+ .IP o
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20080714/38635dc4/attachment-0001.html
More information about the macports-changes
mailing list