copyfile-166.40.1

This commit is contained in:
Ariel Abreu 2020-06-16 08:44:17 -04:00
parent 139d10b1f5
commit ce78db7998
No known key found for this signature in database
GPG Key ID: F4D43CC7053EA2B3
22 changed files with 5101 additions and 2803 deletions

View File

@ -1 +0,0 @@
../../kernel-include/

View File

@ -1,7 +1,7 @@
.\"
.\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
.\"
.Dd April 27, 2006
.Dd July 22, 2019
.Dt COPYFILE 3
.Os
.Sh NAME
@ -178,11 +178,65 @@ Unlink the
file before starting. (This is only applicable for the
.Fn copyfile
function.)
.It Dv COPYFILE_CLONE_FORCE
Clone the file instead.
This is a force flag i.e. if cloning fails, an error is returned.
This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA
| 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. 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.)
.It Dv COPYFILE_CLONE
Try to clone the file instead.
This is a best try flag i.e. if cloning fails, fallback to copying the file.
This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA
| 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. 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
function.)
.It Dv COPYFILE_DATA_SPARSE
Copy a file sparsely.
This requires that the source and destination file systems support sparse files with hole sizes
at least as large as their block sizes.
This also requires that the source file is sparse, and for
.Fn fcopyfile
the source file descriptor's offset be a multiple of the minimum hole size.
If COPYFILE_DATA is also specified, this will fall back to a full copy
if sparse copying cannot be performed for any reason; otherwise, an error is returned.
.It Dv COPYFILE_NOFOLLOW
This is a convenience macro, equivalent to
.Dv (COPYFILE_NOFOLLOW_DST|COPYFILE_NOFOLLOW_SRC) .
.Dv (COPYFILE_NOFOLLOW_DST | COPYFILE_NOFOLLOW_SRC) .
.It Dv COPYFILE_RUN_IN_PLACE
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.
.It Dv COPYFILE_PRESERVE_DST_TRACKED
Preserve the UF_TRACKED flag at
.Va to
when copying metadata, regardless of whether
.Va from
has it set. This flag is used in conjunction with COPYFILE_STAT, or COPYFILE_CLONE (for its fallback case).
.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
@ -264,6 +318,11 @@ Get the number of data bytes copied so far.
(Only valid for
.Fn copyfile_state_get ;
see below for more details about callbacks.)
If a
.Dv COPYFILE_CLONE
or
.Dv COPYFILE_CLONE_FORCE
operation successfully cloned the requested objects, then this value will be 0.
The
.Va dst
parameter is a pointer to
@ -277,6 +336,18 @@ for
(see below for details). This field cannot be set,
and may be
.Dv NULL .
.It Dv COPYFILE_STATE_WAS_CLONED
True if a
.Dv COPYFILE_CLONE
or
.Dv COPYFILE_CLONE_FORCE
operation successfully cloned the requested objects.
The
.Va dst
parameter is a pointer to
.Vt bool
(type
.Vt bool\ * ).
.El
.Sh Recursive Copies
When given the
@ -399,6 +470,26 @@ In the event of an error, the
.Dv errno
value will be set appropriately.
.Pp
Note that recursive cloning is also supported with the
.Dv COPYFILE_CLONE
flag (but not the
.Dv COPYFILE_CLONE_FORCE
flag). A recursive clone operation invokes
.Fn copyfile
with
.Dv COPYFILE_CLONE
on every entry found in the source file-system object. Because
.Fn copyfile
does not allow the cloning of directories, a recursive clone will
instead copy any directory it finds (while cloning its contents).
As symbolic links may point to directories, they are not followed
during recursive clones even if the source is a symbolic link.
Additionally, because the
.Dv COPYFILE_CLONE
flag implies the
.Dv COPYFILE_EXCL
flag, recursive clones require a nonexistent destination.
.Pp
The
.Dv COPYFILE_PACK ,
.Dv COPYFILE_UNPACK ,
@ -407,6 +498,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
@ -485,6 +589,11 @@ parameters will be passed in, they may be
.Dv NULL
in the case of
.Fn fcopyfile .
.Pp
Note that progress callbacks are not invoked when a clone is requested
(e.g.
.Dv COPYFILE_CLONE )
unless the clone cannot be performed and a copy is performed instead.
.Sh RETURN VALUES
Except when given the
.Dv COPYFILE_CHECK
@ -537,15 +646,45 @@ The
or
.Va to
parameter to
.Fn copyfile
.Fn fcopyfile
was a negative number.
.It Bq Er ENOMEM
A memory allocation failed.
.It Bq Er ENOTSUP
The source file was not a directory, symbolic link, or regular file.
.It Bq Er ENOTSUP
COPYFILE_CLONE_FORCE was specified and file cloning is not supported.
.It Bq Er ENOTSUP
COPYFILE_DATA_SPARSE was specified, sparse copying is not supported,
and COPYFILE_DATA was not specified.
.It Bq Er ECANCELED
The copy was cancelled by callback.
.It Bq Er EEXIST
The
.Va to
parameter to
.Fn copyfile
already existed and was passed in with
.Dv COPYFILE_EXCL .
.It Bq Er ENOENT
The
.Va from
parameter to
.Fn copyfile
did not exist.
.It Bq Er EACCES
Search permission is denied for a component of the path prefix for
the
.Va from
or
.Va to
parameters.
.It Bq Er EACCES
Write permission is denied for a component of the path prefix for the
.Va to
parameter.
.El
.Pp
In addition, both functions may set
.Dv errno
via an underlying library or system call.

File diff suppressed because it is too large Load Diff

