./configure
make
-based-on: d52aeae4e9af689aed4ae6af2b7602552c1383e7
+based-on: 98ec67d7860209c5cbbc848769f2ba1a7062ddef
diff --git a/Makefile.in b/Makefile.in
--- a/Makefile.in
+++ b/Makefile.in
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
-@@ -589,7 +589,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
+@@ -591,7 +591,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
seteuid strerror putenv iconv_open locale_charset nl_langinfo getxattr \
extattr_get_link sigaction sigprocmask setattrlist getgrouplist \
#ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
if (!ACL_READY(*sxp))
-@@ -1320,6 +1331,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1322,6 +1333,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);
}
if (statret != 0 && basis_dir[0] != NULL) {
int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
itemizing, code);
-@@ -1362,10 +1377,15 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1357,10 +1372,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. */
rsyserr(FERROR_XFER, errno,
"failed to modify permissions on %s",
full_fname(fname));
-@@ -1400,6 +1420,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1403,6 +1423,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_HARD_LINKS
if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
-@@ -1979,13 +2003,17 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
+@@ -1982,13 +2006,17 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
continue;
fname = f_name(file, NULL);
if (fix_dir_perms)
c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
c[11] = '\0';
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -26,6 +26,9 @@
+ #if defined CONFIG_LOCALE && defined HAVE_LOCALE_H
+ #include <locale.h>
+ #endif
++#ifdef SUPPORT_FORCE_CHANGE
++#include <sys/sysctl.h>
++#endif
+
+ extern int dry_run;
+ extern int list_only;
+@@ -834,6 +837,22 @@ static int do_recv(int f_in, int f_out, char *local_name)
+ * points to an identical file won't be replaced by the referent. */
+ copy_links = copy_dirlinks = copy_unsafe_links = 0;
+
++#ifdef SUPPORT_FORCE_CHANGE
++ if (force_change & SYS_IMMUTABLE) {
++ /* Determine whether we'll be able to unlock a system immutable item. */
++ int mib[2];
++ int securityLevel = 0;
++ size_t len = sizeof securityLevel;
++
++ mib[0] = CTL_KERN;
++ mib[1] = KERN_SECURELVL;
++ if (sysctl(mib, 2, &securityLevel, &len, NULL, 0) == 0 && securityLevel > 0) {
++ rprintf(FERROR, "System security level is too high to force mutability on system immutable files and directories.\n");
++ exit_cleanup(RERR_UNSUPPORTED);
++ }
++ }
++#endif
++
+ #ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && !inc_recurse)
+ match_hard_links(first_flist);
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
diff --git a/generator.c b/generator.c
--- a/generator.c
+++ b/generator.c
-@@ -37,6 +37,8 @@ extern int implied_dirs;
+@@ -37,6 +37,7 @@ extern int implied_dirs;
extern int keep_dirlinks;
extern int preserve_acls;
extern int preserve_xattrs;
+extern int preserve_hfs_compression;
-+extern int fs_supports_hfs_compression;
extern int preserve_links;
extern int preserve_devices;
extern int preserve_specials;
-@@ -1385,13 +1387,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
- goto cleanup;
- }
- }
--#ifdef SUPPORT_XATTRS
-- if (preserve_xattrs && statret == 1)
-- copy_xattrs(fnamecmpbuf, fname);
--#endif
-- if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0)
-- && INFO_GTE(NAME, 1) && code != FNONE && f_out != -1)
-- rprintf(code, "%s/\n", fname);
-
- /* We need to ensure that the dirs in the transfer have writable
- * permissions during the time we are putting files within them.
-@@ -1401,6 +1396,16 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1396,6 +1397,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
&& make_mutable(fname, file->mode, F_FFLAGS(file), force_change))
need_retouch_dir_perms = 1;
#endif
-+
-+#ifdef SUPPORT_XATTRS
-+ /* Copy directory xattrs for a directory that already existed */
-+ if (preserve_xattrs && statret == 1)
-+ copy_xattrs(fnamecmpbuf, fname);
-+#endif
-+ if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0)
-+ && INFO_GTE(NAME, 1) && code != FNONE && f_out != -1)
-+ rprintf(code, "%s/\n", fname);
+
#ifdef HAVE_CHMOD
if (!am_root && !(file->mode & S_IWUSR) && dir_tweaking) {
mode_t mode = file->mode | S_IWUSR;
-@@ -1678,6 +1683,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1681,6 +1683,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
fname, fnamecmpbuf);
}
sx.st.st_size = F_LENGTH(fuzzy_file);
statret = 0;
fnamecmp = fnamecmpbuf;
fnamecmp_type = FNAMECMP_FUZZY;
-@@ -1853,6 +1866,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1856,6 +1866,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
if (read_batch)
goto cleanup;
+ /* At this point the attrs have already been copied, we don't need to transfer a data fork
+ * If my filesystem doesn't support HFS compression, the existing file's content
+ * will not be automatically truncated, so we'll do that manually here */
-+ if (!fs_supports_hfs_compression && sx.st.st_size > 0) {
++ if (preserve_hfs_compression && sx.st.st_size > 0) {
+ if (ftruncate(fd, 0) == 0)
+ sx.st.st_size = 0;
+ }
diff --git a/main.c b/main.c
--- a/main.c
+++ b/main.c
-@@ -27,6 +27,19 @@
- #include <locale.h>
+@@ -29,6 +29,10 @@
+ #ifdef SUPPORT_FORCE_CHANGE
+ #include <sys/sysctl.h>
#endif
-
+#ifdef SUPPORT_HFS_COMPRESSION
-+/* For getattrlist() */
-+#include <sys/attr.h>
-+/* For statfs(): */
-+#include <sys/mount.h>
-+/* For dirname() */
-+#include <libgen.h>
-+#endif
-+
-+#ifdef SUPPORT_FORCE_CHANGE
-+#include <sys/sysctl.h>
++#include <sys/attr.h> /* For getattrlist() */
++#include <sys/mount.h> /* For statfs() */
+#endif
-+
+
extern int dry_run;
extern int list_only;
- extern int io_timeout;
-@@ -50,7 +63,9 @@ extern int copy_dirlinks;
+@@ -53,7 +57,9 @@ extern int copy_dirlinks;
extern int copy_unsafe_links;
extern int keep_dirlinks;
extern int preserve_hard_links;
extern int file_total;
extern int recurse;
extern int xfer_dirs;
-@@ -102,6 +117,7 @@ int daemon_over_rsh = 0;
+@@ -105,6 +111,7 @@ int daemon_over_rsh = 0;
mode_t orig_umask = 0;
int batch_gen_fd = -1;
int sender_keeps_checksum = 0;
/* There's probably never more than at most 2 outstanding child processes,
* but set it higher, just in case. */
-@@ -1062,6 +1078,86 @@ int child_main(int argc, char *argv[])
- return 0;
+@@ -557,6 +564,43 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
+ return 0; /* not reached */
}
+#ifdef SUPPORT_HFS_COMPRESSION
-+static int filesystem_supports_hfs_compression(const char *path)
++static void hfs_receiver_check(const char *dest_path)
+{
+ struct statfs fsb;
-+ char *parent;
-+ int statfs_ret, saved_err;
-+
-+ statfs_ret = statfs(path, &fsb);
-+ if (statfs_ret != 0) {
-+ saved_err = errno;
-+ if ((parent = (char *)dirname((char *)path)) != NULL)
-+ statfs_ret = statfs(parent, &fsb);
-+ errno = saved_err;
++ struct attrlist attrs;
++ struct {
++ int32_t len;
++ vol_capabilities_set_t caps;
++ } attrData;
++
++ if (preserve_hfs_compression != 1)
++ return; /* Nothing to check if --hfs-compression option isn't enabled. */
++
++ if (statfs(dest_path, &fsb) < 0) {
++ rsyserr(FERROR, errno, "statfs %s failed", full_fname(dest_path));
++ exit_cleanup(RERR_FILESELECT);
+ }
+
-+ if (statfs_ret == 0) {
-+ struct attrlist attrs;
-+ struct {
-+ int32_t len;
-+ vol_capabilities_set_t caps;
-+ } attrData;
-+
-+ bzero(&attrs, sizeof(attrs));
-+ attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
-+ attrs.volattr = ATTR_VOL_CAPABILITIES;
-+
-+ bzero(&attrData, sizeof(attrData));
-+ attrData.len = sizeof(attrData);
-+
-+ int ret = getattrlist(fsb.f_mntonname, &attrs, &attrData, sizeof(attrData), 0);
-+ if (ret == 0) {
-+ if (attrData.caps[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION) {
-+ /* Compression is supported */
-+ return 1;
-+ }
-+ } else {
-+ rprintf(FERROR, "Failure in getattrlist while determining HFS compression support on %s (%s): %s\n", path, who_am_i(), strerror(errno));
-+ }
-+ } else {
-+ rprintf(FERROR, "Failure in statfs while determining HFS compression support on %s (%s): %s\n", path, who_am_i(), strerror(errno));
++ bzero(&attrs, sizeof attrs);
++ attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
++ attrs.volattr = ATTR_VOL_CAPABILITIES;
++
++ bzero(&attrData, sizeof attrData);
++ attrData.len = sizeof attrData;
++
++ if (getattrlist(fsb.f_mntonname, &attrs, &attrData, sizeof attrData, 0) < 0) {
++ rsyserr(FERROR, errno, "getattrlist %s failed", full_fname(dest_path));
++ exit_cleanup(RERR_FILESELECT);
++ }
++
++ if (!(attrData.caps[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION)) {
++ rprintf(FERROR, "The destination filesystem does not support HFS+ compression.\n");
++ exit_cleanup(RERR_UNSUPPORTED);
+ }
-+ return 0;
+}
+#endif
+
-+#if defined SUPPORT_HFS_COMPRESSION || defined SUPPORT_FORCE_CHANGE
-+static void do_filesystem_compatibility_checks(const char *path)
-+{
+ /* The receiving side operates in one of two modes:
+ *
+ * 1. it receives any number of files into a destination directory,
+@@ -615,6 +659,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
+ exit_cleanup(RERR_FILESELECT);
+ }
+ filesystem_dev = st.st_dev; /* ensures --force works right w/-x */
+#ifdef SUPPORT_HFS_COMPRESSION
-+ fs_supports_hfs_compression = filesystem_supports_hfs_compression(path);
-+ if (preserve_hfs_compression > 0) {
-+ /* If the filesystem doesn't support compression and decmpfs
-+ * protection wasn't requested, disable support for compression */
-+ if (!fs_supports_hfs_compression && preserve_hfs_compression < 2) {
-+ preserve_hfs_compression = 0;
-+ rprintf(FINFO, "%s: Disabling HFS compression support, %s doesn't support it (use --protect-decmpfs to force protection of the com.apple.decmpfs extended attribute).\n", who_am_i(), path);
-+ }
-+ }
++ hfs_receiver_check(dest_path);
+#endif
-+
-+#ifdef SUPPORT_FORCE_CHANGE
-+ if (force_change & SYS_IMMUTABLE) {
-+ /* determine whether we'll be able to unlock a system immutable item */
-+ int mib[2];
-+ int securityLevel = 0;
-+ size_t len = sizeof(securityLevel);
-+
-+ mib[0] = CTL_KERN;
-+ mib[1] = KERN_SECURELVL;
-+ if (sysctl(mib, 2, &securityLevel, &len, NULL, 0) == 0 && securityLevel > 0) {
-+ rprintf(FERROR, "System security level is too high to force mutability on system immutable files and directories.\n");
-+ /*force_change ~= ~SYS_IMMUTABLE;*/
-+ exit_cleanup(RERR_UNSUPPORTED);
-+ }
-+ }
+ return NULL;
+ }
+ if (file_total > 1) {
+@@ -675,7 +722,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+-
++#ifdef SUPPORT_HFS_COMPRESSION
++ hfs_receiver_check(dest_path);
+#endif
-+
-+ /* TODO: ACLs and xattrs? */
-+}
+ return NULL;
+ }
+
+@@ -695,6 +744,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
++#ifdef SUPPORT_HFS_COMPRESSION
++ hfs_receiver_check(dest_path);
+#endif
+ *cp = '/';
+
+ return cp + 1;
+@@ -1081,7 +1133,6 @@ int child_main(int argc, char *argv[])
+ return 0;
+ }
+-
void start_server(int f_in, int f_out, int argc, char *argv[])
{
-@@ -1069,6 +1165,10 @@ void start_server(int f_in, int f_out, int argc, char *argv[])
- set_nonblocking(f_out);
-
- io_set_sock_fds(f_in, f_out);
-+#if defined SUPPORT_HFS_COMPRESSION || defined SUPPORT_FORCE_CHANGE
-+ if (!local_server)
-+ do_filesystem_compatibility_checks(argv[argc - 1]);
-+#endif
- setup_protocol(f_out, f_in);
-
- if (protocol_version >= 23)
-@@ -1269,7 +1369,13 @@ static int start_client(int argc, char *argv[])
- am_sender = 0;
- if (rsync_port)
- daemon_over_rsh = shell_cmd ? 1 : -1;
-+#if defined SUPPORT_HFS_COMPRESSION || defined SUPPORT_FORCE_CHANGE
-+ do_filesystem_compatibility_checks(*argv);
-+#endif
- } else { /* source is local, check dest arg */
-+#if defined SUPPORT_HFS_COMPRESSION || defined SUPPORT_FORCE_CHANGE
-+ do_filesystem_compatibility_checks(argv[0]);
-+#endif
- am_sender = 1;
-
- if (argc > 1) {
-@@ -1297,6 +1403,9 @@ static int start_client(int argc, char *argv[])
- exit_cleanup(RERR_SYNTAX);
- }
- shell_machine = NULL;
-+#if defined SUPPORT_HFS_COMPRESSION || defined SUPPORT_FORCE_CHANGE
-+ do_filesystem_compatibility_checks(p);
-+#endif
- } else { /* hostspec was found, so dest is remote */
- argv[argc] = path;
- if (rsync_port)
+ set_nonblocking(f_in);
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
rprintf(F," -X, --xattrs preserve extended attributes\n");
#endif
+#ifdef SUPPORT_HFS_COMPRESSION
-+ rprintf(F," --hfs-compression preserve HFS compression (if source & destination support it)\n");
-+ rprintf(F," --protect-decmpfs preserve HFS compression (regardless of volume support)\n");
++ rprintf(F," --hfs-compression preserve HFS compression if supported\n");
++ rprintf(F," --protect-decmpfs preserve HFS compression as xattrs\n");
+#endif
rprintf(F," -o, --owner preserve owner (super-user only)\n");
rprintf(F," -g, --group preserve group\n");
#endif
+#ifdef SUPPORT_HFS_COMPRESSION
+ {"hfs-compression", 0, POPT_ARG_VAL, &preserve_hfs_compression, 1, 0, 0 },
-+ {"protect-decmpfs", 0, POPT_ARG_VAL, &preserve_hfs_compression, 2, 0, 0 },
+ {"no-hfs-compression",0, POPT_ARG_VAL, &preserve_hfs_compression, 0, 0, 0 },
++ {"protect-decmpfs", 0, POPT_ARG_VAL, &preserve_hfs_compression, 2, 0, 0 },
+ {"no-protect-decmpfs",0, POPT_ARG_VAL, &preserve_hfs_compression, 0, 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 },
-@@ -1902,6 +1913,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+@@ -1902,6 +1913,15 @@ int parse_arguments(int *argc_p, const char ***argv_p)
}
#endif
-+ if (preserve_hfs_compression) {
+#ifdef SUPPORT_HFS_COMPRESSION
++ if (preserve_hfs_compression) {
+ if (!preserve_xattrs)
+ preserve_xattrs = 1;
+ if (!preserve_fileflags)
+ preserve_fileflags = 1;
-+#else
-+ snprintf(err_buf,sizeof(err_buf),
-+ "HFS compression is not supported on this %s\n",
-+ am_server ? "server" : "client");
-+ return 0;
-+#endif
+ }
++#endif
+
if (write_batch && read_batch) {
snprintf(err_buf, sizeof err_buf,
"--write-batch and --read-batch can not be used together\n");
-@@ -2486,6 +2511,11 @@ void server_options(char **args, int *argc_p)
+@@ -2486,6 +2506,11 @@ void server_options(char **args, int *argc_p)
if (preserve_fileflags)
args[ac++] = "--fileflags";
--chmod=CHMOD affect file and/or directory permissions
-A, --acls preserve ACLs (implies -p)
-X, --xattrs preserve extended attributes
-+ --hfs-compression preserve HFS compression (if source & destination support it)
-+ --protect-decmpfs preserve HFS compression (regardless of volume support)
++ --hfs-compression preserve HFS compression if supported
++ --protect-decmpfs preserve HFS compression as xattrs
-o, --owner preserve owner (super-user only)
-g, --group preserve group
--devices preserve device files (super-user only)
-@@ -1087,6 +1089,48 @@ flags on files and directories that are being updated or deleted on the
+@@ -1087,6 +1089,42 @@ 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(--hfs-compression)) This option causes rsync to preserve HFS+
-+compression on filesystems that support it. Filesystem compression was
-+introduced to HFS+ in Mac OS 10.6. A file that is compressed has no data in
-+its data fork. Rather, the compressed data is stored in an extended attribute
-+named com.apple.decmpfs and a file flag is set to indicate that the file is
-+compressed (UF_COMPRESSED). HFS+ decompresses this data "on-the-fly" and
-+presents it to the operating system as a normal file. Normal attempts to copy
-+compressed files (e.g. in the Finder, via cp, ditto, etc.) will copy the
-+file's decompressed contents, remove the UF_COMPRESSED file flag, and discard
-+the com.apple.decmpfs extended attribute. This option will preserve the data
-+in the com.apple.decmpfs extended attribute and ignore the synthesized data
-+fork contents as long as both the source and destination filesystems support
-+HFS+ compression.
-+
-+If the source or destination filesystem does not support HFS+ compression,
-+this option will be disabled for both ends of the transfer. Compressed files
-+will be decompressed on the destination, data in the com.apple.decmpfs
-+extended attribute will be discarded, and the UF_COMPRESSED flag will not be
-+set. This option is appropriate if viewing the contents of compressed files is
-+required on operating systems that do not support HFS+ compression.
++compression if the destination filesystem supports it. If the destination
++does not support it, rsync will exit with an error.
++
++Filesystem compression was introduced to HFS+ in Mac OS 10.6. A file that is
++compressed has no data in its data fork. Rather, the compressed data is stored
++in an extended attribute named com.apple.decmpfs and a file flag is set to
++indicate that the file is compressed (UF_COMPRESSED). HFS+ decompresses this
++data "on-the-fly" and presents it to the operating system as a normal file.
++Normal attempts to copy compressed files (e.g. in the Finder, via cp, ditto,
++etc.) will copy the file's decompressed contents, remove the UF_COMPRESSED file
++flag, and discard the com.apple.decmpfs extended attribute. This option will
++preserve the data in the com.apple.decmpfs extended attribute and ignore the
++synthesized data in the file contents.
+
+This option implies both bf(--fileflags) and (--xattrs).
+
+or restoring the Mac OS X filesystem.
+
+This option will transfer the com.apple.decmpfs extended attribute regardless
-+of support on the source or destination. If a source file is compressed and an
-+existing file on the destination is not compressed, the data fork of the
-+destination file will be truncated and the com.apple.decmpfs xattr will be
-+transferred instead. Note that compressed files will not be readable to the
-+operating system of the destination if that operating system does not support
-+HFS+ compression. Once restored (with or without this option) to an operating
-+system that supports HFS+ compression, however, these files will be accessible
-+as usual.
++of support on the destination. If a source file is compressed and an existing
++file on the destination is not compressed, the data fork of the destination
++file will be truncated and the com.apple.decmpfs xattr will be transferred
++instead. Note that compressed files will not be readable to the operating
++system of the destination if that operating system does not support HFS+
++compression. Once restored (with or without this option) to an operating system
++that supports HFS+ compression, however, these files will be accessible as
++usual.
+
+This option implies bf(--fileflags) and bf(--xattrs).
+