]> git.saurik.com Git - apple/copyfile.git/commitdiff
copyfile-146.30.2.tar.gz macos-10132 macos-10133 v146.30.2
authorApple <opensource@apple.com>
Fri, 15 Sep 2017 20:18:15 +0000 (20:18 +0000)
committerApple <opensource@apple.com>
Fri, 15 Sep 2017 20:18:15 +0000 (20:18 +0000)
copyfile.3
copyfile.c

index d66502eaec8f0c7df7c95f49969469953a545c71..4233ce9bcfdddf9d5864436ba4a4c7a2f8d597ee 100644 (file)
@@ -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
index 71c3d9ac135ed532a37d59522b83a0f3866827fd..91e4d9ae59936994fec751dcab61e4d92245bc1c 100644 (file)
@@ -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
         */