View 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@
*
@ -73,6 +73,7 @@ typedef int (*copyfile_callback_t)(int, int, copyfile_state_t, const char *, con
#define COPYFILE_STATE_STATUS_CTX 7
#define COPYFILE_STATE_COPIED 8
#define COPYFILE_STATE_XATTRNAME 9
#define COPYFILE_STATE_WAS_CLONED 10
#define COPYFILE_DISABLE_VAR "COPYFILE_DISABLE"
@ -100,6 +101,15 @@ typedef int (*copyfile_callback_t)(int, int, copyfile_state_t, const char *, con
#define COPYFILE_PACK (1<<22)
#define COPYFILE_UNPACK (1<<23)
#define COPYFILE_CLONE (1<<24)
#define COPYFILE_CLONE_FORCE (1<<25)
#define COPYFILE_RUN_IN_PLACE (1<<26)
#define COPYFILE_DATA_SPARSE (1<<27)
#define COPYFILE_PRESERVE_DST_TRACKED (1<<28)
#define COPYFILE_VERBOSE (1<<30)
#define COPYFILE_RECURSE_ERROR 0

View File

@ -0,0 +1,417 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
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 */; };
726EE9DB1E9423E50017A5B9 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 726EE9DA1E9423E50017A5B9 /* main.c */; };
726EE9E01E9425160017A5B9 /* sparse_test.c in Sources */ = {isa = PBXBuildFile; fileRef = 726EE9DE1E9425160017A5B9 /* sparse_test.c */; };
726EE9E41E946B320017A5B9 /* systemx.c in Sources */ = {isa = PBXBuildFile; fileRef = 726EE9E21E946B320017A5B9 /* systemx.c */; };
726EE9E61E946D590017A5B9 /* test_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 726EE9E51E946D590017A5B9 /* test_utils.c */; };
72B4C0F41676C47D00C13E05 /* copyfile_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 72B4C0F31676C47D00C13E05 /* copyfile_private.h */; settings = {ATTRIBUTES = (Private, ); }; };
72EAA3B016A72F4500833E98 /* xattr_flags.h in Headers */ = {isa = PBXBuildFile; fileRef = 72EAA3AF16A72F4500833E98 /* xattr_flags.h */; settings = {ATTRIBUTES = (Public, ); }; };
86EF9F0A1834018C00AAB3F3 /* xattr_properties.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EF9F091834018C00AAB3F3 /* xattr_properties.h */; settings = {ATTRIBUTES = (Private, ); }; };
FCCE17C3135A658F002CEE6D /* copyfile.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE17C1135A658F002CEE6D /* copyfile.c */; };
FCCE17C4135A658F002CEE6D /* copyfile.h in Headers */ = {isa = PBXBuildFile; fileRef = FCCE17C2135A658F002CEE6D /* copyfile.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
726EE9D61E9423E50017A5B9 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* 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>"; };
726EE9D81E9423E50017A5B9 /* copyfile_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = copyfile_test; sourceTree = BUILT_PRODUCTS_DIR; };
726EE9DA1E9423E50017A5B9 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
726EE9DE1E9425160017A5B9 /* sparse_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sparse_test.c; sourceTree = "<group>"; };
726EE9DF1E9425160017A5B9 /* sparse_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sparse_test.h; sourceTree = "<group>"; };
726EE9E11E9427B40017A5B9 /* test_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = test_utils.h; sourceTree = "<group>"; };
726EE9E21E946B320017A5B9 /* systemx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = systemx.c; sourceTree = "<group>"; };
726EE9E31E946B320017A5B9 /* systemx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = systemx.h; sourceTree = "<group>"; };
726EE9E51E946D590017A5B9 /* test_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_utils.c; sourceTree = "<group>"; };
72B4C0F31676C47D00C13E05 /* copyfile_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = copyfile_private.h; sourceTree = "<group>"; };
72EAA3AF16A72F4500833E98 /* xattr_flags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xattr_flags.h; sourceTree = "<group>"; };
861E1C14180F0AF900E65B9A /* xattr_name_with_flags.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = xattr_name_with_flags.3; sourceTree = "<group>"; };
86EF9F091834018C00AAB3F3 /* xattr_properties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xattr_properties.h; sourceTree = "<group>"; };
FCCE17BB135A6444002CEE6D /* libcopyfile.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcopyfile.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
FCCE17C0135A658F002CEE6D /* copyfile.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = copyfile.3; sourceTree = "<group>"; };
FCCE17C1135A658F002CEE6D /* copyfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = copyfile.c; sourceTree = "<group>"; };
FCCE17C2135A658F002CEE6D /* copyfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = copyfile.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
726EE9D51E9423E50017A5B9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
FCCE17B8135A6444002CEE6D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
721D4F041EA95007000F0555 /* Frameworks */ = {
isa = PBXGroup;
children = (
721D4F051EA95008000F0555 /* libcopyfile.tbd */,
);
name = Frameworks;
sourceTree = "<group>";
};
726EE9D91E9423E50017A5B9 /* copyfile_test */ = {
isa = PBXGroup;
children = (
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>";
};
FCCE17AB135A5FFB002CEE6D = {
isa = PBXGroup;
children = (
3F1EFD4C185C4EB400D1C970 /* copyfile.xcconfig */,
861E1C14180F0AF900E65B9A /* xattr_name_with_flags.3 */,
72B4C0F31676C47D00C13E05 /* copyfile_private.h */,
72EAA3AF16A72F4500833E98 /* xattr_flags.h */,
72406E621676C3C80099568B /* xattr_flags.c */,
FCCE17C0135A658F002CEE6D /* copyfile.3 */,
FCCE17C1135A658F002CEE6D /* copyfile.c */,
86EF9F091834018C00AAB3F3 /* xattr_properties.h */,
FCCE17C2135A658F002CEE6D /* copyfile.h */,
726EE9D91E9423E50017A5B9 /* copyfile_test */,
FCCE17BC135A6444002CEE6D /* Products */,
721D4F041EA95007000F0555 /* Frameworks */,
);
sourceTree = "<group>";
usesTabs = 1;
};
FCCE17BC135A6444002CEE6D /* Products */ = {
isa = PBXGroup;
children = (
FCCE17BB135A6444002CEE6D /* libcopyfile.dylib */,
726EE9D81E9423E50017A5B9 /* copyfile_test */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
FCCE17B9135A6444002CEE6D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
FCCE17C4135A658F002CEE6D /* copyfile.h in Headers */,
72EAA3B016A72F4500833E98 /* xattr_flags.h in Headers */,
72B4C0F41676C47D00C13E05 /* copyfile_private.h in Headers */,
86EF9F0A1834018C00AAB3F3 /* xattr_properties.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
726EE9D71E9423E50017A5B9 /* copyfile_test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 726EE9DD1E9423E50017A5B9 /* Build configuration list for PBXNativeTarget "copyfile_test" */;
buildPhases = (
726EE9D41E9423E50017A5B9 /* Sources */,
726EE9D51E9423E50017A5B9 /* Frameworks */,
726EE9D61E9423E50017A5B9 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = copyfile_test;
productName = copyfile_test;
productReference = 726EE9D81E9423E50017A5B9 /* copyfile_test */;
productType = "com.apple.product-type.tool";
};
FCCE17BA135A6444002CEE6D /* copyfile */ = {
isa = PBXNativeTarget;
buildConfigurationList = FCCE17BE135A6444002CEE6D /* Build configuration list for PBXNativeTarget "copyfile" */;
buildPhases = (
FCCE17B7135A6444002CEE6D /* Sources */,
FCCE17B8135A6444002CEE6D /* Frameworks */,
FCCE17B9135A6444002CEE6D /* Headers */,
FCCE17DF135A70A0002CEE6D /* Run Script */,
);
buildRules = (
);
dependencies = (
);
name = copyfile;
productName = copyfile;
productReference = FCCE17BB135A6444002CEE6D /* libcopyfile.dylib */;
productType = "com.apple.product-type.library.dynamic";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
FCCE17AD135A5FFB002CEE6D /* Project object */ = {
isa = PBXProject;
attributes = {
ORGANIZATIONNAME = "Apple Inc.";
TargetAttributes = {
726EE9D71E9423E50017A5B9 = {
CreatedOnToolsVersion = 9.0;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = FCCE17B0135A5FFB002CEE6D /* Build configuration list for PBXProject "copyfile" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = FCCE17AB135A5FFB002CEE6D;
productRefGroup = FCCE17BC135A6444002CEE6D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
FCCE17BA135A6444002CEE6D /* copyfile */,
726EE9D71E9423E50017A5B9 /* copyfile_test */,
);
};
/* End PBXProject section */
/* Begin PBXShellScriptBuildPhase section */
FCCE17DF135A70A0002CEE6D /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install_files.sh";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
726EE9D41E9423E50017A5B9 /* Sources */ = {
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 */,
726EE9E61E946D590017A5B9 /* test_utils.c in Sources */,
726EE9E41E946B320017A5B9 /* systemx.c in Sources */,
726EE9E01E9425160017A5B9 /* sparse_test.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
FCCE17B7135A6444002CEE6D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FCCE17C3135A658F002CEE6D /* copyfile.c in Sources */,
72406E631676C3C80099568B /* xattr_flags.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
726EE9DC1E9423E50017A5B9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
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";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/system",
);
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SUPPORTS_TEXT_BASED_API = YES;
TAPI_VERIFY_MODE = Pedantic;
};
name = Release;
};
FCCE17B3135A5FFB002CEE6D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3F1EFD4C185C4EB400D1C970 /* copyfile.xcconfig */;
buildSettings = {
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
SDKROOT = macosx.internal;
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-Wformat=2",
"-Wformat-security",
"-Wnested-externs",
"-Wno-trigraphs",
"-Wredundant-decls",
"-Wwrite-strings",
);
};
name = Release;
};
FCCE17BD135A6444002CEE6D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
EXECUTABLE_PREFIX = lib;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "__DARWIN_NOW_CANCELABLE=1";
LINK_WITH_STANDARD_LIBRARIES = NO;
OTHER_LDFLAGS = (
"-Wl,-umbrella,System",
"-L/usr/lib/system",
"-ldyld",
"-lcompiler_rt",
"-lsystem_kernel",
"-lsystem_malloc",
"-lsystem_c",
"-lsystem_blocks",
"-lquarantine",
"-lsystem_asl",
"-lsystem_info",
"-lxpc",
"-ldispatch",
);
"OTHER_LDFLAGS[sdk=iphone*]" = (
"-Wl,-umbrella,System",
"-L/usr/lib/system",
"-ldyld",
"-lcompiler_rt",
"-lsystem$(SIM_SUFFIX)_kernel",
"-lsystem_malloc",
"-lsystem_c",
"-lsystem_blocks",
"-lsystem_asl",
"-lsystem_info",
"-ldispatch",
"-lxpc",
);
SDKROOT = macosx.internal;
"SIM_SUFFIX[sdk=iphonesimulator*]" = _sim;
SUPPORTS_TEXT_BASED_API = YES;
TAPI_VERIFY_MODE = Pedantic;
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-Wformat=2",
"-Wformat-security",
"-Wnested-externs",
"-Wno-parentheses",
"-Wno-trigraphs",
"-Wredundant-decls",
"-Wwrite-strings",
);
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
726EE9DD1E9423E50017A5B9 /* Build configuration list for PBXNativeTarget "copyfile_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
726EE9DC1E9423E50017A5B9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
FCCE17B0135A5FFB002CEE6D /* Build configuration list for PBXProject "copyfile" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FCCE17B3135A5FFB002CEE6D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
FCCE17BE135A6444002CEE6D /* Build configuration list for PBXNativeTarget "copyfile" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FCCE17BD135A6444002CEE6D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = FCCE17AD135A5FFB002CEE6D /* Project object */;
}

View 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@
*
@ -33,6 +33,11 @@
/*
* 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 */

Binary file not shown.

View File

@ -0,0 +1,68 @@
//
// main.c
// copyfile_test
//
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <removefile.h>
#include "sparse_test.h"
#include "stat_test.h"
#include "test_utils.h"
#define DISK_IMAGE_SIZE_MB 512
#if TARGET_OS_OSX
#define TEST_DIR MOUNT_PATH
#define USING_DISK_IMAGE 1
#else
#define TEST_DIR "/tmp/copyfile_test"
#define USING_DISK_IMAGE 0
#endif // TARGET_OS_OSX
#define MIN_BLOCKSIZE_B 512
#define DEFAULT_BLOCKSIZE_B 4096
#define MAX_BLOCKSIZE_B 16384
int main(__unused int argc, __unused const char * argv[]) {
bool failed = false;
struct statfs stb;
// Create a disk image to run our tests in.
if (USING_DISK_IMAGE) {
disk_image_create(APFS_FSTYPE, DISK_IMAGE_SIZE_MB);
} else {
(void)removefile(TEST_DIR, NULL, REMOVEFILE_RECURSIVE);
assert_no_err(mkdir(TEST_DIR, 0777));
}
// Make sure the test directory exists, is apfs formatted,
// and that we have a sane block size.
assert_no_err(statfs(TEST_DIR, &stb));
assert_no_err(memcmp(stb.f_fstypename, APFS_FSTYPE, sizeof(APFS_FSTYPE)));
if (stb.f_bsize < MIN_BLOCKSIZE_B || stb.f_bsize > MAX_BLOCKSIZE_B) {
stb.f_bsize = DEFAULT_BLOCKSIZE_B;
}
// Run our tests.
sranddev();
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);
failed |= do_preserve_dst_tracked_test(TEST_DIR, stb.f_bsize);
// Cleanup the disk image we ran our tests on.
if (USING_DISK_IMAGE) {
disk_image_destroy(false);
} else {
(void)removefile(TEST_DIR, NULL, REMOVEFILE_RECURSIVE);
}
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -0,0 +1,397 @@
//
// sparse_test.c
// copyfile_test
//
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <removefile.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include "../copyfile.h"
#include "sparse_test.h"
#include "test_utils.h"
#include "systemx.h"
/*
* 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.
* Before copying, rewind src_fd to start_off.
*/
static bool test_copy(int src_fd, char* orig_name, char* copy_name, bool do_sparse, off_t start_off) {
struct stat orig_sb, copy_sb;
int copy_fd;
bool result = true;
copyfile_state_t cpf_state;
// Get ready for the test.
memset(&orig_sb, 0, sizeof(orig_sb));
memset(&copy_sb, 0, sizeof(copy_sb));
assert_with_errno((cpf_state = copyfile_state_alloc()) != NULL);
assert_with_errno(lseek(src_fd, start_off, SEEK_SET) == start_off);
// First, verify copyfile().
copyfile_flags_t flags = COPYFILE_ALL;
if (do_sparse) {
flags |= COPYFILE_DATA_SPARSE;
}
assert_no_err(copyfile(orig_name, copy_name, cpf_state, flags));
// The file was (hopefully) copied. Now, we must verify three things:
// 1. If (do_sparse), verify that the copy is a sparse file.
// For now, let's approximate this by testing that the sizes of the two files are equal.
// 2. The copyfile_state_t for the copy returns that all bytes were copied.
// 3. The copy and the source have identical contents.
// 1. The copy is a sparse file.
// 2. The copyfile_state_t for the copy returns that all bytes were copied.
assert_no_err(stat(orig_name, &orig_sb));
assert_no_err(stat(copy_name, &copy_sb));
result &= verify_copy_sizes(&orig_sb, &copy_sb, cpf_state, do_sparse, 0);
// 3. The copy and the source have identical contents.
result &= verify_copy_contents(orig_name, copy_name);
// Post-test cleanup.
assert_no_err(copyfile_state_free(cpf_state));
assert_no_err(removefile(copy_name, NULL, REMOVEFILE_RECURSIVE));
memset(&orig_sb, 0, sizeof(struct stat));
memset(&copy_sb, 0, sizeof(struct stat));
// Next, verify fcopyfile().
// Make an fd for the destination.
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 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 > 0 ? false : do_sparse, start_off);
// 3. The copy and the source have identical contents.
if (start_off == 0) {
result &= verify_copy_contents(orig_name, copy_name);
}
// Post-test cleanup.
assert_no_err(copyfile_state_free(cpf_state));
assert_no_err(removefile(copy_name, NULL, REMOVEFILE_RECURSIVE));
assert_no_err(close(copy_fd));
return result;
}
// Sparse file creation functions.
// Each take the source file descriptor pointing at the beginning of the file and the block size.
// Each return the offset we should return the fd to before any copying should be performed.
typedef off_t (*creator_func)(int, off_t);
static off_t write_start_and_end_holes(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "j", 1, block_size) == 1);
assert_no_err(ftruncate(fd, 3 * block_size));
assert_no_err(create_hole_in_fd(fd, 0, block_size));
assert_no_err(create_hole_in_fd(fd, 2 * block_size, block_size));
return 0;
}
static off_t write_end_hole(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "n", 1, 0) == 1);
assert_no_err(ftruncate(fd, 16 * block_size));
assert_no_err(create_hole_in_fd(fd, block_size, 15 * block_size));
return 0;
}
static off_t write_start_hole(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "p", 1, 16 * block_size) == 1);
assert_no_err(create_hole_in_fd(fd, 0, 16 * block_size));
return 0;
}
static off_t write_middle_hole(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "k", 1, 0) == 1);
assert_with_errno(pwrite(fd, "k", 1, 4 * block_size) == 1);
assert_no_err(ftruncate(fd, 5 * block_size));
assert_no_err(create_hole_in_fd(fd, block_size, 3 * block_size));
return 0;
}
static off_t write_start_and_middle_holes(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "l", 1, 16 * block_size) == 1);
assert_with_errno(pwrite(fd, "l", 1, 32 * block_size) == 1);
assert_no_err(create_hole_in_fd(fd, 0, 16 * block_size));
assert_no_err(create_hole_in_fd(fd, 17 * block_size, 15 * block_size));
return 0;
}
static off_t write_middle_and_end_holes(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "m", 1, 0) == 1);
assert_with_errno(pwrite(fd, "m", 1, 16 * block_size) == 1);
assert_no_err(ftruncate(fd, 32 * block_size));
assert_no_err(create_hole_in_fd(fd, block_size, 15 * block_size));
assert_no_err(create_hole_in_fd(fd, 17 * block_size, 15 * block_size));
return 0;
}
static off_t write_no_sparse(int fd, __unused off_t block_size) {
assert_with_errno(pwrite(fd, "z", 1, 0) == 1);
return 0;
}
static off_t write_sparse_odd_offset(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "q", 1, block_size) == 1);
assert_no_err(create_hole_in_fd(fd, 0, block_size));
// Return with the fd pointing at offset 1.
assert_with_errno(lseek(fd, 1, SEEK_SET) == 1);
return 1;
}
static off_t write_sparse_bs_offset(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "a", 1, block_size) == 1);
assert_with_errno(pwrite(fd, "b", 1, 2 * block_size) == 1);
assert_no_err(create_hole_in_fd(fd, 0, block_size));
// Return with the fd pointing at block_size.
assert_with_errno(lseek(fd, block_size, SEEK_SET) == block_size);
return block_size;
}
static off_t write_diff_adj_holes(int fd, off_t block_size) {
assert_with_errno(pwrite(fd, "w", 1, 0));
assert_with_errno(pwrite(fd, "w", 1, 3 * block_size));
assert_no_err(ftruncate(fd, 4 * block_size));
assert_no_err(create_hole_in_fd(fd, block_size, block_size));
assert_no_err(create_hole_in_fd(fd, 2 * block_size, block_size));
return 0;
}
typedef struct {
creator_func func; // pointer to function to create a sparse file
const char * name; // null terminated string
} sparse_test_func;
#define NUM_TEST_FUNCTIONS 10
sparse_test_func test_functions[NUM_TEST_FUNCTIONS] = {
{write_start_and_end_holes, "start_and_end_holes"},
{write_middle_hole, "middle_hole"},
{write_start_and_middle_holes, "start_and_middle_holes"},
{write_middle_and_end_holes, "middle_and_end_holes"},
{write_end_hole, "end_hole"},
{write_start_hole, "start_hole"},
{write_no_sparse, "no_sparse"},
{write_sparse_odd_offset, "write_sparse_odd_offset"},
{write_sparse_bs_offset, "write_sparse_bs_offset"},
{write_diff_adj_holes, "write_diff_adj_holes"}
};
bool do_sparse_test(const char* apfs_test_directory, size_t block_size) {
int fd, test_file_id;
char out_name[BSIZE_B], sparse_copy_name[BSIZE_B], full_copy_name[BSIZE_B];
bool success = true, sub_test_success;
off_t start_off;
for (size_t sub_test = 0; sub_test < NUM_TEST_FUNCTIONS; sub_test++) {
printf("START [%s]\n", test_functions[sub_test].name);
sub_test_success = false;
// Make new names for this file and its copies.
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, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
assert_with_errno(fd >= 0);
// Write to the test file, making it sparse.
start_off = test_functions[sub_test].func(fd, (off_t) block_size);
assert_no_err(fsync(fd));
// Make sure that a sparse copy is successful.
sub_test_success = test_copy(fd, out_name, sparse_copy_name, true, start_off);
if (sub_test_success) {
// Make sure that a full copy is successful.
sub_test_success = test_copy(fd, out_name, full_copy_name, false, start_off);
}
// Report the result on stdout.
if (!sub_test_success) {
printf("FAIL [%s]\n", test_functions[sub_test].name);
success = false;
} else {
printf("PASS [%s]\n", test_functions[sub_test].name);
}
// Cleanup for the next test.
assert_no_err(close(fd));
(void)removefile(out_name, NULL, 0);
(void)removefile(sparse_copy_name, NULL, 0);
(void)removefile(full_copy_name, NULL, 0);
memset(out_name, 0, BSIZE_B);
memset(sparse_copy_name, 0, BSIZE_B);
memset(full_copy_name, 0, BSIZE_B);
}
return success ? EXIT_SUCCESS : EXIT_FAILURE;
}
bool do_sparse_recursive_test(const char *apfs_test_directory, size_t block_size) {
int exterior_file_src_fd, interior_file_src_fd, test_file_id;
char exterior_dir_src[BSIZE_B] = {0}, interior_dir_src[BSIZE_B] = {0}, exterior_dir_dst[BSIZE_B] = {0}, interior_dir_dst[BSIZE_B] = {0};
char exterior_file_src[BSIZE_B] = {0}, interior_file_src[BSIZE_B] = {0}, exterior_file_dst[BSIZE_B] = {0}, interior_file_dst[BSIZE_B] = {0};
struct stat exterior_file_src_sb, interior_file_src_sb, exterior_file_dst_sb, interior_file_dst_sb;
bool success = true;
printf("START [sparse_recursive]\n");
// Get ready for the test.
memset(&exterior_file_src_sb, 0, sizeof(exterior_file_src_sb));
memset(&interior_file_src_sb, 0, sizeof(interior_file_src_sb));
memset(&exterior_file_dst_sb, 0, sizeof(exterior_file_dst_sb));
memset(&interior_file_dst_sb, 0, sizeof(interior_file_dst_sb));
// Construct our source layout.
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, DEFAULT_MKDIR_PERM));
assert_no_err(mkdir(interior_dir_src, DEFAULT_MKDIR_PERM));
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, 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, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
assert_with_errno(interior_file_src_fd >= 0);
write_middle_hole(interior_file_src_fd, block_size);
// Now, recursively copy our folder using sparse data copying.
assert_with_errno(snprintf(exterior_dir_dst, BSIZE_B, "%s/recursive_dst", apfs_test_directory) > 0);
assert_no_err(copyfile(exterior_dir_src, exterior_dir_dst, NULL, COPYFILE_ALL|COPYFILE_RECURSIVE|COPYFILE_DATA_SPARSE));
// The files were (hopefully) copied. Now, we must verify three things:
// 1. Verify that the copy is a sparse file.
// For now, let's approximate this by testing that the sizes of the two files are equal.
// 2. The copy and the source have identical contents.
// First, construct our destination layout.
assert_with_errno(snprintf(exterior_dir_dst, BSIZE_B, "%s/recursive_dst", apfs_test_directory) > 0);
create_test_file_name(exterior_dir_dst, "exterior_sparse_file", test_file_id, exterior_file_dst);
assert_with_errno(snprintf(interior_dir_dst, BSIZE_B, "%s/interior", exterior_dir_dst) > 0);
create_test_file_name(interior_dir_dst, "interior_sparse_file", test_file_id, interior_file_dst);
// 1. The copy is a sparse file.
assert_no_err(fstat(exterior_file_src_fd, &exterior_file_src_sb));
assert_no_err(stat(exterior_file_dst, &exterior_file_dst_sb));
assert_no_err(fstat(interior_file_src_fd, &interior_file_src_sb));
assert_no_err(stat(interior_file_dst, &interior_file_dst_sb));
success &= verify_copy_sizes(&exterior_file_src_sb, &exterior_file_dst_sb, NULL, true, 0);
success &= verify_copy_sizes(&interior_file_src_sb, &interior_file_dst_sb, NULL, true, 0);
// 2. The copy and the source have identical contents.
success &= verify_copy_contents(exterior_file_src, exterior_file_dst);
success &= verify_copy_contents(interior_file_src, interior_file_dst);
if (success) {
printf("PASS [sparse_recursive]\n");
} else {
printf("FAIL [sparse_recursive]\n");
}
assert_no_err(close(interior_file_src_fd));
assert_no_err(close(exterior_file_src_fd));
(void)removefile(exterior_dir_src, NULL, REMOVEFILE_RECURSIVE);
(void)removefile(exterior_dir_dst, NULL, REMOVEFILE_RECURSIVE);
return success ? EXIT_SUCCESS : EXIT_FAILURE;
}
bool do_fcopyfile_offset_test(const char *apfs_test_directory, size_t block_size) {
int src_fd, sparse_copy_fd, full_copy_fd, test_file_id;
char out_name[BSIZE_B], sparse_copy_name[BSIZE_B], full_copy_name[BSIZE_B];
bool success = true;
printf("START [fcopyfile_offset]\n");
// Make new names for this file and its copies.
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, 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, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM);
assert_with_errno(sparse_copy_fd >= 0);
// Seek the sparse copy to a non-zero offset.
assert_with_errno(lseek(sparse_copy_fd, block_size, SEEK_SET) == (off_t) block_size);
// Write into the sparse copy a different byte.
assert_with_errno(pwrite(sparse_copy_fd, "z", 1, block_size) == 1);
// Run fcopyfile().
assert_no_err(fcopyfile(src_fd, sparse_copy_fd, NULL, COPYFILE_ALL|COPYFILE_DATA_SPARSE));
// Check that the source matches the copy at the appropriate region.
success &= verify_fd_contents(src_fd, 0, sparse_copy_fd, block_size, 4 * 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, 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);
assert_with_errno(pwrite(full_copy_fd, "r", 1, block_size) == 1);
// Run fcopyfile().
assert_no_err(fcopyfile(src_fd, full_copy_fd, NULL, COPYFILE_ALL));
// Check that the source matches the copy at the appropriate region.
success &= verify_fd_contents(src_fd, 0, full_copy_fd, block_size, 4 * block_size);
if (success) {
printf("PASS [fcopyfile_offset]\n");
} else {
printf("FAIL [fcopyfile_offset]\n");
}
assert_no_err(close(full_copy_fd));
assert_no_err(close(sparse_copy_fd));
assert_no_err(close(src_fd));
(void)removefile(full_copy_name, NULL, 0);
(void)removefile(sparse_copy_name, NULL, 0);
(void)removefile(out_name, NULL, 0);
return success ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,16 @@
//
// sparse_test.h
// copyfile_test
//
#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);
#endif /* sparse_test_h */

