]> git.saurik.com Git - apple/copyfile.git/commitdiff
copyfile-166.tar.gz macos-1015 v166
authorApple <opensource@apple.com>
Thu, 25 Apr 2019 18:09:42 +0000 (18:09 +0000)
committerApple <opensource@apple.com>
Thu, 25 Apr 2019 18:09:42 +0000 (18:09 +0000)
17 files changed:
.gitignore [new file with mode: 0644]
copyfile.3
copyfile.c
copyfile.h
copyfile.xcodeproj/project.pbxproj
copyfile_private.h
copyfile_test/copyfile_test.entitlements [new file with mode: 0644]
copyfile_test/main.c
copyfile_test/sparse_test.c
copyfile_test/sparse_test.h
copyfile_test/stat_test.c [new file with mode: 0644]
copyfile_test/stat_test.h [new file with mode: 0644]
copyfile_test/test_utils.c
copyfile_test/test_utils.h
xattr_flags.h
xattr_name_with_flags.3
xattr_properties.h

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..909d75d
--- /dev/null
@@ -0,0 +1,4 @@
+copyfile.xcodeproj/project.xcworkspace
+copyfile.xcodeproj/xcuserdata/
+.DS_Store
+build/
index ceb898b037909a03d071563293ef4c2a92d5e05d..be1aa318e5749d28f7f5e2a53fd83797c890c31a 100644 (file)
@@ -1,7 +1,7 @@
 .\"
 .\" Copyright (c) 2002 Apple Computer, Inc.  All rights reserved.
 .\"
-.Dd August 30, 2017
+.Dd November 2, 2017
 .Dt COPYFILE 3
 .Os
 .Sh NAME
@@ -185,7 +185,8 @@ This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYF
 | COPYFILE_NOFOLLOW_SRC).
 Note that if cloning is successful, progress callbacks will not be invoked.
 Note also that there is no support for cloning directories: if a directory is provided as the source,
-an error will be returned.
+an error will be returned. Since this flag implies COPYFILE_NOFOLLOW_SRC, symbolic links themselves will
+be cloned instead of their targets.
 (This is only applicable for the
 .Fn copyfile
 function.)
@@ -196,7 +197,8 @@ This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYF
 | COPYFILE_NOFOLLOW_SRC).
 Note that if cloning is successful, progress callbacks will not be invoked.
 Note also that there is no support for cloning directories: if a directory is provided as the source and
