.\"
.\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
.\"
-.Dd February 27, 2017
+.Dd August 30, 2017
.Dt COPYFILE 3
.Os
.Sh NAME
.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).
-Note that if cloning is successful, callbacks will not be invoked.
+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.
(This is only applicable for the
.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).
-Note that if cloning is successful, callbacks will not be invoked.
-Note also that there is no support for cloning directories: if a directory is provided as the source,
-this will instead copy the directory (recursively if COPYFILE_RECURSIVE is also passed).
+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. Recursive copying however is
+supported, see below for more information.
(This is only applicable for the
.Fn copyfile
function.)
.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 ,
.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
* copytree() is called from copyfile() -- but copytree() itself then calls
* copyfile() to copy each individual object.
*
+ * If COPYFILE_CLONE is passed, copytree() will clone (instead of copy)
+ * regular files and symbolic links found in each directory.
+ * Directories will still be copied normally.
+ *
* XXX - no effort is made to handle overlapping hierarchies at the moment.
*
*/
goto done;
}
- flags = s->flags & (COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_VERBOSE | COPYFILE_EXCL);
+ flags = s->flags & (COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_VERBOSE | COPYFILE_EXCL | COPYFILE_CLONE);
paths[0] = src = s->src;
dst = s->dst;
// COPYFILE_RECURSIVE is always done physically: see 11717978.
fts_flags |= FTS_PHYSICAL;
- if (!(s->flags & COPYFILE_NOFOLLOW_SRC)) {
- // Follow 'src', even if it's a symlink.
+ if (!(s->flags & (COPYFILE_NOFOLLOW_SRC|COPYFILE_CLONE))) {
+ // Follow 'src', even if it's a symlink, unless instructed not to
+ // or we're cloning, where we never follow symlinks.
fts_flags |= FTS_COMFOLLOW;
}
goto stopit;
}
}
+ // Since we don't support cloning directories this code depends on copyfile()
+ // falling back to a regular directory copy.
int tmp_flags = (cmd == COPYFILE_RECURSE_DIR) ? (flags & ~COPYFILE_STAT) : flags;
rv = copyfile(ftsent->fts_path, dstfile, tstate, tmp_flags);
if (rv < 0) {
* however, in case of best try flag, we fallback to the copy method.
*/
-static int copyfile_clone(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags)
+static int copyfile_clone(copyfile_state_t state)
{
int ret = 0;
- int cloneFlags = 0;
+ // Since we don't allow cloning of directories, we must also forbid
+ // cloning the target of symlinks (since that may be a directory).
+ int cloneFlags = CLONE_NOFOLLOW;
struct stat src_sb;
- if (lstat(src, &src_sb) != 0)
+ if (lstat(state->src, &src_sb) != 0)
{
errno = EINVAL;
return -1;
}
- if (COPYFILE_NOFOLLOW & flags)
- {
- cloneFlags = CLONE_NOFOLLOW;
- }
-
/*
* Support only for files and symbolic links.
* TODO:Remove this check when support for directories is added.
* before we create it. We don't care if the file doesn't
* exist, so we ignore ENOENT.
*/
- if (flags & COPYFILE_UNLINK)
+ if (state->flags & COPYFILE_UNLINK)
{
- if (remove(dst) < 0 && errno != ENOENT)
+ if (remove(state->dst) < 0 && errno != ENOENT)
{
return -1;
}
}
- ret = clonefileat(AT_FDCWD, src, AT_FDCWD, dst, cloneFlags);
+ ret = clonefileat(AT_FDCWD, state->src, AT_FDCWD, state->dst, cloneFlags);
if (ret == 0) {
/*
* We could also report the size of the single
* and let the caller figure out how they want to
* deal.
*/
- if (state != NULL)
- state->was_cloned = true;
+ state->was_cloned = true;
/*
* COPYFILE_MOVE tells us to attempt removing
* the source file after the copy, and to
* ignore any errors returned by remove(3).
*/
- if (flags & COPYFILE_MOVE) {
- (void)remove(src);
+ if (state->flags & COPYFILE_MOVE) {
+ (void)remove(state->src);
}
}
}
return -1;
}
- if (flags & (COPYFILE_CLONE_FORCE | COPYFILE_CLONE))
- {
- ret = copyfile_clone(src, dst, state, flags);
- if (ret == 0) {
- goto exit;
- } else if (flags & COPYFILE_CLONE_FORCE) {
- goto error_exit;
- }
- // cloning failed. Inherit clonefile flags required for
- // falling back to copyfile.
- flags = flags | (COPYFILE_EXCL | COPYFILE_ACL |
- COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA);
- flags = flags & (~COPYFILE_CLONE);
- }
- ret = 0;
if (copyfile_preamble(&s, flags) < 0)
{
return -1;
goto exit;
}
+ if (s->flags & (COPYFILE_CLONE_FORCE | COPYFILE_CLONE))
+ {
+ ret = copyfile_clone(s);
+ if (ret == 0) {
+ goto exit;
+ } else if (s->flags & COPYFILE_CLONE_FORCE) {
+ goto error_exit;
+ }
+ // cloning failed. Inherit clonefile flags required for
+ // falling back to copyfile.
+ s->flags |= (COPYFILE_ACL | COPYFILE_EXCL | COPYFILE_NOFOLLOW_SRC |
+ COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA);
+
+ s->flags &= ~COPYFILE_CLONE;
+ flags = s->flags;
+ ret = 0;
+ }
+
/*
* Get a copy of the source file's security settings
*/