View File

@ -0,0 +1,189 @@
//
// 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;
}
bool do_preserve_dst_tracked_test(const char *test_directory, __unused size_t block_size) {
char file_src[BSIZE_B] = {0}, file_dst[BSIZE_B] = {0};
off_t src_fsize = 0x1000;
int test_file_id;
struct stat dst_stb;
bool success = true;
printf("START [preserve_dst_tracked]\n");
// Create source file
assert_with_errno(snprintf(file_src, BSIZE_B, "%s/" TEST_FILE_NAME, test_directory) > 0);
assert_no_err(close(open(file_src, O_CREAT|O_EXCL, 0644)));
assert_no_err(truncate(file_src, src_fsize));
// Create destination file
test_file_id = rand() % DEFAULT_NAME_MOD;
assert_with_errno(snprintf(file_dst, BSIZE_B, "%s/%s.%d", test_directory, TEST_FILE_NAME, test_file_id) > 0);
assert_no_err(close(open(file_dst, O_CREAT|O_EXCL, 0644)));
// Track destination file
assert_no_err(chflags(file_dst, UF_TRACKED));
// Try to copy src onto destination
assert_no_err(copyfile(file_src, file_dst, NULL, COPYFILE_DATA|COPYFILE_STAT|COPYFILE_PRESERVE_DST_TRACKED));
assert_no_err(stat(file_dst, &dst_stb));
success &= (dst_stb.st_size == src_fsize);
success &= (dst_stb.st_flags & UF_TRACKED);
if (success) {
printf("PASS [preserve_dst_tracked]\n");
} else {
printf("FAIL [preserve_dst_tracked]\n");
}
(void)unlink(file_src);
(void)unlink(file_dst);
return success ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,15 @@
//
// 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);
bool do_preserve_dst_tracked_test(const char *test_directory, size_t block_size);
#endif /* stat_test_h */

View File

@ -0,0 +1,68 @@
//
// systemx.c
// copyfile_test
// Stolen from the test routines from the apfs project.
//
#include <fcntl.h>
#include <libgen.h>
#include <spawn.h>
#include <stdarg.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/stat.h>
#include "systemx.h"
#include "test_utils.h"
int __attribute__((sentinel)) systemx(const char *prog, ...)
{
const char *args[64];
va_list ap;
const char **parg = args;
*parg++ = basename((char *)prog);
va_start(ap, prog);
bool quiet = false, quiet_stderr = false;
while ((*parg = va_arg(ap, char *))) {
if (*parg == SYSTEMX_QUIET) {
quiet = true;
} else if (*parg == SYSTEMX_QUIET_STDERR)
quiet_stderr = true;
else
++parg;
}
va_end(ap);
posix_spawn_file_actions_t facts, *pfacts = NULL;
if (quiet || quiet_stderr) {
posix_spawn_file_actions_init(&facts);
if (quiet)
posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
if (quiet_stderr)
posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null", O_APPEND, 0);
pfacts = &facts;
}
pid_t pid;
assert_no_err(errno = posix_spawn(&pid, prog, pfacts, NULL,
(char * const *)args, NULL));
if (pfacts)
posix_spawn_file_actions_destroy(pfacts);
int status;
assert(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
if (WIFEXITED(status))
return WEXITSTATUS(status);
else
return -1;
}

View File

@ -0,0 +1,15 @@
//
// systemx.h
// copyfile_test
// Stolen from the test routines from the apfs project.
//
#ifndef systemx_h_
#define systemx_h_
#define SYSTEMX_QUIET ((void *)1)
#define SYSTEMX_QUIET_STDERR ((void *)2)
int __attribute__((sentinel)) systemx(const char *prog, ...);
#endif /* systemx_h_ */

View File

@ -0,0 +1,160 @@
//
// test_utils.c
// copyfile_test
//
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <removefile.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/stat.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.
char orig_contents[length], copy_contents[length];
bool equal;
assert(orig_fd > 0 && copy_fd > 0);
memset(orig_contents, 0, length);
memset(copy_contents, 0, length);
// Read the contents into our temporary buffers, and call memcmp().
errno = 0;
ssize_t pread_res = pread(orig_fd, orig_contents, length, 0);
assert_with_errno(pread_res == (off_t) length);
assert_with_errno(pread(copy_fd, copy_contents, length, copy_pos) >= 0);
equal = (memcmp(orig_contents, copy_contents, length) == 0);
if (!equal) {
printf("original fd (%lld - %lld) did not match copy (%lld - %lld)\n",
orig_pos, orig_pos + length, copy_pos, copy_pos + length);
// Find the first non-matching byte and print it out.
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, (unsigned long long)orig_contents[bad_off],
(unsigned long long)copy_contents[bad_off]);
break;
}
}
}
return equal;
}
bool verify_copy_contents(const char *orig_name, const char *copy_name) {
// Verify that the copy and the source have identical contents.
// Here, we just call out to 'diff' to do the work for us.
int rc = systemx(DIFF_PATH, orig_name, copy_name, SYSTEMX_QUIET, SYSTEMX_QUIET_STDERR, NULL);
if (rc != 0) {
printf("%s and %s are not identical: diff returned %d\n", orig_name, copy_name, rc);
exit(1);
}
return !rc;
}
bool verify_copy_sizes(struct stat *orig_sb, struct stat *copy_sb, copyfile_state_t cpf_state,
bool do_sparse, off_t src_start) {
off_t cpf_bytes_copied, blocks_offset;
bool result = true;
// 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 (%lld) != copy size (%lld)\n",
orig_sb->st_size - src_start, copy_sb->st_size);
result = false;
}
blocks_offset = src_start / S_BLKSIZE;
if (orig_sb->st_blocks - blocks_offset < copy_sb->st_blocks) {
printf("original blocks - offset (%lld) < copy blocks (%lld)\n",
orig_sb->st_blocks - blocks_offset, copy_sb->st_blocks);
result = false;
}
}
// Verify that the copyfile_state_t for the copy returns that all bytes were copied.
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 (%lld) != copied bytes (%lld)\n",
orig_sb->st_size - src_start, cpf_bytes_copied);
result = false;
}
}
return result;
}
int create_hole_in_fd(int fd, off_t offset, off_t length) {
struct fpunchhole punchhole_args = {
.fp_flags = 0,
.reserved = 0,
.fp_offset = offset,
.fp_length = length
};
return fcntl(fd, F_PUNCHHOLE, &punchhole_args);
}
void create_test_file_name(const char *dir, const char *postfix, int id, char *string_out) {
// Make a name for this new file and put it in out_name, which should be BSIZE_B bytes.
assert_with_errno(snprintf(string_out, BSIZE_B, "%s/testfile-%d.%s", dir, id, postfix) > 0);
}
void disk_image_create(const char *fstype, size_t size_in_mb) {
char size[BSIZE_B];
// Set up good default values.
if (!fstype) {
fstype = DEFAULT_FSTYPE;
}
if (size_in_mb > MAX_DISK_IMAGE_SIZE_MB) {
size_in_mb = MAX_DISK_IMAGE_SIZE_MB;
}
assert_with_errno(snprintf(size, BSIZE_B, "%zum", size_in_mb) >= 3);
// Unmount and remove the sparseimage if it already exists.
disk_image_destroy(true);
if (removefile(DISK_IMAGE_PATH, NULL, REMOVEFILE_RECURSIVE) < 0) {
assert_with_errno(errno == ENOENT);
}
// Make the disk image.
assert_no_err(systemx(HDIUTIL_PATH, SYSTEMX_QUIET, "create", "-fs", fstype,
"-size", size, "-type", "SPARSE", "-volname", "apfs_sparse",
DISK_IMAGE_PATH, NULL));
// Attach the disk image.
assert_no_err(systemx(HDIUTIL_PATH, SYSTEMX_QUIET, "attach", DISK_IMAGE_PATH, NULL));
}
void disk_image_destroy(bool allow_failure) {
// If the caller allows, ignore any failures (also silence stderr).
if (allow_failure) {
systemx(HDIUTIL_PATH, "eject", MOUNT_PATH, SYSTEMX_QUIET, SYSTEMX_QUIET_STDERR, NULL);
} else {
assert_no_err(systemx(HDIUTIL_PATH, "eject", MOUNT_PATH, SYSTEMX_QUIET, NULL));
}
}

