Annotation of embedaddon/rsync/patches/hfs-compression.diff, revision 1.1
1.1 ! misho 1: This patch adds support for HFS+ compression.
! 2:
! 3: Written by Mike Bombich. Taken from http://www.bombich.com/rsync.html
! 4:
! 5: Modified by Wayne to fix some issues and tweak the implementation a bit.
! 6: This compiles on OS X and passes the testsuite, but otherwise UNTESTED!
! 7:
! 8: To use this patch, run these commands for a successful build:
! 9:
! 10: patch -p1 <patches/fileflags.diff
! 11: patch -p1 <patches/hfs-compression.diff
! 12: ./prepare-source
! 13: ./configure
! 14: make
! 15:
! 16: TODO:
! 17: - Should rsync try to treat the compressed data as file data and use the
! 18: rsync algorithm on the data transfer?
! 19:
! 20: based-on: patch/master/fileflags
! 21: diff --git a/flist.c b/flist.c
! 22: --- a/flist.c
! 23: +++ b/flist.c
! 24: @@ -1629,6 +1629,9 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
! 25: #ifdef SUPPORT_XATTRS
! 26: if (preserve_xattrs) {
! 27: sx.st.st_mode = file->mode;
! 28: + if (preserve_fileflags)
! 29: + sx.st.st_flags = F_FFLAGS(file);
! 30: + sx.st.st_mtime = file->modtime; /* get_xattr needs mtime for decmpfs xattrs */
! 31: if (get_xattr(fname, &sx) < 0) {
! 32: io_error |= IOERR_GENERAL;
! 33: return NULL;
! 34: diff --git a/generator.c b/generator.c
! 35: --- a/generator.c
! 36: +++ b/generator.c
! 37: @@ -37,6 +37,7 @@ extern int implied_dirs;
! 38: extern int keep_dirlinks;
! 39: extern int preserve_acls;
! 40: extern int preserve_xattrs;
! 41: +extern int preserve_hfs_compression;
! 42: extern int preserve_links;
! 43: extern int preserve_devices;
! 44: extern int write_devices;
! 45: @@ -1798,6 +1799,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
! 46: fname, fnamecmpbuf);
! 47: }
! 48: sx.st.st_size = F_LENGTH(fuzzy_file);
! 49: +#ifdef SUPPORT_HFS_COMPRESSION
! 50: + if (sx.st.st_flags & UF_COMPRESSED) {
! 51: + if (preserve_hfs_compression)
! 52: + sx.st.st_size = 0;
! 53: + else
! 54: + sx.st.st_flags &= ~UF_COMPRESSED;
! 55: + }
! 56: +#endif
! 57: statret = 0;
! 58: fnamecmp = fnamecmpbuf;
! 59: }
! 60: @@ -1965,6 +1974,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
! 61: if (read_batch)
! 62: goto cleanup;
! 63:
! 64: +#ifdef SUPPORT_HFS_COMPRESSION
! 65: + if (F_FFLAGS(file) & UF_COMPRESSED) {
! 66: + /* At this point the attrs have already been copied, we don't need to transfer a data fork
! 67: + * If my filesystem doesn't support HFS compression, the existing file's content
! 68: + * will not be automatically truncated, so we'll do that manually here */
! 69: + if (preserve_hfs_compression && sx.st.st_size > 0) {
! 70: + if (ftruncate(fd, 0) == 0)
! 71: + sx.st.st_size = 0;
! 72: + }
! 73: + }
! 74: +#endif
! 75: +
! 76: if (statret != 0 || whole_file)
! 77: write_sum_head(f_out, NULL);
! 78: else if (sx.st.st_size <= 0) {
! 79: diff --git a/lib/sysxattrs.c b/lib/sysxattrs.c
! 80: --- a/lib/sysxattrs.c
! 81: +++ b/lib/sysxattrs.c
! 82: @@ -22,10 +22,17 @@
! 83: #include "rsync.h"
! 84: #include "sysxattrs.h"
! 85:
! 86: +extern int preserve_hfs_compression;
! 87: +
! 88: #ifdef SUPPORT_XATTRS
! 89:
! 90: #ifdef HAVE_OSX_XATTRS
! 91: +#ifndef XATTR_SHOWCOMPRESSION
! 92: +#define XATTR_SHOWCOMPRESSION 0x0020
! 93: +#endif
! 94: #define GETXATTR_FETCH_LIMIT (64*1024*1024)
! 95: +
! 96: +int xattr_options = XATTR_NOFOLLOW;
! 97: #endif
! 98:
! 99: #if defined HAVE_LINUX_XATTRS
! 100: @@ -59,7 +66,12 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
! 101:
! 102: ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
! 103: {
! 104: - ssize_t len = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
! 105: + ssize_t len;
! 106: +
! 107: + if (preserve_hfs_compression)
! 108: + xattr_options |= XATTR_SHOWCOMPRESSION;
! 109: +
! 110: + len = getxattr(path, name, value, size, 0, xattr_options);
! 111:
! 112: /* If we're retrieving data, handle resource forks > 64MB specially */
! 113: if (value != NULL && len == GETXATTR_FETCH_LIMIT && (size_t)len < size) {
! 114: @@ -67,7 +79,7 @@ ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t si
! 115: u_int32_t offset = len;
! 116: size_t data_retrieved = len;
! 117: while (data_retrieved < size) {
! 118: - len = getxattr(path, name, value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
! 119: + len = getxattr(path, name, value + offset, size - data_retrieved, offset, xattr_options);
! 120: if (len <= 0)
! 121: break;
! 122: data_retrieved += len;
! 123: @@ -91,12 +103,16 @@ int sys_lsetxattr(const char *path, const char *name, const void *value, size_t
! 124:
! 125: int sys_lremovexattr(const char *path, const char *name)
! 126: {
! 127: - return removexattr(path, name, XATTR_NOFOLLOW);
! 128: + if (preserve_hfs_compression)
! 129: + xattr_options |= XATTR_SHOWCOMPRESSION;
! 130: + return removexattr(path, name, xattr_options);
! 131: }
! 132:
! 133: ssize_t sys_llistxattr(const char *path, char *list, size_t size)
! 134: {
! 135: - return listxattr(path, list, size, XATTR_NOFOLLOW);
! 136: + if (preserve_hfs_compression)
! 137: + xattr_options |= XATTR_SHOWCOMPRESSION;
! 138: + return listxattr(path, list, size, xattr_options);
! 139: }
! 140:
! 141: #elif HAVE_FREEBSD_XATTRS
! 142: diff --git a/main.c b/main.c
! 143: --- a/main.c
! 144: +++ b/main.c
! 145: @@ -34,6 +34,10 @@
! 146: #ifdef SUPPORT_FORCE_CHANGE
! 147: #include <sys/sysctl.h>
! 148: #endif
! 149: +#ifdef SUPPORT_HFS_COMPRESSION
! 150: +#include <sys/attr.h> /* For getattrlist() */
! 151: +#include <sys/mount.h> /* For statfs() */
! 152: +#endif
! 153:
! 154: extern int dry_run;
! 155: extern int list_only;
! 156: @@ -60,6 +64,7 @@ extern int copy_dirlinks;
! 157: extern int copy_unsafe_links;
! 158: extern int keep_dirlinks;
! 159: extern int preserve_hard_links;
! 160: +extern int preserve_hfs_compression;
! 161: extern int protocol_version;
! 162: extern int mkpath_dest_arg;
! 163: extern int file_total;
! 164: @@ -117,6 +122,7 @@ int daemon_connection = 0; /* 0 = no daemon, 1 = daemon via remote shell, -1 = d
! 165: mode_t orig_umask = 0;
! 166: int batch_gen_fd = -1;
! 167: int sender_keeps_checksum = 0;
! 168: +int fs_supports_hfs_compression = 0;
! 169: int raw_argc, cooked_argc;
! 170: char **raw_argv, **cooked_argv;
! 171:
! 172: @@ -671,6 +677,43 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
! 173: return pid;
! 174: }
! 175:
! 176: +#ifdef SUPPORT_HFS_COMPRESSION
! 177: +static void hfs_receiver_check(void)
! 178: +{
! 179: + struct statfs fsb;
! 180: + struct attrlist attrs;
! 181: + struct {
! 182: + int32_t len;
! 183: + vol_capabilities_set_t caps;
! 184: + } attrData;
! 185: +
! 186: + if (preserve_hfs_compression != 1)
! 187: + return; /* Nothing to check if --hfs-compression option isn't enabled. */
! 188: +
! 189: + if (statfs(".", &fsb) < 0) {
! 190: + rsyserr(FERROR, errno, "statfs %s failed", curr_dir);
! 191: + exit_cleanup(RERR_FILESELECT);
! 192: + }
! 193: +
! 194: + bzero(&attrs, sizeof attrs);
! 195: + attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
! 196: + attrs.volattr = ATTR_VOL_CAPABILITIES;
! 197: +
! 198: + bzero(&attrData, sizeof attrData);
! 199: + attrData.len = sizeof attrData;
! 200: +
! 201: + if (getattrlist(fsb.f_mntonname, &attrs, &attrData, sizeof attrData, 0) < 0) {
! 202: + rsyserr(FERROR, errno, "getattrlist %s failed", curr_dir);
! 203: + exit_cleanup(RERR_FILESELECT);
! 204: + }
! 205: +
! 206: + if (!(attrData.caps[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION)) {
! 207: + rprintf(FERROR, "The destination filesystem does not support HFS+ compression.\n");
! 208: + exit_cleanup(RERR_UNSUPPORTED);
! 209: + }
! 210: +}
! 211: +#endif
! 212: +
! 213: /* The receiving side operates in one of two modes:
! 214: *
! 215: * 1. it receives any number of files into a destination directory,
! 216: @@ -748,6 +791,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
! 217: exit_cleanup(RERR_FILESELECT);
! 218: }
! 219: filesystem_dev = st.st_dev; /* ensures --force works right w/-x */
! 220: +#ifdef SUPPORT_HFS_COMPRESSION
! 221: + hfs_receiver_check();
! 222: +#endif
! 223: return NULL;
! 224: }
! 225: if (file_total > 1) {
! 226: @@ -805,7 +851,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
! 227: full_fname(dest_path));
! 228: exit_cleanup(RERR_FILESELECT);
! 229: }
! 230: -
! 231: +#ifdef SUPPORT_HFS_COMPRESSION
! 232: + hfs_receiver_check();
! 233: +#endif
! 234: return NULL;
! 235: }
! 236:
! 237: @@ -825,6 +873,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
! 238: full_fname(dest_path));
! 239: exit_cleanup(RERR_FILESELECT);
! 240: }
! 241: +#ifdef SUPPORT_HFS_COMPRESSION
! 242: + hfs_receiver_check();
! 243: +#endif
! 244: *cp = '/';
! 245:
! 246: return cp + 1;
! 247: diff --git a/options.c b/options.c
! 248: --- a/options.c
! 249: +++ b/options.c
! 250: @@ -52,6 +52,7 @@ int preserve_links = 0;
! 251: int preserve_hard_links = 0;
! 252: int preserve_acls = 0;
! 253: int preserve_xattrs = 0;
! 254: +int preserve_hfs_compression = 0;
! 255: int preserve_perms = 0;
! 256: int preserve_fileflags = 0;
! 257: int preserve_executability = 0;
! 258: @@ -721,6 +722,10 @@ static struct poptOption long_options[] = {
! 259: {"no-force-change", 0, POPT_ARG_VAL, &force_change, 0, 0, 0 },
! 260: {"force-uchange", 0, POPT_ARG_VAL, &force_change, USR_IMMUTABLE, 0, 0 },
! 261: {"force-schange", 0, POPT_ARG_VAL, &force_change, SYS_IMMUTABLE, 0, 0 },
! 262: + {"hfs-compression", 0, POPT_ARG_VAL, &preserve_hfs_compression, 1, 0, 0 },
! 263: + {"no-hfs-compression",0, POPT_ARG_VAL, &preserve_hfs_compression, 0, 0, 0 },
! 264: + {"protect-decmpfs", 0, POPT_ARG_VAL, &preserve_hfs_compression, 2, 0, 0 },
! 265: + {"no-protect-decmpfs",0, POPT_ARG_VAL, &preserve_hfs_compression, 0, 0, 0 },
! 266: {"ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 1, 0, 0 },
! 267: {"no-ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 0, 0, 0 },
! 268: {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 },
! 269: @@ -1016,6 +1021,10 @@ static void set_refuse_options(void)
! 270: parse_one_refuse_match(0, "force-uchange", list_end);
! 271: parse_one_refuse_match(0, "force-schange", list_end);
! 272: #endif
! 273: +#ifndef SUPPORT_HFS_COMPRESSION
! 274: + parse_one_refuse_match(0, "hfs-compression", list_end);
! 275: + parse_one_refuse_match(0, "protect-decmpfs", list_end);
! 276: +#endif
! 277:
! 278: /* Now we use the descrip values to actually mark the options for refusal. */
! 279: for (op = long_options; op != list_end; op++) {
! 280: @@ -2070,6 +2079,15 @@ int parse_arguments(int *argc_p, const char ***argv_p)
! 281: }
! 282: #endif
! 283:
! 284: +#ifdef SUPPORT_HFS_COMPRESSION
! 285: + if (preserve_hfs_compression) {
! 286: + if (!preserve_xattrs)
! 287: + preserve_xattrs = 1;
! 288: + if (!preserve_fileflags)
! 289: + preserve_fileflags = 1;
! 290: + }
! 291: +#endif
! 292: +
! 293: if (write_batch && read_batch) {
! 294: snprintf(err_buf, sizeof err_buf,
! 295: "--write-batch and --read-batch can not be used together\n");
! 296: @@ -2667,6 +2685,11 @@ void server_options(char **args, int *argc_p)
! 297: if (preserve_fileflags)
! 298: args[ac++] = "--fileflags";
! 299:
! 300: +#ifdef SUPPORT_HFS_COMPRESSION
! 301: + if (preserve_hfs_compression)
! 302: + args[ac++] = preserve_hfs_compression == 1 ? "--hfs-compression" : "--protect-decmpfs";
! 303: +#endif
! 304: +
! 305: if (do_compression && do_compression_level != CLVL_NOT_SPECIFIED) {
! 306: if (asprintf(&arg, "--compress-level=%d", do_compression_level) < 0)
! 307: goto oom;
! 308: diff --git a/rsync.1.md b/rsync.1.md
! 309: --- a/rsync.1.md
! 310: +++ b/rsync.1.md
! 311: @@ -366,6 +366,8 @@ detailed description below for a complete description.
! 312: --chmod=CHMOD affect file and/or directory permissions
! 313: --acls, -A preserve ACLs (implies --perms)
! 314: --xattrs, -X preserve extended attributes
! 315: +--hfs-compression preserve HFS compression if supported
! 316: +--protect-decmpfs preserve HFS compression as xattrs
! 317: --owner, -o preserve owner (super-user only)
! 318: --group, -g preserve group
! 319: --devices preserve device files (super-user only)
! 320: @@ -1291,6 +1293,47 @@ your home directory (remove the '=' for that).
! 321: receiving side. It does not try to affect user flags. This option
! 322: overrides `--force-change` and `--force-uchange`.
! 323:
! 324: +0. `--hfs-compression`
! 325: +
! 326: + This option causes rsync to preserve HFS+ compression if the destination
! 327: + filesystem supports it. If the destination does not support it, rsync will
! 328: + exit with an error.
! 329: +
! 330: + Filesystem compression was introduced to HFS+ in Mac OS 10.6. A file that
! 331: + is compressed has no data in its data fork. Rather, the compressed data is
! 332: + stored in an extended attribute named com.apple.decmpfs and a file flag is
! 333: + set to indicate that the file is compressed (UF_COMPRESSED). HFS+
! 334: + decompresses this data "on-the-fly" and presents it to the operating system
! 335: + as a normal file. Normal attempts to copy compressed files (e.g. in the
! 336: + Finder, via cp, ditto, etc.) will copy the file's decompressed contents,
! 337: + remove the UF_COMPRESSED file flag, and discard the com.apple.decmpfs
! 338: + extended attribute. This option will preserve the data in the
! 339: + com.apple.decmpfs extended attribute and ignore the synthesized data in the
! 340: + file contents.
! 341: +
! 342: + This option implies both `--fileflags` and (--xattrs).
! 343: +
! 344: +0. `--protect-decmpfs`
! 345: +
! 346: + The com.apple.decmpfs extended attribute is hidden by default from list/get
! 347: + xattr calls, therefore normal attempts to copy compressed files will
! 348: + functionally decompress those files. While this is desirable behavior when
! 349: + copying files to filesystems that do not support HFS+ compression, it has
! 350: + serious performance and capacity impacts when backing up or restoring the
! 351: + Mac OS X filesystem.
! 352: +
! 353: + This option will transfer the com.apple.decmpfs extended attribute
! 354: + regardless of support on the destination. If a source file is compressed
! 355: + and an existing file on the destination is not compressed, the data fork of
! 356: + the destination file will be truncated and the com.apple.decmpfs xattr will
! 357: + be transferred instead. Note that compressed files will not be readable to
! 358: + the operating system of the destination if that operating system does not
! 359: + support HFS+ compression. Once restored (with or without this option) to an
! 360: + operating system that supports HFS+ compression, however, these files will
! 361: + be accessible as usual.
! 362: +
! 363: + This option implies `--fileflags` and `--xattrs`.
! 364: +
! 365: 0. `--chmod=CHMOD`
! 366:
! 367: This option tells rsync to apply one or more comma-separated "chmod" modes
! 368: diff --git a/rsync.c b/rsync.c
! 369: --- a/rsync.c
! 370: +++ b/rsync.c
! 371: @@ -606,8 +606,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
! 372: #ifdef SUPPORT_XATTRS
! 373: if (am_root < 0)
! 374: set_stat_xattr(fname, file, new_mode);
! 375: - if (preserve_xattrs && fnamecmp)
! 376: + if (preserve_xattrs && fnamecmp) {
! 377: + uint32 tmpflags = sxp->st.st_flags;
! 378: + sxp->st.st_flags = F_FFLAGS(file); /* set_xattr() needs to check UF_COMPRESSED */
! 379: set_xattr(fname, file, fnamecmp, sxp);
! 380: + sxp->st.st_flags = tmpflags;
! 381: + if (S_ISDIR(sxp->st.st_mode))
! 382: + link_stat(fname, &sx2.st, 0);
! 383: + }
! 384: #endif
! 385:
! 386: if (!preserve_times
! 387: @@ -621,7 +627,11 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
! 388: /* Don't set the creation date on the root folder of an HFS+ volume. */
! 389: if (sxp->st.st_ino == 2 && S_ISDIR(sxp->st.st_mode))
! 390: flags |= ATTRS_SKIP_CRTIME;
! 391: - if (!(flags & ATTRS_SKIP_MTIME) && !same_mtime(file, &sxp->st, flags & ATTRS_ACCURATE_TIME)) {
! 392: + if (!(flags & ATTRS_SKIP_MTIME)
! 393: +#ifdef SUPPORT_HFS_COMPRESSION
! 394: + && !(sxp->st.st_flags & UF_COMPRESSED) /* setting this alters mtime, so defer to after set_fileflags */
! 395: +#endif
! 396: + && !same_mtime(file, &sxp->st, flags & ATTRS_ACCURATE_TIME)) {
! 397: sx2.st.st_mtime = file->modtime;
! 398: #ifdef ST_MTIME_NSEC
! 399: sx2.st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file);
! 400: @@ -698,6 +708,16 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
! 401: && !set_fileflags(fname, fileflags))
! 402: goto cleanup;
! 403: updated = 1;
! 404: +#ifdef SUPPORT_HFS_COMPRESSION
! 405: + int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), new_mode, fileflags);
! 406: + if (ret < 0) {
! 407: + rsyserr(FERROR_XFER, errno, "failed to set times on %s",
! 408: + full_fname(fname));
! 409: + goto cleanup;
! 410: + }
! 411: + if (ret != 0)
! 412: + file->flags |= FLAG_TIME_FAILED;
! 413: +#endif
! 414: }
! 415: #endif
! 416:
! 417: diff --git a/rsync.h b/rsync.h
! 418: --- a/rsync.h
! 419: +++ b/rsync.h
! 420: @@ -572,6 +572,17 @@ typedef unsigned int size_t;
! 421: #endif
! 422: #endif
! 423:
! 424: +#ifndef UF_COMPRESSED
! 425: +#define UF_COMPRESSED 0x00000020
! 426: +#endif
! 427: +#ifndef VOL_CAP_FMT_DECMPFS_COMPRESSION
! 428: +#define VOL_CAP_FMT_DECMPFS_COMPRESSION 0x00010000
! 429: +#endif
! 430: +
! 431: +#if defined SUPPORT_XATTRS && defined SUPPORT_FILEFLAGS
! 432: +#define SUPPORT_HFS_COMPRESSION 1
! 433: +#endif
! 434: +
! 435: #ifndef __APPLE__ /* Do we need a configure check for this? */
! 436: #define SUPPORT_ATIMES 1
! 437: #endif
! 438: diff --git a/t_stub.c b/t_stub.c
! 439: --- a/t_stub.c
! 440: +++ b/t_stub.c
! 441: @@ -34,6 +34,7 @@ int preserve_times = 0;
! 442: int preserve_xattrs = 0;
! 443: int preserve_perms = 0;
! 444: int preserve_executability = 0;
! 445: +int preserve_hfs_compression = 0;
! 446: int open_noatime = 0;
! 447: size_t max_alloc = 0; /* max_alloc is needed when combined with util2.o */
! 448: char *partial_dir;
! 449: diff --git a/xattrs.c b/xattrs.c
! 450: --- a/xattrs.c
! 451: +++ b/xattrs.c
! 452: @@ -33,6 +33,7 @@ extern int am_generator;
! 453: extern int read_only;
! 454: extern int list_only;
! 455: extern int preserve_xattrs;
! 456: +extern int preserve_hfs_compression;
! 457: extern int preserve_links;
! 458: extern int preserve_devices;
! 459: extern int preserve_specials;
! 460: @@ -42,6 +43,10 @@ extern int saw_xattr_filter;
! 461: #define RSYNC_XAL_INITIAL 5
! 462: #define RSYNC_XAL_LIST_INITIAL 100
! 463:
! 464: +#define GXD_NO_MISSING_ERROR (1<<0)
! 465: +#define GXD_OMIT_COMPRESSED (1<<1)
! 466: +#define GXD_FILE_IS_COMPRESSED (1<<2)
! 467: +
! 468: #define MAX_FULL_DATUM 32
! 469:
! 470: #define HAS_PREFIX(str, prfx) (*(str) == *(prfx) && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
! 471: @@ -73,6 +78,17 @@ extern int saw_xattr_filter;
! 472: #define XDEF_ACL_SUFFIX "dacl"
! 473: #define XDEF_ACL_ATTR RSYNC_PREFIX "%" XDEF_ACL_SUFFIX
! 474:
! 475: +#define APPLE_PREFIX "com.apple."
! 476: +#define APLPRE_LEN ((int)sizeof APPLE_PREFIX - 1)
! 477: +#define DECMPFS_SUFFIX "decmpfs"
! 478: +#define RESOURCEFORK_SUFFIX "ResourceFork"
! 479: +
! 480: +#define UNREAD_DATA ((char *)1)
! 481: +
! 482: +#if MAX_DIGEST_LEN < SIZEOF_TIME_T
! 483: +#error MAX_DIGEST_LEN is too small to hold an mtime
! 484: +#endif
! 485: +
! 486: typedef struct {
! 487: char *datum, *name;
! 488: size_t datum_len, name_len;
! 489: @@ -180,7 +196,7 @@ static ssize_t get_xattr_names(const char *fname)
! 490: /* On entry, the *len_ptr parameter contains the size of the extra space we
! 491: * should allocate when we create a buffer for the data. On exit, it contains
! 492: * the length of the datum. */
! 493: -static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr, int no_missing_error)
! 494: +static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr, int flags)
! 495: {
! 496: size_t datum_len = sys_lgetxattr(fname, name, NULL, 0);
! 497: size_t extra_len = *len_ptr;
! 498: @@ -189,7 +205,7 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
! 499: *len_ptr = datum_len;
! 500:
! 501: if (datum_len == (size_t)-1) {
! 502: - if (errno == ENOTSUP || no_missing_error)
! 503: + if (errno == ENOTSUP || flags & GXD_NO_MISSING_ERROR)
! 504: return NULL;
! 505: rsyserr(FERROR_XFER, errno,
! 506: "get_xattr_data: lgetxattr(%s,\"%s\",0) failed",
! 507: @@ -197,6 +213,15 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
! 508: return NULL;
! 509: }
! 510:
! 511: + if (flags & GXD_OMIT_COMPRESSED && datum_len > MAX_FULL_DATUM
! 512: + && HAS_PREFIX(name, APPLE_PREFIX)
! 513: + && (strcmp(name+APLPRE_LEN, DECMPFS_SUFFIX) == 0
! 514: + || (flags & GXD_FILE_IS_COMPRESSED && strcmp(name+APLPRE_LEN, RESOURCEFORK_SUFFIX) == 0))) {
! 515: + /* If we are omitting compress-file-related data, we don't want to
! 516: + * actually read this data. */
! 517: + return UNREAD_DATA;
! 518: + }
! 519: +
! 520: if (!datum_len && !extra_len)
! 521: extra_len = 1; /* request non-zero amount of memory */
! 522: if (SIZE_MAX - datum_len < extra_len)
! 523: @@ -224,7 +249,31 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
! 524: return ptr;
! 525: }
! 526:
! 527: -static int rsync_xal_get(const char *fname, item_list *xalp)
! 528: +static void checksum_xattr_data(char *sum, const char *datum, size_t datum_len, stat_x *sxp)
! 529: +{
! 530: + if (datum == UNREAD_DATA) {
! 531: + /* For abbreviated compressed data, we store the file's mtime as the checksum. */
! 532: + SIVAL(sum, 0, sxp->st.st_mtime);
! 533: +#if SIZEOF_TIME_T > 4
! 534: + SIVAL(sum, 4, sxp->st.st_mtime >> 32);
! 535: +#if MAX_DIGEST_LEN > 8
! 536: + memset(sum + 8, 0, MAX_DIGEST_LEN - 8);
! 537: +#endif
! 538: +#else
! 539: +#if MAX_DIGEST_LEN > 4
! 540: + memset(sum + 4, 0, MAX_DIGEST_LEN - 4);
! 541: +#endif
! 542: +#endif
! 543: + } else {
! 544: + sum_init(-1, checksum_seed);
! 545: + sum_update(datum, datum_len);
! 546: + sum_end(sum);
! 547: + }
! 548: +}
! 549: +
! 550: +$$$ERROR$$$ the old patch needs reworking since rsync_xal_get() has totally changed!
! 551: +
! 552: +static int rsync_xal_get(const char *fname, stat_x *sxp)
! 553: {
! 554: ssize_t list_len, name_len;
! 555: size_t datum_len, name_offset;
! 556: @@ -233,7 +282,8 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
! 557: int user_only = am_sender ? 0 : !am_root;
! 558: #endif
! 559: rsync_xa *rxa;
! 560: - int count;
! 561: + int count, flags;
! 562: + item_list *xalp = sxp->xattr;
! 563:
! 564: /* This puts the name list into the "namebuf" buffer. */
! 565: if ((list_len = get_xattr_names(fname)) < 0)
! 566: @@ -264,11 +314,15 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
! 567: }
! 568:
! 569: datum_len = name_len; /* Pass extra size to get_xattr_data() */
! 570: - if (!(ptr = get_xattr_data(fname, name, &datum_len, 0)))
! 571: + flags = GXD_OMIT_COMPRESSED;
! 572: + if (preserve_hfs_compression && sxp->st.st_flags & UF_COMPRESSED)
! 573: + flags |= GXD_FILE_IS_COMPRESSED;
! 574: + if (!(ptr = get_xattr_data(fname, name, &datum_len, flags)))
! 575: return -1;
! 576:
! 577: if (datum_len > MAX_FULL_DATUM) {
! 578: /* For large datums, we store a flag and a checksum. */
! 579: + char *datum = ptr;
! 580: name_offset = 1 + MAX_DIGEST_LEN;
! 581: sum_init(-1, checksum_seed);
! 582: sum_update(ptr, datum_len);
! 583: @@ -276,7 +330,9 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
! 584:
! 585: ptr = new_array(char, name_offset + name_len);
! 586: *ptr = XSTATE_ABBREV;
! 587: - sum_end(ptr + 1);
! 588: + checksum_xattr_data(ptr+1, datum, datum_len, sxp);
! 589: + if (datum != UNREAD_DATA)
! 590: + free(datum);
! 591: } else
! 592: name_offset = datum_len;
! 593:
! 594: @@ -322,7 +378,7 @@ int get_xattr(const char *fname, stat_x *sxp)
! 595: } else if (IS_MISSING_FILE(sxp->st))
! 596: return 0;
! 597:
! 598: - if (rsync_xal_get(fname, sxp->xattr) < 0) {
! 599: + if (rsync_xal_get(fname, sxp) < 0) {
! 600: free_xattr(sxp);
! 601: return -1;
! 602: }
! 603: @@ -359,6 +415,8 @@ int copy_xattrs(const char *source, const char *dest)
! 604: datum_len = 0;
! 605: if (!(ptr = get_xattr_data(source, name, &datum_len, 0)))
! 606: return -1;
! 607: + if (ptr == UNREAD_DATA)
! 608: + continue; /* XXX Is this right? */
! 609: if (sys_lsetxattr(dest, name, ptr, datum_len) < 0) {
! 610: int save_errno = errno ? errno : EINVAL;
! 611: rsyserr(FERROR_XFER, errno,
! 612: @@ -449,6 +507,7 @@ static int find_matching_xattr(const item_list *xalp)
! 613: }
! 614:
! 615: return -1;
! 616: +#endif
! 617: }
! 618:
! 619: /* Store *xalp on the end of rsync_xal_l */
! 620: @@ -663,11 +722,13 @@ void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
! 621:
! 622: /* Re-read the long datum. */
! 623: if (!(ptr = get_xattr_data(fname, rxa->name, &len, 0))) {
! 624: - rprintf(FERROR_XFER, "failed to re-read xattr %s for %s\n", rxa->name, fname);
! 625: + if (errno != ENOTSUP && errno != ENOATTR)
! 626: + rprintf(FERROR_XFER, "failed to re-read xattr %s for %s\n", rxa->name, fname);
! 627: write_varint(f_out, 0);
! 628: continue;
! 629: }
! 630:
! 631: + assert(ptr != UNREAD_DATA);
! 632: write_varint(f_out, len); /* length might have changed! */
! 633: write_bigbuf(f_out, ptr, len);
! 634: free(ptr);
! 635: @@ -948,7 +1009,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
! 636: int user_only = am_root <= 0;
! 637: #endif
! 638: size_t name_len;
! 639: - int ret = 0;
! 640: + int flags, ret = 0;
! 641:
! 642: /* This puts the current name list into the "namebuf" buffer. */
! 643: if ((list_len = get_xattr_names(fname)) < 0)
! 644: @@ -961,7 +1022,10 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
! 645: int sum_len;
! 646: /* See if the fnamecmp version is identical. */
! 647: len = name_len = rxas[i].name_len;
! 648: - if ((ptr = get_xattr_data(fnamecmp, name, &len, 1)) == NULL) {
! 649: + flags = GXD_OMIT_COMPRESSED | GXD_NO_MISSING_ERROR;
! 650: + if (preserve_hfs_compression && sxp->st.st_flags & UF_COMPRESSED)
! 651: + flags |= GXD_FILE_IS_COMPRESSED;
! 652: + if ((ptr = get_xattr_data(fnamecmp, name, &len, flags)) == NULL) {
! 653: still_abbrev:
! 654: if (am_generator)
! 655: continue;
! 656: @@ -970,6 +1034,8 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
! 657: ret = -1;
! 658: continue;
! 659: }
! 660: + if (ptr == UNREAD_DATA)
! 661: + continue; /* XXX Is this right? */
! 662: if (len != rxas[i].datum_len) {
! 663: free(ptr);
! 664: goto still_abbrev;
! 665: @@ -1047,6 +1113,10 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
! 666: }
! 667: }
! 668:
! 669: +#ifdef HAVE_OSX_XATTRS
! 670: + rsync_xal_free(xalp); /* Free this because we aren't using find_matching_xattr(). */
! 671: +#endif
! 672: +
! 673: return ret;
! 674: }
! 675:
! 676: @@ -1095,7 +1165,7 @@ char *get_xattr_acl(const char *fname, int is_access_acl, size_t *len_p)
! 677: {
! 678: const char *name = is_access_acl ? XACC_ACL_ATTR : XDEF_ACL_ATTR;
! 679: *len_p = 0; /* no extra data alloc needed from get_xattr_data() */
! 680: - return get_xattr_data(fname, name, len_p, 1);
! 681: + return get_xattr_data(fname, name, len_p, GXD_NO_MISSING_ERROR);
! 682: }
! 683:
! 684: int set_xattr_acl(const char *fname, int is_access_acl, const char *buf, size_t buf_len)
! 685: @@ -1238,11 +1308,33 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
! 686: return 0;
! 687: }
! 688:
! 689: +#ifdef SUPPORT_HFS_COMPRESSION
! 690: +static inline void hfs_compress_tweaks(STRUCT_STAT *fst)
! 691: +{
! 692: + if (fst->st_flags & UF_COMPRESSED) {
! 693: + if (preserve_hfs_compression) {
! 694: + /* We're sending the compression xattr, not the decompressed data fork.
! 695: + * Setting rsync's idea of the file size to 0 effectively prevents the
! 696: + * transfer of the data fork. */
! 697: + fst->st_size = 0;
! 698: + } else {
! 699: + /* If the sender's filesystem supports compression, then we'll be able
! 700: + * to send the decompressed data fork and the decmpfs xattr will be
! 701: + * hidden (not sent). As such, we need to strip the compression flag. */
! 702: + fst->st_flags &= ~UF_COMPRESSED;
! 703: + }
! 704: + }
! 705: +}
! 706: +#endif
! 707: +
! 708: int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
! 709: {
! 710: int ret = do_stat(fname, fst);
! 711: if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
! 712: xst->st_mode = 0;
! 713: +#ifdef SUPPORT_HFS_COMPRESSION
! 714: + hfs_compress_tweaks(fst);
! 715: +#endif
! 716: return ret;
! 717: }
! 718:
! 719: @@ -1251,6 +1343,9 @@ int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
! 720: int ret = do_lstat(fname, fst);
! 721: if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
! 722: xst->st_mode = 0;
! 723: +#ifdef SUPPORT_HFS_COMPRESSION
! 724: + hfs_compress_tweaks(fst);
! 725: +#endif
! 726: return ret;
! 727: }
! 728:
! 729: @@ -1259,6 +1354,9 @@ int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
! 730: int ret = do_fstat(fd, fst);
! 731: if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
! 732: xst->st_mode = 0;
! 733: +#ifdef SUPPORT_HFS_COMPRESSION
! 734: + hfs_compress_tweaks(fst);
! 735: +#endif
! 736: return ret;
! 737: }
! 738:
! 739: diff -Nurp a/rsync.1 b/rsync.1
! 740: --- a/rsync.1
! 741: +++ b/rsync.1
! 742: @@ -442,6 +442,8 @@ detailed description below for a complet
! 743: --chmod=CHMOD affect file and/or directory permissions
! 744: --acls, -A preserve ACLs (implies --perms)
! 745: --xattrs, -X preserve extended attributes
! 746: +--hfs-compression preserve HFS compression if supported
! 747: +--protect-decmpfs preserve HFS compression as xattrs
! 748: --owner, -o preserve owner (super-user only)
! 749: --group, -g preserve group
! 750: --devices preserve device files (super-user only)
! 751: @@ -1373,6 +1375,43 @@ side. It does not try to affect system
! 752: flags on files and directories that are being updated or deleted on the
! 753: receiving side. It does not try to affect user flags. This option
! 754: overrides \fB\-\-force-change\fP and \fB\-\-force-uchange\fP."
! 755: +.IP "\fB\-\-hfs-compression\fP"
! 756: +This option causes rsync to preserve HFS+ compression if the destination
! 757: +filesystem supports it. If the destination does not support it, rsync will
! 758: +exit with an error.
! 759: +.IP
! 760: +Filesystem compression was introduced to HFS+ in Mac OS 10.6. A file that
! 761: +is compressed has no data in its data fork. Rather, the compressed data is
! 762: +stored in an extended attribute named com.apple.decmpfs and a file flag is
! 763: +set to indicate that the file is compressed (UF_COMPRESSED). HFS+
! 764: +decompresses this data "on-the-fly" and presents it to the operating system
! 765: +as a normal file. Normal attempts to copy compressed files (e.g. in the
! 766: +Finder, via cp, ditto, etc.) will copy the file's decompressed contents,
! 767: +remove the UF_COMPRESSED file flag, and discard the com.apple.decmpfs
! 768: +extended attribute. This option will preserve the data in the
! 769: +com.apple.decmpfs extended attribute and ignore the synthesized data in the
! 770: +file contents.
! 771: +.IP
! 772: +This option implies both \fB\-\-fileflags\fP and (\-\-xattrs).
! 773: +.IP "\fB\-\-protect-decmpfs\fP"
! 774: +The com.apple.decmpfs extended attribute is hidden by default from list/get
! 775: +xattr calls, therefore normal attempts to copy compressed files will
! 776: +functionally decompress those files. While this is desirable behavior when
! 777: +copying files to filesystems that do not support HFS+ compression, it has
! 778: +serious performance and capacity impacts when backing up or restoring the
! 779: +Mac OS X filesystem.
! 780: +.IP
! 781: +This option will transfer the com.apple.decmpfs extended attribute
! 782: +regardless of support on the destination. If a source file is compressed
! 783: +and an existing file on the destination is not compressed, the data fork of
! 784: +the destination file will be truncated and the com.apple.decmpfs xattr will
! 785: +be transferred instead. Note that compressed files will not be readable to
! 786: +the operating system of the destination if that operating system does not
! 787: +support HFS+ compression. Once restored (with or without this option) to an
! 788: +operating system that supports HFS+ compression, however, these files will
! 789: +be accessible as usual.
! 790: +.IP
! 791: +This option implies \fB\-\-fileflags\fP and \fB\-\-xattrs\fP.
! 792: .IP "\fB\-\-chmod=CHMOD\fP"
! 793: This option tells rsync to apply one or more comma-separated "chmod" modes
! 794: to the permission of the files in the transfer. The resulting value is
! 795: diff -Nurp a/rsync.1.html b/rsync.1.html
! 796: --- a/rsync.1.html
! 797: +++ b/rsync.1.html
! 798: @@ -357,6 +357,8 @@ detailed description below for a complet
! 799: --chmod=CHMOD affect file and/or directory permissions
! 800: --acls, -A preserve ACLs (implies --perms)
! 801: --xattrs, -X preserve extended attributes
! 802: +--hfs-compression preserve HFS compression if supported
! 803: +--protect-decmpfs preserve HFS compression as xattrs
! 804: --owner, -o preserve owner (super-user only)
! 805: --group, -g preserve group
! 806: --devices preserve device files (super-user only)
! 807: @@ -1252,6 +1254,43 @@ receiving side. It does not try to affe
! 808: overrides <code>--force-change</code> and <code>--force-uchange</code>.</dt><dd>
! 809: </dd>
! 810:
! 811: +<dt><code>--hfs-compression</code></dt><dd>
! 812: +<p>This option causes rsync to preserve HFS+ compression if the destination
! 813: +filesystem supports it. If the destination does not support it, rsync will
! 814: +exit with an error.</p>
! 815: +<p>Filesystem compression was introduced to HFS+ in Mac OS 10.6. A file that
! 816: +is compressed has no data in its data fork. Rather, the compressed data is
! 817: +stored in an extended attribute named com.apple.decmpfs and a file flag is
! 818: +set to indicate that the file is compressed (UF_COMPRESSED). HFS+
! 819: +decompresses this data "on-the-fly" and presents it to the operating system
! 820: +as a normal file. Normal attempts to copy compressed files (e.g. in the
! 821: +Finder, via cp, ditto, etc.) will copy the file's decompressed contents,
! 822: +remove the UF_COMPRESSED file flag, and discard the com.apple.decmpfs
! 823: +extended attribute. This option will preserve the data in the
! 824: +com.apple.decmpfs extended attribute and ignore the synthesized data in the
! 825: +file contents.</p>
! 826: +<p>This option implies both <code>--fileflags</code> and (-⁠-⁠xattrs).</p>
! 827: +</dd>
! 828: +
! 829: +<dt><code>--protect-decmpfs</code></dt><dd>
! 830: +<p>The com.apple.decmpfs extended attribute is hidden by default from list/get
! 831: +xattr calls, therefore normal attempts to copy compressed files will
! 832: +functionally decompress those files. While this is desirable behavior when
! 833: +copying files to filesystems that do not support HFS+ compression, it has
! 834: +serious performance and capacity impacts when backing up or restoring the
! 835: +Mac OS X filesystem.</p>
! 836: +<p>This option will transfer the com.apple.decmpfs extended attribute
! 837: +regardless of support on the destination. If a source file is compressed
! 838: +and an existing file on the destination is not compressed, the data fork of
! 839: +the destination file will be truncated and the com.apple.decmpfs xattr will
! 840: +be transferred instead. Note that compressed files will not be readable to
! 841: +the operating system of the destination if that operating system does not
! 842: +support HFS+ compression. Once restored (with or without this option) to an
! 843: +operating system that supports HFS+ compression, however, these files will
! 844: +be accessible as usual.</p>
! 845: +<p>This option implies <code>--fileflags</code> and <code>--xattrs</code>.</p>
! 846: +</dd>
! 847: +
! 848: <dt><code>--chmod=CHMOD</code></dt><dd>
! 849: <p>This option tells rsync to apply one or more comma-separated "chmod" modes
! 850: to the permission of the files in the transfer. The resulting value is
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>