-COPYFILE_CLONE_FORCE is not passed, this will instead copy the directory. Recursive copying however is
+COPYFILE_CLONE_FORCE is not passed, this will instead copy the directory. Since this flag implies COPYFILE_NOFOLLOW_SRC,
+symbolic links themselves will be cloned instead of their targets. Recursive copying however is
 supported, see below for more information.
 (This is only applicable for the
 .Fn copyfile
@@ -217,6 +219,18 @@ This is a convenience macro, equivalent to
 If the src file has quarantine information, add the QTN_FLAG_DO_NOT_TRANSLOCATE flag to the quarantine information of the dst file. This allows a bundle to run in place instead of being translocated.
 .El
 .Pp
+Copying files into a directory is supported. If
+.Va to
+is a directory,
+.Va from
+will be copied into
+.Va to
+(if
+.Va from
+is a directory,
+copying its contents requires use of the COPYFILE_RECURSIVE parameter,
+which is documented below).
+.Pp
 The
 .Fn copyfile_state_get
 and
@@ -478,6 +492,19 @@ and
 .Dv COPYFILE_UNLINK
 flags are not used during a recursive copy, and will result
 in an error being returned.
+.Pp
+Note that if the source path ends in a
+.Va /
+its contents are copied rather than the directory itself (like cp(1)).
+The behavior of a recursive copy on a directory hierarchy also depends
+on the contents of the destination. If the destination is a directory,
+the source directory (or its contents, if the source path ends in a
+.Va /
+) will be copied into it. If the destination exists but is not a
+directory, and the source is a non-empty directory, the copy will fail;
+the exact error set depends on the flags provided to
+.Fn copyfile
+initially.
 .Sh Progress Callback
 In addition to the recursive callbacks described above,
 .Fn copyfile
index 99e6d42b10ad2ea43d14c20d3d677c7d9c085f10..286d0ff8584649e037380c798225d5b937766bb5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2010 Apple, Inc. All rights reserved.
+ * Copyright (c) 2004-2019 Apple, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
 #define qtn_file_t void *
 #define QTN_SERIALIZED_DATA_MAX 0
 static void * qtn_file_alloc(void) { return NULL; }
-static int qtn_file_init_with_fd(void *x, int y) { return -1; }
-static int qtn_file_init_with_path(void *x, const char *path) { return -1; }
-static int qtn_file_init_with_data(void *x, const void *data, size_t len) { return -1; }
-static void qtn_file_free(void *x) { return; }
-static int qtn_file_apply_to_fd(void *x, int y) { return 0; }
-static char *qtn_error(int x) { return NULL; }
-static int qtn_file_to_data(void *x, char *y, size_t z) { return -1; }
-static void *qtn_file_clone(void *x) { return NULL; }
-static uint32_t qtn_file_get_flags(void *x) { return 0; }
-static int qtn_file_set_flags(void *x, uint32_t flags) { return 0; }
+static int qtn_file_init_with_fd(__unused void *x, __unused int y) { return -1; }
+static int qtn_file_init_with_path(__unused void *x, __unused const char *path) { return -1; }
+static int qtn_file_init_with_data(__unused void *x, __unused const void *data, __unused size_t len) { return -1; }
+static void qtn_file_free(__unused void *x) { return; }
+static int qtn_file_apply_to_fd(__unused void *x, __unused int y) { return 0; }
+static char *qtn_error(__unused int x) { return NULL; }
+static int qtn_file_to_data(__unused void *x, __unused char *y, __unused size_t *z) { return -1; }
+static void *qtn_file_clone(__unused void *x) { return NULL; }
+static uint32_t qtn_file_get_flags(__unused void *x) { return 0; }
+static int qtn_file_set_flags(__unused void *x, __unused uint32_t flags) { return 0; }
 #define        XATTR_QUARANTINE_NAME "figgledidiggledy"
 #define QTN_FLAG_DO_NOT_TRANSLOCATE 0
 #endif /* TARGET_OS_IPHONE */
@@ -237,7 +237,7 @@ sort_xattrname_list(void *start, size_t length)
 
        tmp = ptrs[indx++] = (char*)start;
 
-       while (tmp = memchr(tmp, 0, ((char*)start + length) - tmp)) {
+       while ((tmp = memchr(tmp, 0, ((char*)start + length) - tmp))) {
                if (indx == nel) {
                        nel += 10;
                        ptrs = realloc(ptrs, sizeof(char**) * nel);
@@ -645,7 +645,7 @@ copytree(copyfile_state_t s)
                retval = -1;
                goto done;
        }
-       if (s->flags & (COPYFILE_MOVE | COPYFILE_UNLINK | COPYFILE_CHECK | COPYFILE_PACK | COPYFILE_UNPACK)) {
+       if (s->flags & (COPYFILE_MOVE | COPYFILE_UNLINK | COPYFILE_CHECK | COPYFILE_PACK | COPYFILE_UNPACK | COPYFILE_CLONE_FORCE)) {
                errno = EINVAL;
                retval = -1;
                goto done;
@@ -2701,6 +2701,8 @@ error_exit:
 /*
  * Attempt to set the destination file's stat information -- including
  * flags and time-related fields -- to the source's.
+ * Note that we must set file flags *last*, as setting a flag like
+ * UF_IMMUTABLE can prevent us from setting other attributes.
  */
 static int copyfile_stat(copyfile_state_t s)
 {
@@ -2713,6 +2715,20 @@ static int copyfile_stat(copyfile_state_t s)
                struct timespec acc_time;
        } ma_times;
 
+       /* Try to set m/atimes using setattrlist(), for nanosecond precision. */
+       memset(&attrlist, 0, sizeof(attrlist));
+       attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
+       attrlist.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
+       ma_times.mod_time = s->sb.st_mtimespec;
+       ma_times.acc_time = s->sb.st_atimespec;
+       (void)fsetattrlist(s->dst_fd, &attrlist, &ma_times, sizeof(ma_times), 0);
+
+       /* If this fails, we don't care */
+       (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
+
+       /* This may have already been done in copyfile_security() */
+       (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT);
+
        /*
         * NFS doesn't support chflags; ignore errors as a result, since
         * we don't return failure for this.
@@ -2721,32 +2737,17 @@ static int copyfile_stat(copyfile_state_t s)
                added_flags |= UF_HIDDEN;
 
        /*
-        * We need to check if SF_RESTRICTED was set on the destination
-        * by the kernel.  If it was, don't drop it.
+        * We need to check if certain flags were set on the destination
+        * by the kernel.  If they were, don't drop them.
         */
        if (fstat(s->dst_fd, &dst_sb))
                return -1;
-       if (dst_sb.st_flags & SF_RESTRICTED)
-               added_flags |= SF_RESTRICTED;
+       added_flags |= (dst_sb.st_flags & COPYFILE_PRESERVE_FLAGS);
 
        /* Copy file flags, masking out any we don't want to preserve */
        dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags;
        (void)fchflags(s->dst_fd, dst_flags);
 
-       /* If this fails, we don't care */
-       (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
-
-       /* This may have already been done in copyfile_security() */
-       (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT);
-
-       /* Try to set m/atimes using setattrlist(), for nanosecond precision. */
-       memset(&attrlist, 0, sizeof(attrlist));
-       attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
-       attrlist.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
-       ma_times.mod_time = s->sb.st_mtimespec;
-       ma_times.acc_time = s->sb.st_atimespec;
-       (void)fsetattrlist(s->dst_fd, &attrlist, &ma_times, sizeof(ma_times), 0);
-
        return 0;
 }
 
@@ -3003,7 +3004,7 @@ static int copyfile_xattr(copyfile_state_t s)
                        {
                                errno = error;
                                ret = -1;
-                               copyfile_warn("could not set attributes %s on destination file descriptor: %s", name, strerror(error));
+                               copyfile_warn("could not set attributes %s on destination file descriptor", name);
                                continue;
                        }
                }
@@ -3252,9 +3253,6 @@ int main(int c, char *v[])
  * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
  */
 
-
-#define offsetof(type, member) __builtin_offsetof(type, member)
-
 #define        XATTR_MAXATTRLEN   (16*1024*1024)
 
 
@@ -4425,10 +4423,10 @@ static int copyfile_pack(copyfile_state_t s)
        filehdr->appledouble.version            = ADH_VERSION;
        filehdr->appledouble.numEntries         = 2;
        filehdr->appledouble.entries[0].type    = AD_FINDERINFO;
-       filehdr->appledouble.entries[0].offset  = (u_int32_t)offsetof(apple_double_header_t, finfo);
+       filehdr->appledouble.entries[0].offset  = (u_int32_t)__builtin_offsetof(apple_double_header_t, finfo);
        filehdr->appledouble.entries[0].length  = FINDERINFOSIZE;
        filehdr->appledouble.entries[1].type    = AD_RESOURCE;
-       filehdr->appledouble.entries[1].offset  = (u_int32_t)offsetof(apple_double_header_t, pad);
+       filehdr->appledouble.entries[1].offset  = (u_int32_t)__builtin_offsetof(apple_double_header_t, pad);
        filehdr->appledouble.entries[1].length  = 0;
        bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler));
 
index efea9dd5445ea19f607ac038067d2b7cc1815627..1e98a995190e2b207dc64f04fb50c4e3f084bf79 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2010 Apple, Inc. All rights reserved.
+ * Copyright (c) 2004-2019 Apple, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
index b8ec504038649744601a719e723addfc8d5523bd..f2827504a8a2ab6b7d9a1eca6d07a86bf3ca5866 100644 (file)
@@ -7,6 +7,7 @@
        objects = {
 
 /* Begin PBXBuildFile section */
+               098AF3B622692BF300F9BA42 /* stat_test.c in Sources */ = {isa = PBXBuildFile; fileRef = 098AF3B522692BF300F9BA42 /* stat_test.c */; };
                721D4F071EA95283000F0555 /* copyfile.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE17C1135A658F002CEE6D /* copyfile.c */; };
                721D4F081EA95290000F0555 /* xattr_flags.c in Sources */ = {isa = PBXBuildFile; fileRef = 72406E621676C3C80099568B /* xattr_flags.c */; };
                72406E631676C3C80099568B /* xattr_flags.c in Sources */ = {isa = PBXBuildFile; fileRef = 72406E621676C3C80099568B /* xattr_flags.c */; };
@@ -34,6 +35,9 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+               098AF3B422692BF300F9BA42 /* stat_test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stat_test.h; sourceTree = "<group>"; };
+               098AF3B522692BF300F9BA42 /* stat_test.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = stat_test.c; sourceTree = "<group>"; };
+               098AF3B7226A510E00F9BA42 /* copyfile_test.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = copyfile_test.entitlements; sourceTree = "<group>"; };
                3F1EFD4C185C4EB400D1C970 /* copyfile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = copyfile.xcconfig; path = xcodescripts/copyfile.xcconfig; sourceTree = "<group>"; };
                721D4F051EA95008000F0555 /* libcopyfile.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcopyfile.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/lib/system/libcopyfile.tbd; sourceTree = DEVELOPER_DIR; };
                72406E621676C3C80099568B /* xattr_flags.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xattr_flags.c; sourceTree = "<group>"; };
                                726EE9DA1E9423E50017A5B9 /* main.c */,
                                726EE9DE1E9425160017A5B9 /* sparse_test.c */,
                                726EE9DF1E9425160017A5B9 /* sparse_test.h */,
+                               098AF3B522692BF300F9BA42 /* stat_test.c */,
+                               098AF3B422692BF300F9BA42 /* stat_test.h */,
                                726EE9E51E946D590017A5B9 /* test_utils.c */,
                                726EE9E11E9427B40017A5B9 /* test_utils.h */,
                                726EE9E21E946B320017A5B9 /* systemx.c */,
                                726EE9E31E946B320017A5B9 /* systemx.h */,
+                               098AF3B7226A510E00F9BA42 /* copyfile_test.entitlements */,
                        );
                        path = copyfile_test;
                        sourceTree = "<group>";
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               098AF3B622692BF300F9BA42 /* stat_test.c in Sources */,
                                721D4F081EA95290000F0555 /* xattr_flags.c in Sources */,
                                721D4F071EA95283000F0555 /* copyfile.c in Sources */,
                                726EE9DB1E9423E50017A5B9 /* main.c in Sources */,
                                CLANG_WARN_SUSPICIOUS_MOVE = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGN_ENTITLEMENTS = copyfile_test/copyfile_test.entitlements;
                                CODE_SIGN_IDENTITY = "-";
                                COPY_PHASE_STRIP = NO;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
index 0111e0d750bc82cafb3cbc60ab9dc1ef5e2e2ca5..2d5417fa3df2fbe927e18df8c6bdafaa90692ed4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Apple, Inc. All rights reserved.
+ * Copyright (c) 2013-19 Apple, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
 /*
  * File flags that are not preserved when copying stat information.
  */
-#define COPYFILE_OMIT_FLAGS    (UF_TRACKED | SF_RESTRICTED)
+#define COPYFILE_OMIT_FLAGS    (UF_TRACKED | SF_RESTRICTED | SF_NOUNLINK | UF_DATAVAULT)
+
+/*
+ * File flags that are not removed when replacing an existing file.
+ */
+#define COPYFILE_PRESERVE_FLAGS        (SF_RESTRICTED | SF_NOUNLINK | UF_DATAVAULT)
 
 #endif /* _COPYFILE_PRIVATE_H */
diff --git a/copyfile_test/copyfile_test.entitlements b/copyfile_test/copyfile_test.entitlements
new file mode 100644 (file)
index 0000000..f5b1b18
Binary files /dev/null and b/copyfile_test/copyfile_test.entitlements differ
index 616dbe3f7b514c9f6a77b34b47876594806af1e8..338b17cdced51f38e5dd471f5c55742df849a4cc 100644 (file)
@@ -12,6 +12,7 @@
 #include <removefile.h>
 
 #include "sparse_test.h"
+#include "stat_test.h"
 #include "test_utils.h"
 
 #define DISK_IMAGE_SIZE_MB     512
@@ -53,6 +54,7 @@ int main(__unused int argc, __unused const char * argv[]) {
        failed |= do_sparse_test(TEST_DIR, stb.f_bsize);
        failed |= do_sparse_recursive_test(TEST_DIR, stb.f_bsize);
        failed |= do_fcopyfile_offset_test(TEST_DIR, stb.f_bsize);
+       failed |= do_preserve_dst_flags_test(TEST_DIR, stb.f_bsize);
 
        // Cleanup the disk image we ran our tests on.
        if (USING_DISK_IMAGE) {
index ccced99fc02033c577eb956f5fa3595219e1b0dd..fc126d76710fbb9065c80c285e6e19fbed0a222e 100644 (file)
 #include "test_utils.h"
 #include "systemx.h"
 
-#define OPEN_FLAGS     O_CREAT|O_TRUNC|O_RDWR
-#define OPEN_PERM      0666
-#define MKDIR_PERM     0777
-#define NAME_MOD       999
-
 /*
  * Copy the file pointed to by src_fd (and orig_name) to copy_name,
  * using copyfile()/fcopyfile() and COPYFILE_DATA. If do_sparse, also pass COPYFILE_DATA_SPARSE.
@@ -69,18 +64,18 @@ static bool test_copy(int src_fd, char* orig_name, char* copy_name, bool do_spar
 
        // Next, verify fcopyfile().
        // Make an fd for the destination.
-       assert_with_errno((copy_fd = open(copy_name, OPEN_FLAGS, OPEN_PERM)) > 0);
+       assert_with_errno((copy_fd = open(copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM)) > 0);
 
        // Call fcopyfile().
        assert_with_errno((cpf_state = copyfile_state_alloc()) != NULL);
        assert_no_err(fcopyfile(src_fd, copy_fd, cpf_state, flags));
 
-       // 1. The copy is a sparse file (if start_off is a multiple of the block size).
+       // 1. The copy is a sparse file (if start_off is 0).
        // 2. The copyfile_state_t for the copy returns that all bytes were copied.
        assert_no_err(fstat(src_fd, &orig_sb));
        assert_no_err(fstat(copy_fd, &copy_sb));
        result &= verify_copy_sizes(&orig_sb, &copy_sb, cpf_state,
-                                                               start_off % copy_sb.st_blksize ? false : do_sparse, start_off);
+               start_off > 0 ? false : do_sparse, start_off);
 
        // 3. The copy and the source have identical contents.
        if (start_off == 0) {
@@ -217,13 +212,13 @@ bool do_sparse_test(const char* apfs_test_directory, size_t block_size) {
                sub_test_success = false;
 
                // Make new names for this file and its copies.
-               test_file_id = rand() % NAME_MOD;
+               test_file_id = rand() % DEFAULT_NAME_MOD;
                create_test_file_name(apfs_test_directory, "sparse", test_file_id, out_name);
                create_test_file_name(apfs_test_directory, "copy_sparse", test_file_id, sparse_copy_name);
                create_test_file_name(apfs_test_directory, "copy_full", test_file_id, full_copy_name);
 
                // Create the test file.
-               fd = open(out_name, OPEN_FLAGS, OPEN_PERM);
+               fd = open(out_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
                assert_with_errno(fd >= 0);
 
                // Write to the test file, making it sparse.
@@ -277,19 +272,19 @@ bool do_sparse_recursive_test(const char *apfs_test_directory, size_t block_size
        assert_with_errno(snprintf(exterior_dir_src, BSIZE_B, "%s/recursive_src", apfs_test_directory) > 0);
        assert_with_errno(snprintf(interior_dir_src, BSIZE_B, "%s/interior", exterior_dir_src) > 0);
 
-       assert_no_err(mkdir(exterior_dir_src, MKDIR_PERM));
-       assert_no_err(mkdir(interior_dir_src, MKDIR_PERM));
+       assert_no_err(mkdir(exterior_dir_src, DEFAULT_MKDIR_PERM));
+       assert_no_err(mkdir(interior_dir_src, DEFAULT_MKDIR_PERM));
 
-       test_file_id = rand() % NAME_MOD;
+       test_file_id = rand() % DEFAULT_NAME_MOD;
        create_test_file_name(exterior_dir_src, "exterior_sparse_file", test_file_id, exterior_file_src);
        create_test_file_name(interior_dir_src, "interior_sparse_file", test_file_id, interior_file_src);
 
        // Create the actual test files.
-       exterior_file_src_fd = open(exterior_file_src, OPEN_FLAGS, OPEN_PERM);
+       exterior_file_src_fd = open(exterior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
        assert_with_errno(exterior_file_src_fd >= 0);
        write_start_and_end_holes(exterior_file_src_fd, block_size);
 
-       interior_file_src_fd = open(interior_file_src, OPEN_FLAGS, OPEN_PERM);
+       interior_file_src_fd = open(interior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
        assert_with_errno(interior_file_src_fd >= 0);
        write_middle_hole(interior_file_src_fd, block_size);
 
@@ -344,20 +339,20 @@ bool do_fcopyfile_offset_test(const char *apfs_test_directory, size_t block_size
        printf("START [fcopyfile_offset]\n");
 
        // Make new names for this file and its copies.
-       test_file_id = rand() % NAME_MOD;
+       test_file_id = rand() % DEFAULT_NAME_MOD;
 
        create_test_file_name(apfs_test_directory, "foff_sparse", test_file_id, out_name);
        create_test_file_name(apfs_test_directory, "foff_copy_sparse", test_file_id, sparse_copy_name);
        create_test_file_name(apfs_test_directory, "foff_copy_full", test_file_id, full_copy_name);
 
        // Create the test file.
-       src_fd = open(out_name, OPEN_FLAGS, OPEN_PERM);
+       src_fd = open(out_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
        assert_with_errno(src_fd >= 0);
        // This writes 5 * block_size bytes.
        assert_with_errno(lseek(src_fd, write_middle_hole(src_fd, block_size), SEEK_SET) == 0);
 
        // Create a sparse copy using fcopyfile().
-       sparse_copy_fd = open(sparse_copy_name, OPEN_FLAGS, OPEN_PERM);
+       sparse_copy_fd = open(sparse_copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
        assert_with_errno(sparse_copy_fd >= 0);
 
        // Seek the sparse copy to a non-zero offset.
@@ -373,7 +368,7 @@ bool do_fcopyfile_offset_test(const char *apfs_test_directory, size_t block_size
 
        // Now, repeat the same procedure with a full copy.
        assert_with_errno(lseek(src_fd, 0, SEEK_SET) == 0);
-       full_copy_fd = open(full_copy_name, OPEN_FLAGS, OPEN_PERM);
+       full_copy_fd = open(full_copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
        assert_with_errno(full_copy_name >= 0);
 
        assert_with_errno(lseek(full_copy_fd, block_size, SEEK_SET) == (off_t) block_size);
index 414007eba91990baa054d2f896647bacbd90d439..089b28069691a9b1efb8b61b1571c405b2c4eee2 100644 (file)
@@ -6,6 +6,9 @@
 #ifndef sparse_test_h
 #define sparse_test_h
 
+#include <stdbool.h>
+#include <stdlib.h>
+
 bool do_sparse_test(const char *apfs_test_directory, size_t block_size);
 bool do_sparse_recursive_test(const char *apfs_test_directory, size_t block_size);
 bool do_fcopyfile_offset_test(const char *apfs_test_directory, size_t block_size);
diff --git a/copyfile_test/stat_test.c b/copyfile_test/stat_test.c
new file mode 100644 (file)
index 0000000..0890544
--- /dev/null
@@ -0,0 +1,149 @@
+//
+//  stat_test.c
+//  copyfile_test
+//
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <removefile.h>
+#include <sandbox/rootless.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "../copyfile.h"
+#include "stat_test.h"
+#include "test_utils.h"
+
+#define STORAGE_CLASS          "copyfile_test"
+#define SPECIAL_DIR_NAME       "special_dir/"
+#define REGULAR_DIR_NAME       "regular_dir"
+#define TEST_FILE_NAME         "almighty_tallest"
+
+typedef int (*special_mkdir_func)(const char *, mode_t, const char *);
+
+static bool test_special_dir_with_flag(const char *source_directory, const char *test_directory,
+       special_mkdir_func create_func, uint32_t flag_to_test) {
+       char special_dir[BSIZE_B] = {0}, regular_dir[BSIZE_B] = {0}, test_file[BSIZE_B] = {0};
+       struct stat sb = {0};
+       bool success = true;
+
+       // The plan here is as follows:
+       //
+       // (1) Create a special directory using 'create_func', and verify that its bsdflags
+       // have `flag_to_test` set.
+       //
+       // (2) Copy the contents of `source_directory` into that special directory,
+       // and verify that the directory and, optionally, a well-known file inside it
+       // have `flag_to_test` set.
+       //
+       // (3) Copy the contents of the special directory into a directory that is not a child
+       // of our special directory (a 'regular' directory), and verify that the directory and
+       // a well-known file inside it do *NOT* have `flag_to_test` set.
+
+       // Create path names.
+       assert_with_errno(snprintf(special_dir, BSIZE_B, "%s/" SPECIAL_DIR_NAME, test_directory) > 0);
+       assert_with_errno(snprintf(regular_dir, BSIZE_B, "%s/" REGULAR_DIR_NAME, test_directory) > 0);
+       assert_with_errno(snprintf(test_file, BSIZE_B, "%s/" TEST_FILE_NAME, special_dir) > 0);
+
+       // Create our regular directory.
+       assert_no_err(mkdir(regular_dir, DEFAULT_MKDIR_PERM));
+
+       // Create our special directory.
+       assert_no_err(create_func(special_dir, DEFAULT_MKDIR_PERM, STORAGE_CLASS));
+
+       // (1) Make sure the special directory has the specified bit set.
+       assert_no_err(stat(special_dir, &sb));
+       assert(sb.st_flags & flag_to_test);
+
+       // Now, copy the source directory's into the special directory.
+       assert_no_err(copyfile(source_directory, special_dir, NULL, COPYFILE_ALL|COPYFILE_RECURSIVE));
+
+       // (2) Make sure that the resulting folder (and optionally, its well-known subfile)
+       // have the specified bit set.
+       assert_no_err(stat(special_dir, &sb));
+       success &= verify_st_flags(&sb, flag_to_test);
+
+       if (flag_to_test != SF_NOUNLINK) {
+               assert_no_err(stat(test_file, &sb));
+               success &= verify_st_flags(&sb, flag_to_test);
+       }
+
+       // Finally, copy the contents of the special directory into our regular directory.
+       // Since at least one of the files in this directory will have a rootless xattr,
+       // which cannot be copied here, we do not attempt to copy extended attributes here.
+       assert_no_err(copyfile(special_dir, regular_dir, NULL,
+               COPYFILE_DATA|COPYFILE_SECURITY|COPYFILE_RECURSIVE));
+
+       // (3) Make sure that the regular directory (and optionally, its well-known subfile)
+       // do *NOT* have the specified bit set.
+       assert_no_err(stat(regular_dir, &sb));
+       success &= ((sb.st_flags & flag_to_test) == 0);
+
+       if (flag_to_test != SF_NOUNLINK) {
+               // Rebuild the path to the subfile, as our original path is relative
+               // to the special directory.
+               memset(test_file, 0, BSIZE_B);
+               assert_with_errno(snprintf(test_file, BSIZE_B, "%s/" TEST_FILE_NAME, regular_dir) > 0);
+
+               assert_no_err(stat(test_file, &sb));
+               success &= verify_st_flags(&sb, 0);
+       }
+
+       // Clean up after the test.
+       assert_no_err(removefile(special_dir, NULL, REMOVEFILE_RECURSIVE));
+       assert_no_err(removefile(regular_dir, NULL, REMOVEFILE_RECURSIVE));
+
+       return success;
+}
+
+bool do_preserve_dst_flags_test(const char *test_directory, __unused size_t block_size) {
+       int interior_file_src_fd, test_file_id;
+       char exterior_dir_src[BSIZE_B] = {0}, interior_file_src[BSIZE_B] = {0};
+       uid_t euid = geteuid();
+       bool success = true;
+
+       printf("START [preserve_dst_flags]\n");
+
+       // Construct our source layout.
+       assert_with_errno(snprintf(exterior_dir_src, BSIZE_B, "%s/" TEST_FILE_NAME, test_directory) > 0);
+
+       assert_no_err(mkdir(exterior_dir_src, DEFAULT_MKDIR_PERM));
+
+       test_file_id = rand() % DEFAULT_NAME_MOD;
+       create_test_file_name(exterior_dir_src, TEST_FILE_NAME, test_file_id, interior_file_src);
+
+       // Create our interior test file.
+       interior_file_src_fd = open(interior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
+       assert_with_errno(interior_file_src_fd >= 0);
+       assert_no_err(close(interior_file_src_fd)); // file only needs to exist
+
+       if (euid == 0) {
+               // Try to copy our directory into a restricted environment.
+               success &= test_special_dir_with_flag(exterior_dir_src, test_directory,
+                       rootless_mkdir_restricted, SF_RESTRICTED);
+
+               // Make sure SF_NOUNLINK works as well.
+               success &= test_special_dir_with_flag(exterior_dir_src, test_directory,
+                       rootless_mkdir_nounlink, SF_NOUNLINK);
+       } else {
+               printf("Skipping SF_RESTRICTED and SF_NOUNLINK tests, because we are not root.\n");
+       }
+
+       // Try to copy our directory into a datavault.
+       success &= test_special_dir_with_flag(exterior_dir_src, test_directory,
+               rootless_mkdir_datavault, UF_DATAVAULT);
+
+       if (success) {
+               printf("PASS  [preserve_dst_flags]\n");
+       } else {
+               printf("FAIL  [preserve_dst_flags]\n");
+       }
+
+       (void)removefile(exterior_dir_src, NULL, REMOVEFILE_RECURSIVE);
+
+       return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/copyfile_test/stat_test.h b/copyfile_test/stat_test.h
new file mode 100644 (file)
index 0000000..ab5c3c5
--- /dev/null
@@ -0,0 +1,14 @@
+//
+//  stat_test.h
+//  copyfile_test
+//
+
+#ifndef stat_test_h
+#define stat_test_h
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+bool do_preserve_dst_flags_test(const char *test_directory, size_t block_size);
+
+#endif /* stat_test_h */
index 97c9efe21d1cd776506133f121685140fa7dc4af..95741b131f11123cfef90d16aa3f1e5783276954 100644 (file)
 #include "test_utils.h"
 #include "systemx.h"
 
+bool verify_st_flags(struct stat *sb, uint32_t flags_to_expect) {
+       // Verify that sb's flags include flags_to_expect.
+       if (((sb->st_flags & flags_to_expect)) != flags_to_expect) {
+               printf("st_flags (%u) do not include expected flags (%u)\n",
+                       sb->st_flags, flags_to_expect);
+               return false;
+       }
+
+       return true;
+}
+
 bool verify_fd_contents(int orig_fd, off_t orig_pos, int copy_fd, off_t copy_pos, size_t length) {
        // Read *length* contents of the two fds and make sure they compare as equal.
        // Don't alter the position of either fd.
@@ -39,7 +50,8 @@ bool verify_fd_contents(int orig_fd, off_t orig_pos, int copy_fd, off_t copy_pos
                for (size_t bad_off = 0; bad_off < length; bad_off++) {
                        if (orig_contents[bad_off] != copy_contents[bad_off]) {
                                printf("first mismatch is at offset %zu, original 0x%llx COPY 0x%llx\n",
-                                          bad_off, orig_contents[bad_off], copy_contents[bad_off]);
+                                          bad_off, (unsigned long long)orig_contents[bad_off],
+                                          (unsigned long long)copy_contents[bad_off]);
                                break;
                        }
                }
@@ -68,14 +80,14 @@ bool verify_copy_sizes(struct stat *orig_sb, struct stat *copy_sb, copyfile_stat
        // If requested, verify that the copy is a sparse file.
        if (do_sparse) {
                if (orig_sb->st_size - src_start != copy_sb->st_size) {
-                       printf("original size - offset (%zd) != copy size (%zd)\n",
+                       printf("original size - offset (%lld) != copy size (%lld)\n",
                                   orig_sb->st_size - src_start, copy_sb->st_size);
                        result = false;
                }
 
-               blocks_offset = src_start / orig_sb->st_blksize;
+               blocks_offset = src_start / S_BLKSIZE;
                if (orig_sb->st_blocks - blocks_offset < copy_sb->st_blocks) {
-                       printf("original blocks - offset (%zd) < copy blocks (%zd)\n",
+                       printf("original blocks - offset (%lld) < copy blocks (%lld)\n",
                                   orig_sb->st_blocks - blocks_offset, copy_sb->st_blocks);
                        result = false;
                }
@@ -85,7 +97,7 @@ bool verify_copy_sizes(struct stat *orig_sb, struct stat *copy_sb, copyfile_stat
        if (cpf_state) {
                assert_no_err(copyfile_state_get(cpf_state, COPYFILE_STATE_COPIED, &cpf_bytes_copied));
                if (orig_sb->st_size - src_start != cpf_bytes_copied) {
-                       printf("original size - start (%zd) != copied bytes (%zd)\n",
+                       printf("original size - start (%lld) != copied bytes (%lld)\n",
                                   orig_sb->st_size - src_start, cpf_bytes_copied);
                        result = false;
                }
index a8fabf858e728ca4ee6ebc3d98d6c2b0ab25bd2f..4cd7faa7dd46cbc1c5465483da416bb999f8d915 100644 (file)
 #define BSIZE_B                                        128
 #define MAX_DISK_IMAGE_SIZE_MB 1024
 
+#define DEFAULT_NAME_MOD               999
+#define DEFAULT_OPEN_FLAGS             O_CREAT|O_TRUNC|O_RDWR
+#define DEFAULT_OPEN_PERM              0666
+#define DEFAULT_MKDIR_PERM             0777
+
 #define DISK_IMAGE_PATH                        "/tmp/copyfile_sparse.sparseimage"
 #define VOLUME_NAME                            "apfs_sparse"
 #define DEFAULT_FSTYPE                 "JHFS+"
@@ -30,6 +35,7 @@
 #define DIFF_PATH                              "/usr/bin/diff"
 
 // Test routine helpers.
+bool verify_st_flags(struct stat *sb, uint32_t flags_to_expect);
 bool verify_fd_contents(int orig_fd, off_t orig_pos, int copy_fd, off_t copy_pos, size_t length);
 bool verify_copy_contents(const char *orig_name, const char *copy_name);
 bool verify_copy_sizes(struct stat *orig_sb, struct stat *copy_sb, copyfile_state_t cpf_state,
index 032b6ad011c2af01755f8585cd6d8c54d52dab6a..dc7290a1035e224686255f8ed605b22fc05c24e5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Apple, Inc. All rights reserved.
+ * Copyright (c) 2013-19 Apple, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
index 0840430613bee208c9f54495831237afd2a87287..35afc2a866abe01f4d7c8d6443183785ccfd5485 100644 (file)
@@ -1,7 +1,7 @@
 .\"
 .\" Copyright (c) 2013 Apple Computer, Inc.  All rights reserved.
 .\"
-.Dd December 21, 2016
+.Dd October 9, 2018
 .Dt XATTR_NAME_WITH_FLAGS 3
 .Os
 .Sh NAME
@@ -80,7 +80,7 @@ Indicates that the intent is to share, or export, the object.  For example,
 saving as an attachment in an email message, or placing in a public folder.
 Sensitive information should probably not be preserved in this case.
 .It Dv XATTR_OPERATION_INTENT_SYNC
-Indicates that the intent is to sync the object to a service like iCloud.
+Indicates that the intent is to sync the object to a service like iCloud Drive.
 .El
 .Sh FLAGS
 Various flags are defined by the type
@@ -101,9 +101,8 @@ This indicates that the extended attribute should never be copied from a
 source object to a destination, no matter what the given intent is.
 .It Dv XATTR_FLAG_SYNCABLE
 This indicates that the extended attribute should be copied when the file
-is synced on services like iCloud. Sync services tends to want the metadata
-synced to be kept to a bare minimum, and may enforce additional restrictions
-on the acceptable size and number of extended attributes.
+is synced on services like iCloud Drive. Sync services may enforce additional
+restrictions on the acceptable size and number of extended attributes.
 .El
 .Sh EXAMPLE
 The following example is a simple function that, given an extended attribute
index 21d9a29eb73215f6836818ca02d9a9a06034d372..942720301d8848477fa8de8e2f2ca2de158cde75 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Apple, Inc. All rights reserved.
+ * Copyright (c) 2013-19 Apple, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -89,40 +89,6 @@ typedef uint64_t CopyOperationProperties_t;
  */
 #define kCopyOperationPropertyNeverPreserve    ((CopyOperationProperties_t)0x0004)
 
-#if 0
-/*
- * These are all going to be removed, and I don't believe anyone used them.
- */
-/*
- * Given an extended attribute name, and a set of properties, return an
- *  allocated C string with the name.  This will return NULL on error;
- * errno may be set to ENOMEM if the new name cannot be allocated, or
- * ENAMETOOLONG if the new name is longer than the maximum for EAs (127 UTF8
- * characters).  The caller must deallocate the return value otherwise.
- *
- * If no properties are set, it returns a copy of the EA name.
- *
- * If the EA name is in the internal list, and the properties are the same as
- * defined there, then it will also return an unmodified copy of the EA name.
- */
-extern char *_xattrNameWithProperties(const char *, CopyOperationProperties_t) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
-
-/*
- * Given an extended attribute name, which may or may not have properties encoded
- * as a suffix, return just the name of the attribute.  E.g., com.example.mine#P
- * would return "com.example.mine".  The return value will be NULL on error;
- * errno will be set to ENOMEM if it cannot be allocated.  The caller must deallocate
- * the return value.
- */
-extern char *_xattrNameWithoutProperties(const char *) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
-
-/*
- * Given an EA name, return the properties.  If the name is in the internal list,
- * those properties will be returned.  Unknown property encodings are ignored.
- */
-extern CopyOperationProperties_t _xattrPropertiesFromName(const char *) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
-#endif /* 0 */
-
 __END_DECLS
 
 #endif /* _XATTR_PROPERTIES_H */