View File

@ -0,0 +1,125 @@
//
// test_utils.h
// copyfile_test
// Based on the test routines from the apfs project.
//
#ifndef test_utils_h
#define test_utils_h
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include "../copyfile.h"
#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 APFS_FSTYPE "apfs"
// We assume that we're mounted on /Volumes.
#define MOUNT_PATH "/Volumes/" VOLUME_NAME
#define HDIUTIL_PATH "/usr/bin/hdiutil"
#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,
bool do_sparse, off_t src_start);
int create_hole_in_fd(int fd, off_t offset, off_t length);
void create_test_file_name(const char *dir, const char *postfix, int id, char *string_out);
// Our disk image test functions.
void disk_image_create(const char *fstype, size_t size_in_mb);
void disk_image_destroy(bool allow_failure);
// Assertion functions/macros for tests.
static inline void
__attribute__((format (printf, 3, 4)))
__attribute__((noreturn))
assert_fail_(const char *file, int line, const char *assertion, ...)
{
va_list args;
va_start(args, assertion);
char *msg;
vasprintf(&msg, assertion, args);
va_end(args);
printf("\n%s:%u: error: %s\n", file, line, msg);
exit(1);
}
#define assert_fail(str, ...) \
assert_fail_(__FILE__, __LINE__, str, ## __VA_ARGS__)
#undef assert
#define assert(condition) \
do { \
if (!(condition)) \
assert_fail_(__FILE__, __LINE__, \
"assertion failed: %s", #condition); \
} while (0)
#define assert_with_errno_(condition, condition_str) \
do { \
if (!(condition)) \
assert_fail_(__FILE__, __LINE__, "%s failed: %s", \
condition_str, strerror(errno)); \
} while (0)
#define assert_with_errno(condition) \
assert_with_errno_((condition), #condition)
#define assert_no_err(condition) \
assert_with_errno_(!(condition), #condition)
#define assert_equal(lhs, rhs, fmt) \
do { \
typeof (lhs) lhs_ = (lhs); \
typeof (lhs) rhs_ = (rhs); \
if (lhs_ != rhs_) \
assert_fail(#lhs " (" fmt ") != " #rhs " (" fmt ")", \
lhs_, rhs_); \
} while (0)
#define assert_equal_(lhs, rhs, lhs_str, rhs_str, fmt) \
do { \
typeof (lhs) lhs_ = (lhs); \
typeof (lhs) rhs_ = (rhs); \
if (lhs_ != rhs_) \
assert_fail(lhs_str " (" fmt ") != " rhs_str " (" fmt ")", \
lhs_, rhs_); \
} while (0)
#define assert_equal_int(lhs, rhs) assert_equal_(lhs, rhs, #lhs, #rhs, "%d")
#define assert_equal_ll(lhs, rhs) assert_equal_(lhs, rhs, #lhs, #rhs, "%lld")
#define assert_equal_str(lhs, rhs) \
do { \
const char *lhs_ = (lhs), *rhs_ = (rhs); \
if (strcmp(lhs_, rhs_)) \
assert_fail("\"%s\" != \"%s\"", lhs_, rhs_); \
} while (0)
#define ignore_eintr(x, error_val) \
({ \
typeof(x) eintr_ret_; \
do { \
eintr_ret_ = (x); \
} while (eintr_ret_ == (error_val) && errno == EINTR); \
eintr_ret_; \
})
#endif /* test_utils_h */

View File

@ -58,6 +58,7 @@ defaultUnboxedPropertyTable[] = {
{ "com.apple.security.", "S", propFlagsPrefix },
{ XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save
{ XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork
{ "com.apple.root.installed", "PC", 0}, // Don't share or sync. Copyable by entitled callers
{ 0, 0, 0 },
};
@ -69,6 +70,7 @@ defaultSandboxedPropertyTable[] = {
{ "com.apple.security.", "N", propFlagsPrefix },
{ XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save
{ XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork
{ "com.apple.root.installed", "PC", 0}, // Don't share or sync. Copyable by entitled callers
{ 0, 0, 0 },
};

View 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@
*

View File

@ -1,7 +1,7 @@
.\"
.\" Copyright (c) 2013 Apple Computer, Inc. All rights reserved.
.\"
.Dd October 7, 2013
.Dd October 9, 2018
.Dt XATTR_NAME_WITH_FLAGS 3
.Os
.Sh NAME
@ -10,7 +10,7 @@
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In xattr_properties.h
.In xattr_flags.h
.Ft int
.Fn xattr_preserve_for_intent "const char *" "xattr_operation_intent_t"
.Ft char *
@ -56,7 +56,7 @@ argument indicates it should not be preserved for the given
intent, or 1 if it should.
.Pp
The function
.Fn xattr_presere_for_intent
.Fn xattr_preserve_for_intent
combines the functions above, and will return zero if the
named extended attribute should be preserved during a copy for
the given intent.
@ -73,14 +73,14 @@ case.
.It Dv XATTR_OPERATION_INTENT_SAVE
Indicates that intent is to perform a save (perhaps as in a "safe save").
This differs from a copy in that the content may be changing; the destination
may be over-writing or replacing the source, and som extended attributes should
may be over-writing or replacing the source, and some extended attributes should
not be preserved during this process.
.It Dv XATTR_OPERATION_INTENT_SHARE
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

View 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 */

View File

@ -0,0 +1,12 @@
#include "<DEVELOPER_DIR>/Makefiles/CoreOS/Xcode/BSD.xcconfig"
INSTALL_PATH = /usr/lib/system
PUBLIC_HEADERS_FOLDER_PATH = /usr/include
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include
BUILD_VARIANTS = normal debug
VERSION_INFO_PREFIX = __attribute__((visibility("hidden")))
IS_ZIPPERED = YES
PRODUCT_NAME = copyfile

View File

@ -0,0 +1,40 @@
#!/bin/sh
set -e -x
# don't install man pages for installhdrs or iOS builds
if [ "$ACTION" = installhdrs ]; then exit 0; fi
if [ "${PLATFORM_NAME/iphone/}" != "${PLATFORM_NAME}" ]; then exit 0; fi
function InstallManPages() {
for MANPAGE in "$@"; do
SECTION=`basename "${MANPAGE/*./}"`
MANDIR="$DSTROOT"/usr/share/man/man"$SECTION"
install -d -o "$INSTALL_OWNER" -g "$INSTALL_GROUP" -m 0755 "$MANDIR"
install -o "$INSTALL_OWNER" -g "$INSTALL_GROUP" -m 0444 "$MANPAGE" "$MANDIR"
done
}
function LinkManPages() {
MANPAGE=`basename "$1"`
SECTION=`basename "${MANPAGE/*./}"`
MANDIR="$DSTROOT"/usr/share/man/man"$SECTION"
shift
for LINK in "$@"; do
ln -hf "$MANDIR/$MANPAGE" "$MANDIR/$LINK"
done
}
InstallManPages copyfile.3
LinkManPages copyfile.3 \
fcopyfile.3 \
copyfile_state_alloc.3 \
copyfile_state_free.3 \
copyfile_state_get.3 \
copyfile_state_set.3
InstallManPages xattr_name_with_flags.3
LinkManPages xattr_name_with_flags.3 \
xattr_name_without_flags.3 \
xattr_flags_from_name.3 \
xattr_intent_with_flags.3