[38605] trunk/dports/net/rsync

ecronin at macports.org ecronin at macports.org
Fri Jul 25 12:02:16 PDT 2008


Revision: 38605
          http://trac.macosforge.org/projects/macports/changeset/38605
Author:   ecronin at macports.org
Date:     2008-07-25 12:02:16 -0700 (Fri, 25 Jul 2008)
Log Message:
-----------
net/rsync: Fix compilation on panther/10.3 where neither xattr nor acl headers are present; rename patches to pass lint; not bumping revision on purpose, neither fix changes already installed packages

Modified Paths:
--------------
    trunk/dports/net/rsync/Portfile

Added Paths:
-----------
    trunk/dports/net/rsync/files/patch-crtimes.diff
    trunk/dports/net/rsync/files/patch-fileflags.diff

Removed 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-25 18:48:44 UTC (rev 38604)
+++ trunk/dports/net/rsync/Portfile	2008-07-25 19:02:16 UTC (rev 38605)
@@ -30,8 +30,8 @@
 
 depends_lib         port:popt port:libiconv
 
-patchfiles          fileflags.diff \
-                    crtimes.diff
+patchfiles          patch-fileflags.diff \
+                    patch-crtimes.diff
 patch.pre_args      -p1
 
 configure.args      --mandir=${prefix}/share/man \
@@ -52,6 +52,11 @@
                     ${destroot}${prefix}/share/doc/${name}
 }
 
+platform darwin 7 {
+        configure.args-delete   --enable-xattr-support
+        configure.args-append   --disable-acl-support --disable-xattr-support
+}
+
 livecheck.check     regex
 livecheck.regex     "Rsync version (\\d+(?:\\.\\d+)*) released"
 

Deleted: trunk/dports/net/rsync/files/crtimes.diff
===================================================================
--- trunk/dports/net/rsync/files/crtimes.diff	2008-07-25 18:48:44 UTC (rev 38604)
+++ trunk/dports/net/rsync/files/crtimes.diff	2008-07-25 19:02:16 UTC (rev 38605)
@@ -1,615 +0,0 @@
-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

Deleted: trunk/dports/net/rsync/files/fileflags.diff
===================================================================
--- trunk/dports/net/rsync/files/fileflags.diff	2008-07-25 18:48:44 UTC (rev 38604)
+++ trunk/dports/net/rsync/files/fileflags.diff	2008-07-25 19:02:16 UTC (rev 38605)
@@ -1,1262 +0,0 @@
-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 

Copied: trunk/dports/net/rsync/files/patch-crtimes.diff (from rev 38537, trunk/dports/net/rsync/files/crtimes.diff)
===================================================================
--- trunk/dports/net/rsync/files/patch-crtimes.diff	                        (rev 0)
+++ trunk/dports/net/rsync/files/patch-crtimes.diff	2008-07-25 19:02:16 UTC (rev 38605)
@@ -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

Copied: trunk/dports/net/rsync/files/patch-fileflags.diff (from rev 38537, trunk/dports/net/rsync/files/fileflags.diff)
===================================================================
--- trunk/dports/net/rsync/files/patch-fileflags.diff	                        (rev 0)
+++ trunk/dports/net/rsync/files/patch-fileflags.diff	2008-07-25 19:02:16 UTC (rev 38605)
@@ -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/20080725/7c2ba7a7/attachment-0001.html 


More information about the macports-changes mailing list