.\"
.\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
.\"
-.Dd April 27, 2006
+.Dd February 27, 2017
.Dt COPYFILE 3
.Os
.Sh NAME
.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
or
.Va to
parameter to
-.Fn copyfile
+.Fn fcopyfile
was a negative number.
.It Bq Er ENOMEM
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.
/*
* 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
#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
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);
if (fts)
fts_close(fts);
+ copyfile_debug(1, "returning: %d errno %d\n", retval, errno);
return retval;
}
copyfile_state_free(s);
errno = t;
}
+ if (ret >= 0) {
+ errno = 0;
+ }
return ret;
-
}
/*
*/
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
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.
(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;
}
}
/*
- * 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.
*/
*/
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
/* 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;
}
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");
}
}
- 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
/*
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 {
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;
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)