From: Apple Date: Tue, 6 Jun 2017 16:46:43 +0000 (+0000) Subject: copyfile-146.tar.gz X-Git-Tag: macos-1013^0 X-Git-Url: https://git.saurik.com/apple/copyfile.git/commitdiff_plain/f475fe667ad53cee92f5333e91ff2f8d517e91cd copyfile-146.tar.gz --- diff --git a/copyfile.3 b/copyfile.3 index 93ae95d..d66502e 100644 --- a/copyfile.3 +++ b/copyfile.3 @@ -1,7 +1,7 @@ .\" .\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved. .\" -.Dd April 27, 2006 +.Dd February 27, 2017 .Dt COPYFILE 3 .Os .Sh NAME @@ -179,18 +179,28 @@ file before starting. (This is only applicable for the .Fn copyfile function.) .It Dv COPYFILE_CLONE_FORCE -Clone the file/directory instead. +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. +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 +.Fn copyfile +function.) .It Dv COPYFILE_CLONE -Try to clone the file/directory instead. +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 is only applicable for the +.Fn copyfile +function.) .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. .El @@ -566,7 +576,7 @@ The or .Va to parameter to -.Fn copyfile +.Fn fcopyfile was a negative number. .It Bq Er ENOMEM A memory allocation failed. @@ -574,7 +584,32 @@ A memory allocation failed. The source file was not a directory, symbolic link, or regular file. .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. diff --git a/copyfile.c b/copyfile.c index ed3bfce..71c3d9a 100644 --- a/copyfile.c +++ b/copyfile.c @@ -94,7 +94,7 @@ enum cfInternalFlags { /* * The state structure keeps track of * the source filename, the destination filename, their - * associated file-descriptors, the stat infomration for the + * associated file-descriptors, the stat information for the * source file, the security information for the source file, * the flags passed in for the copy, a pointer to place statistics * (not currently implemented), debug flags, and a pointer to callbacks @@ -310,21 +310,35 @@ static int copyfile_quarantine(copyfile_state_t); #define COPYFILE_DEBUG (1<<31) #define COPYFILE_DEBUG_VAR "COPYFILE_DEBUG" +// These macros preserve the value of errno. #ifndef _COPYFILE_TEST -# define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__) +# define copyfile_warn(str, ...) \ + do { \ + errno_t _errsv = errno; \ + syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__); \ + errno = _errsv; \ + } while (0) # define copyfile_debug(d, str, ...) \ do { \ if (s && (d <= s->debug)) {\ + errno_t _errsv = errno; \ syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ + errno = _errsv; \ } \ } while (0) #else #define copyfile_warn(str, ...) \ - fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "") + do { \ + errno_t _errsv = errno; \ + fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : ""); \ + errno = _errsv; \ + } while (0) # define copyfile_debug(d, str, ...) \ do { \ if (s && (d <= s->debug)) {\ + errno_t _errsv = errno; \ fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ + errno = _errsv; \ } \ } while(0) #endif @@ -723,10 +737,12 @@ copytree(copyfile_state_t s) offset = strlen(src); } - if (s->flags | COPYFILE_NOFOLLOW_SRC) - fts_flags |= FTS_PHYSICAL; - else - fts_flags |= FTS_LOGICAL; + // 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. + fts_flags |= FTS_COMFOLLOW; + } fts = fts_open((char * const *)paths, fts_flags, NULL); @@ -891,6 +907,7 @@ done: if (fts) fts_close(fts); + copyfile_debug(1, "returning: %d errno %d\n", retval, errno); return retval; } @@ -968,9 +985,11 @@ int fcopyfile(int src_fd, int dst_fd, copyfile_state_t state, copyfile_flags_t f copyfile_state_free(s); errno = t; } + if (ret >= 0) { + errno = 0; + } return ret; - } /* @@ -1034,6 +1053,15 @@ static int copyfile_clone(const char *src, const char *dst, copyfile_state_t sta */ if (state != NULL) 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); + } } } else @@ -1066,9 +1094,10 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_ if (flags & (COPYFILE_CLONE_FORCE | COPYFILE_CLONE)) { ret = copyfile_clone(src, dst, state, flags); - if ((ret == 0) || (flags & COPYFILE_CLONE_FORCE)) - { - return ret; + 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. @@ -1200,17 +1229,21 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_ (void)remove(s->src); exit: + if (ret >= 0) { + errno = 0; + } + copyfile_debug(5, "returning %d errno %d\n", ret, errno); + if (state == NULL) { int t = errno; copyfile_state_free(s); errno = t; } - return ret; error_exit: ret = -1; - if (s->err) { + if (s && s->err) { errno = s->err; s->err = 0; } @@ -1379,7 +1412,7 @@ static int copyfile_internal(copyfile_state_t s, copyfile_flags_t flags) } /* - * Simialr to above, this tells us whether or not to copy + * Similar to above, this tells us whether or not to copy * the non-meta data portion of the file. We attempt to * remove (via unlink) the destination file if we fail. */ @@ -2315,9 +2348,14 @@ error_exit: */ static int copyfile_stat(copyfile_state_t s) { - struct timeval tval[2]; unsigned int added_flags = 0, dst_flags = 0; + struct attrlist attrlist; struct stat dst_sb; + struct { + /* Order of these structs matters for setattrlist. */ + struct timespec mod_time; + struct timespec acc_time; + } ma_times; /* * NFS doesn't support chflags; ignore errors as a result, since @@ -2345,10 +2383,13 @@ static int copyfile_stat(copyfile_state_t s) /* This may have already been done in copyfile_security() */ (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT); - tval[0].tv_sec = s->sb.st_atime; - tval[1].tv_sec = s->sb.st_mtime; - tval[0].tv_usec = tval[1].tv_usec = 0; - (void)futimes(s->dst_fd, tval); + /* Try to set m/atimes using setattrlist(), for nanosecond precision. */ + memset(&attrlist, 0, sizeof(attrlist)); + attrlist.bitmapcount = ATTR_BIT_MAP_COUNT; + attrlist.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME; + ma_times.mod_time = s->sb.st_mtimespec; + ma_times.acc_time = s->sb.st_atimespec; + (void)fsetattrlist(s->dst_fd, &attrlist, &ma_times, sizeof(ma_times), 0); return 0; } @@ -2794,15 +2835,19 @@ struct {char *s; int v;} opts[] = { COPYFILE_OPTION(PACK) COPYFILE_OPTION(UNPACK) COPYFILE_OPTION(CHECK) + COPYFILE_OPTION(CLONE) + COPYFILE_OPTION(CLONE_FORCE) COPYFILE_OPTION(VERBOSE) + COPYFILE_OPTION(RECURSIVE) COPYFILE_OPTION(DEBUG) {NULL, 0} }; int main(int c, char *v[]) { - int i; + int i, ret; int flags = 0; + copyfile_state_t state = NULL; if (c < 3) errx(1, "insufficient arguments"); @@ -2820,7 +2865,16 @@ int main(int c, char *v[]) } } - return copyfile(v[1], v[2], NULL, flags); + if (flags & COPYFILE_DEBUG) { + state = copyfile_state_alloc(); + state->debug = 10; // Turn on all debug statements + } + ret = copyfile(v[1], v[2], state, flags); + if (state) { + (void)copyfile_state_free(state); + } + + return ret; } #endif /* @@ -3463,7 +3517,9 @@ static int copyfile_unpack(copyfile_state_t s) error = -1; goto exit; } - strlcpy(tmpstr, tcp, entry->length + 1); + // Can't use strlcpy here: tcp is not NUL-terminated! + memcpy(tmpstr, tcp, entry->length); + tmpstr[entry->length] = 0; acl = acl_from_text(tmpstr); free(tmpstr); } else { @@ -3639,7 +3695,12 @@ skip_fi: size_t length; off_t offset; struct stat sb; - struct timeval tval[2]; + struct attrlist attrlist; + struct { + /* Order of these structs matters for setattrlist. */ + struct timespec mod_time; + struct timespec acc_time; + } ma_times; length = adhdr->entries[1].length; offset = adhdr->entries[1].offset; @@ -3739,12 +3800,15 @@ skip_fi: if (!(s->flags & COPYFILE_STAT)) { - tval[0].tv_sec = sb.st_atime; - tval[1].tv_sec = sb.st_mtime; - tval[0].tv_usec = tval[1].tv_usec = 0; - - if (futimes(s->dst_fd, tval)) + /* Try to set m/atimes using setattrlist(), for nanosecond precision. */ + memset(&attrlist, 0, sizeof(attrlist)); + attrlist.bitmapcount = ATTR_BIT_MAP_COUNT; + attrlist.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME; + ma_times.mod_time = sb.st_mtimespec; + ma_times.acc_time = sb.st_atimespec; + if (fsetattrlist(s->dst_fd, &attrlist, &ma_times, sizeof(ma_times), 0) != 0) { copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)"); + } } bad: if (rsrcforkdata) diff --git a/xattr_name_with_flags.3 b/xattr_name_with_flags.3 index e8b6b76..0840430 100644 --- a/xattr_name_with_flags.3 +++ b/xattr_name_with_flags.3 @@ -1,7 +1,7 @@ .\" .\" Copyright (c) 2013 Apple Computer, Inc. All rights reserved. .\" -.Dd October 7, 2013 +.Dd December 21, 2016 .Dt XATTR_NAME_WITH_FLAGS 3 .Os .Sh NAME @@ -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,7 +73,7 @@ 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,