mirror of
https://github.com/darlinghq/darling.git
synced 2024-10-07 01:04:08 +00:00
copyfile-166.40.1
This commit is contained in:
parent
139d10b1f5
commit
ce78db7998
@ -1 +0,0 @@
|
||||
../../kernel-include/
|
@ -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
@ -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
|
417
src/copyfile/copyfile.xcodeproj/project.pbxproj
Normal file
417
src/copyfile/copyfile.xcodeproj/project.pbxproj
Normal 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 */;
|
||||
}
|
@ -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 */
|
BIN
src/copyfile/copyfile_test/copyfile_test.entitlements
Normal file
BIN
src/copyfile/copyfile_test/copyfile_test.entitlements
Normal file
Binary file not shown.
68
src/copyfile/copyfile_test/main.c
Normal file
68
src/copyfile/copyfile_test/main.c
Normal 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;
|
||||
}
|
397
src/copyfile/copyfile_test/sparse_test.c
Normal file
397
src/copyfile/copyfile_test/sparse_test.c
Normal 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(©_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, ©_sb));
|
||||
result &= verify_copy_sizes(&orig_sb, ©_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(©_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, ©_sb));
|
||||
result &= verify_copy_sizes(&orig_sb, ©_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;
|
||||
}
|
16
src/copyfile/copyfile_test/sparse_test.h
Normal file
16
src/copyfile/copyfile_test/sparse_test.h
Normal 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 */
|
189
src/copyfile/copyfile_test/stat_test.c
Normal file
189
src/copyfile/copyfile_test/stat_test.c
Normal 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;
|
||||
}
|
15
src/copyfile/copyfile_test/stat_test.h
Normal file
15
src/copyfile/copyfile_test/stat_test.h
Normal 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 */
|
68
src/copyfile/copyfile_test/systemx.c
Normal file
68
src/copyfile/copyfile_test/systemx.c
Normal 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;
|
||||
}
|
15
src/copyfile/copyfile_test/systemx.h
Normal file
15
src/copyfile/copyfile_test/systemx.h
Normal 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_ */
|
160
src/copyfile/copyfile_test/test_utils.c
Normal file
160
src/copyfile/copyfile_test/test_utils.c
Normal 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));
|
||||
}
|
||||
}
|
125
src/copyfile/copyfile_test/test_utils.h
Normal file
125
src/copyfile/copyfile_test/test_utils.h
Normal 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 */
|
@ -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 },
|
||||
};
|
||||
|
||||
|
@ -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@
|
||||
*
|
@ -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
|
||||
|
@ -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 */
|
12
src/copyfile/xcodescripts/copyfile.xcconfig
Normal file
12
src/copyfile/xcodescripts/copyfile.xcconfig
Normal 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
|
40
src/copyfile/xcodescripts/install_files.sh
Normal file
40
src/copyfile/xcodescripts/install_files.sh
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user