--- /dev/null
+copyfile.xcodeproj/project.xcworkspace
+copyfile.xcodeproj/xcuserdata/
+.DS_Store
+build/
.\"
.\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
.\"
-.Dd August 30, 2017
+.Dd November 2, 2017
.Dt COPYFILE 3
.Os
.Sh NAME
| 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.)
| 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
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
.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
/*
- * 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 */
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);
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;
/*
* 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)
{
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.
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;
}
{
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;
}
}
* Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
*/
-
-#define offsetof(type, member) __builtin_offsetof(type, member)
-
#define XATTR_MAXATTRLEN (16*1024*1024)
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));
/*
- * Copyright (c) 2004-2010 Apple, Inc. All rights reserved.
+ * Copyright (c) 2004-2019 Apple, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
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 */; };
/* 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";
/*
- * 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 */
#include <removefile.h>
#include "sparse_test.h"
+#include "stat_test.h"
#include "test_utils.h"
#define DISK_IMAGE_SIZE_MB 512
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) {
#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.
// 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, ©_sb));
result &= verify_copy_sizes(&orig_sb, ©_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) {
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.
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);
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.
// 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);
#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);
--- /dev/null
+//
+// 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;
+}
--- /dev/null
+//
+// 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 */
#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.
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;
}
}
// 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;
}
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;
}
#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+"
#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,
/*
- * Copyright (c) 2013 Apple, Inc. All rights reserved.
+ * Copyright (c) 2013-19 Apple, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
.\"
.\" 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
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
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
/*
- * Copyright (c) 2013 Apple, Inc. All rights reserved.
+ * Copyright (c) 2013-19 Apple, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*/
#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 */