From dcb8fd50e51cba13bdc95a405d909e51538f0d1d Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 15 Sep 2017 20:18:15 +0000 Subject: [PATCH] copyfile-146.30.2.tar.gz --- copyfile.3 | 42 +++++++++++++++++++++++++------ copyfile.c | 72 +++++++++++++++++++++++++++++------------------------- 2 files changed, 74 insertions(+), 40 deletions(-) diff --git a/copyfile.3 b/copyfile.3 index d66502e..4233ce9 100644 --- a/copyfile.3 +++ b/copyfile.3 @@ -1,7 +1,7 @@ .\" .\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved. .\" -.Dd February 27, 2017 +.Dd August 30, 2017 .Dt COPYFILE 3 .Os .Sh NAME @@ -181,8 +181,9 @@ 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). -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 @@ -191,10 +192,12 @@ 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). -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.) @@ -438,6 +441,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 , @@ -524,6 +547,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 diff --git a/copyfile.c b/copyfile.c index 71c3d9a..91e4d9a 100644 --- a/copyfile.c +++ b/copyfile.c @@ -607,6 +607,10 @@ reset_security(copyfile_state_t s) * 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. * */ @@ -645,7 +649,7 @@ copytree(copyfile_state_t s) 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; @@ -739,8 +743,9 @@ copytree(copyfile_state_t s) // 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; } @@ -829,6 +834,8 @@ copytree(copyfile_state_t s) 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) { @@ -1005,23 +1012,20 @@ int fcopyfile(int src_fd, int dst_fd, copyfile_state_t state, copyfile_flags_t f * 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. @@ -1033,14 +1037,14 @@ static int copyfile_clone(const char *src, const char *dst, copyfile_state_t sta * 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 @@ -1051,16 +1055,15 @@ static int copyfile_clone(const char *src, const char *dst, copyfile_state_t sta * 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); } } } @@ -1091,21 +1094,6 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_ 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; @@ -1149,6 +1137,24 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_ 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 */ -- 2.47.2