Annotation of embedaddon/rsync/patches/hfs-compression.diff, revision 1.1.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 &quot;on-the-fly&quot; 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 (-&#8288;-&#8288;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 &quot;chmod&quot; modes
                    850:  to the permission of the files in the transfer.  The resulting value is

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>