#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <sys/attr.h>
#include <err.h>
#include <errno.h>
void
setfile(const char *name, struct stat *fs)
{
- static struct timeval tv[2];
+ struct attrlist ts_req = {};
+ struct {
+ struct timespec mtime;
+ struct timespec atime;
+ } set_ts;
fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
- TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
- TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
- if (utimes(name, tv))
- cwarn("utimes: %s", name);
+ ts_req.bitmapcount = ATTR_BIT_MAP_COUNT;
+ ts_req.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
+ set_ts.mtime = fs->st_mtimespec;
+ set_ts.atime = fs->st_atimespec;
+
+ if (setattrlist(name, &ts_req, &set_ts, sizeof(set_ts), 0))
+ cwarn("setattrlist: %s", name);
/*
* Changing the ownership probably won't succeed, unless we're root
#include "extern.h"
#define cp_pct(x,y) (int)(100.0 * (double)(x) / (double)(y))
+/* Memory strategy threshold, in pages: if physmem is larger then this, use a
+ * large buffer */
+#define PHYSPAGES_THRESHOLD (32*1024)
+
+/* Maximum buffer size in bytes - do not allow it to grow larger than this */
+#define BUFSIZE_MAX (2*1024*1024)
+
+/* Small (default) buffer size in bytes. It's inefficient for this to be
+ * smaller than MAXPHYS */
+#define BUFSIZE_SMALL (MAXPHYS)
+
int
copy_file(const FTSENT *entp, int dne)
{
- static char buf[MAXBSIZE];
+ static char *buf = NULL;
+ static size_t bufsize;
struct stat *fs;
int ch, checkch, from_fd, rval, to_fd;
ssize_t rcount;
} else
#endif
{
+ if (buf == NULL) {
+ /*
+ * Note that buf and bufsize are static. If
+ * malloc() fails, it will fail at the start
+ * and not copy only some files.
+ */
+ if (sysconf(_SC_PHYS_PAGES) >
+ PHYSPAGES_THRESHOLD)
+ bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
+ else
+ bufsize = BUFSIZE_SMALL;
+ buf = malloc(bufsize);
+ if (buf == NULL)
+ err(1, "Not enough memory");
+ }
wtotal = 0;
- while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+ while ((rcount = read(from_fd, buf, bufsize)) > 0) {
for (bufp = buf, wresid = rcount; ;
bufp += wcount, wresid -= wcount) {
wcount = write(to_fd, bufp, wresid);
int
setfile(struct stat *fs, int fd)
{
- static struct timeval tv[2];
+ struct attrlist ts_req = {};
struct stat ts;
int rval, gotstat, islink, fdval;
+ struct {
+ struct timespec mtime;
+ struct timespec atime;
+ } set_ts;
rval = 0;
fdval = fd != -1;
islink = !fdval && S_ISLNK(fs->st_mode);
fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO;
+ unsigned int options = islink ? FSOPT_NOFOLLOW : 0;
+
+ ts_req.bitmapcount = ATTR_BIT_MAP_COUNT;
+ ts_req.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
+ set_ts.mtime = fs->st_mtimespec;
+ set_ts.atime = fs->st_atimespec;
- TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
- TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
- if (fdval ? futimes(fd, tv) : (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv))) {
- warn("%sutimes: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path);
+ if (fdval ? fsetattrlist(fd, &ts_req, &set_ts, sizeof(set_ts), options) :
+ setattrlist(to.p_path, &ts_req, &set_ts, sizeof(set_ts), options)) {
+ warn("%ssetattrlist: %s", fdval ? "f" : "", to.p_path);
rval = 1;
}
if (fdval ? fstat(fd, &ts) : (islink ? lstat(to.p_path, &ts) :
isa = PBXAggregateTarget;
buildConfigurationList = FC8A8C8114B655ED001B97AD /* Build configuration list for PBXAggregateTarget "executables" */;
buildPhases = (
+ D178BEFA253DAE01001FC103 /* Copy plist */,
+ D178BF21253DAE2E001FC103 /* Copy tests */,
);
dependencies = (
FC8A8C8414B655FD001B97AD /* PBXTargetDependency */,
3E966CF01FB2218A0019F7A1 /* chgrp.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3E966CEB1FB2214F0019F7A1 /* chgrp.sh */; };
729D06D8230B5E42000716E5 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729D06D7230B5E42000716E5 /* CoreFoundation.framework */; };
7D0A20EA2499364700F0F6D7 /* metrics.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D0A20E92499364700F0F6D7 /* metrics.c */; };
+ D178BEFB253DAE2A001FC103 /* file_cmds.plist in Copy plist */ = {isa = PBXBuildFile; fileRef = 3E966CEC1FB2214F0019F7A1 /* file_cmds.plist */; };
+ D178BF22253DAE42001FC103 /* chgrp.sh in Copy tests */ = {isa = PBXBuildFile; fileRef = 3E966CEB1FB2214F0019F7A1 /* chgrp.sh */; };
+ D178BF48253DAE45001FC103 /* touch.sh in Copy tests */ = {isa = PBXBuildFile; fileRef = D11B5750253C22BD009A59BF /* touch.sh */; };
+ D1B4221F25762FC8003E3A47 /* cp.sh in Copy tests */ = {isa = PBXBuildFile; fileRef = D1B421D325762E9E003E3A47 /* cp.sh */; };
FC8A8A2814B6486E001B97AD /* chflags.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDCC14B6460C0070FACB /* chflags.c */; };
FC8A8BE414B6494B001B97AD /* chflags.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDCB14B6460C0070FACB /* chflags.1 */; };
FC8A8BE514B64958001B97AD /* chmod.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDD014B6460C0070FACB /* chmod.c */; };
);
runOnlyForDeploymentPostprocessing = 0;
};
+ D178BEFA253DAE01001FC103 /* Copy plist */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /AppleInternal/CoreOS/BATS/unit_tests;
+ dstSubfolderSpec = 0;
+ files = (
+ D178BEFB253DAE2A001FC103 /* file_cmds.plist in Copy plist */,
+ );
+ name = "Copy plist";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ D178BF21253DAE2E001FC103 /* Copy tests */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /AppleInternal/Tests/file_cmds;
+ dstSubfolderSpec = 0;
+ files = (
+ D1B4221F25762FC8003E3A47 /* cp.sh in Copy tests */,
+ D178BF48253DAE45001FC103 /* touch.sh in Copy tests */,
+ D178BF22253DAE42001FC103 /* chgrp.sh in Copy tests */,
+ );
+ name = "Copy tests";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
FC8A8B0F14B648D7001B97AD /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
729D06D7230B5E42000716E5 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
7D0A20E82499364700F0F6D7 /* metrics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metrics.h; sourceTree = "<group>"; };
7D0A20E92499364700F0F6D7 /* metrics.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = metrics.c; sourceTree = "<group>"; };
+ D11B5750253C22BD009A59BF /* touch.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = touch.sh; sourceTree = "<group>"; };
+ D1B421D325762E9E003E3A47 /* cp.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = cp.sh; sourceTree = "<group>"; };
FC8A8B1214B648D7001B97AD /* chmod */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chmod; sourceTree = BUILT_PRODUCTS_DIR; };
FC8A8B1A14B648E0001B97AD /* chown */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chown; sourceTree = BUILT_PRODUCTS_DIR; };
FC8A8B2214B648E3001B97AD /* cksum */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cksum; sourceTree = BUILT_PRODUCTS_DIR; };
children = (
3E966CEB1FB2214F0019F7A1 /* chgrp.sh */,
3E966CEC1FB2214F0019F7A1 /* file_cmds.plist */,
+ D11B5750253C22BD009A59BF /* touch.sh */,
+ D1B421D325762E9E003E3A47 /* cp.sh */,
);
path = tests;
sourceTree = "<group>";
SHA256_Path_XATTRs(char *path, char *buf) {
xattr_info *ai = NULL;
- if (mflag) {
+ // mflag is passed during manifest comparision while xflag is used to generate the specification
+ if (mflag || xflag) {
ai = get_xdstream_privateid(path, buf);
} else {
ai = calculate_SHA256_XATTRs(path, buf);
int supported;
struct timespec ptimespec = ptime(p->fts_accpath, &supported);
if (!supported) {
- LABEL;
- (void)printf("%stime added to parent folder expected %.24s.%09ld found that it is not supported\n",
- tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec);
- tab = "\t";
- } else if (supported && ((s->st_ptimespec.tv_sec != ptimespec.tv_sec) ||
+ if (mflag) {
+ ptimespec.tv_sec = 0;
+ ptimespec.tv_nsec = 0;
+ supported = 1;
+ } else {
+ LABEL;
+ (void)printf("%stime added to parent folder expected %.24s.%09ld found that it is not supported\n",
+ tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec);
+ tab = "\t";
+ }
+ }
+ if (supported && ((s->st_ptimespec.tv_sec != ptimespec.tv_sec) ||
(s->st_ptimespec.tv_nsec != ptimespec.tv_nsec))) {
if (!mflag) {
LABEL;
extern int ftsoptions;
extern u_int keys;
extern int lineno;
-extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag, mflag, tflag;
+extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag, mflag, tflag, xflag;
extern int insert_mod, insert_birth, insert_access, insert_change, insert_parent;
extern struct timespec ts;
#ifdef MAXPATHLEN
Remove any files in the file hierarchy that are not described in the
specification.
.\" ==========
+.It Fl S
+Skip calculating the digest of the extended attributes of the file.
+.\" ==========
.It Fl s Ar seed
Display a single checksum to the standard error output that represents all
of the files for which the keyword
#define SECONDS_IN_A_DAY (60 * 60 * 24)
int ftsoptions = FTS_PHYSICAL;
-int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag, mflag, tflag;
+int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag, mflag, tflag, xflag;
int insert_mod, insert_birth, insert_access, insert_change, insert_parent;
struct timespec ts;
u_int keys;
atexit(do_cleanup);
atexit(print_metrics_to_file);
- while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:m:F:t:E:")) != -1)
+ while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:m:F:t:E:S")) != -1)
switch((char)ch) {
case 'c':
cflag = 1;
} else {
set_metrics_file(file);
}
- break;
-
+ break;
+ case 'S':
+ xflag = 1;
+ break;
case '?':
default:
RECORD_FAILURE(92, WARN_USAGE);
status = mtree_verifyspec(spec1);
if (Uflag & (status == MISMATCHEXIT)) {
status = 0;
- } else {
+ } else if (status) {
RECORD_FAILURE(100, status);
}
if (mflag && CFDictionaryGetCount(dict)) {
usage(void)
{
(void)fprintf(stderr,
-"usage: mtree [-LPUcdeinqruxw] [-f spec] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n"
+"usage: mtree [-LPUScdeinqruxw] [-f spec] [-K key] [-k key] [-p path] [-s seed] [-m xml dictionary] [-t timestamp]\n"
"\t[-X excludes]\n");
exit(1);
}
mode_t *m;
int value;
char *ep;
+ char *l;
for (; (kw = strtok(t, "= \t\n")); t = NULL) {
ip->flags |= type = parsekey(kw, &value);
}
break;
case F_XATTRS:
- ep = strtok(val,".");
+ /*
+ * Note this is nested inside an strtok loop,
+ * strtok_r must be used to preserve the strtok context
+ * of the loop.
+ */
+ ep = strtok_r(val,".", &l);
ip->xattrsdigest = strdup(ep);
if (!ip->xattrsdigest) {
error = errno;
RECORD_FAILURE(54, error);
errc(1, error, "strdup");
}
- val = strtok(NULL,".");
+ val = strtok_r(NULL,".", &l);
if (val) {
ip->xdstream_priv_id = strtoull(val, &ep, 10);
if (*ep) {
RECORD_FAILURE(60, WARN_MISMATCH);
return rval;
} else {
- RECORD_FAILURE(61, WARN_MISMATCH);
+ if (mval != 0) {
+ RECORD_FAILURE(61, WARN_MISMATCH);
+ }
return mval;
}
}
--- /dev/null
+#!/bin/sh
+
+# Regression test for 69452380
+function regression_69452380()
+{
+ echo "Verifying that cp -p preserves symlink's attributes, rather than the attributes of the symlink's target."
+ test_dir=`mktemp -d /tmp/69452380_src_XXXX`
+ touch ${test_dir}/target
+ mkdir ${test_dir}/link_dir
+ # Create symlink (must use relative path for the failure to occur)
+ cd ${test_dir}/link_dir
+ ln -s ../target link
+ # cp (with attribute preservation) the test dir containing both the
+ # target and the link (in a subdirectory) to a new dir.
+ # Prior to 69452380, this failed because we followed the symlink to
+ # try and preserve attributes for a non-existing file, instead of
+ # preserving the attributes of the symlink itself.
+ cp -R -P -p ${test_dir} /tmp/69452380_tgt_${RANDOM}
+}
+
+set -eu -o pipefail
+
+regression_69452380
<true/>
<key>Tests</key>
<array>
+ <dict>
+ <key>Command</key>
+ <array>
+ <string>/bin/sh</string>
+ <string>cp.sh</string>
+ </array>
+ <key>AsRoot</key>
+ <false/>
+ <key>TestName</key>
+ <string>cp</string>
+ <key>WhenToRun</key>
+ <array>
+ <string>PRESUBMISSION</string>
+ <string>NIGHTLY</string>
+ </array>
+ <key>WorkingDirectory</key>
+ <string>/AppleInternal/Tests/file_cmds</string>
+ </dict>
+ <dict>
+ <key>Command</key>
+ <array>
+ <string>/bin/sh</string>
+ <string>touch.sh</string>
+ </array>
+ <key>AsRoot</key>
+ <false/>
+ <key>TestName</key>
+ <string>touch</string>
+ <key>WhenToRun</key>
+ <array>
+ <string>PRESUBMISSION</string>
+ <string>NIGHTLY</string>
+ </array>
+ <key>WorkingDirectory</key>
+ <string>/AppleInternal/Tests/file_cmds</string>
+ </dict>
<dict>
<key>Command</key>
<array>
--- /dev/null
+#!/bin/sh
+
+file_name=`mktemp /tmp/XXXXXX`
+file_ctime=`/usr/bin/stat -f%c ${file_name}`
+
+/usr/bin/touch $file_name
+file_mtime=`/usr/bin/stat -f%m ${file_name}`
+
+if [ "$file_ctime" -gt "$file_mtime" ]; then
+ echo "file's mod time ($file_mtime) should be later than the file's creation time ($file_ctime)"
+ exit 1
+fi
+
+exit 0
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <sys/attr.h>
#include <err.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
+typedef struct {
+ struct timespec mtime;
+ struct timespec atime;
+} set_ts;
+
int rw(char *, struct stat *, int);
-void stime_arg1(char *, struct timeval *);
-void stime_arg2(char *, int, struct timeval *);
-void stime_file(char *, struct timeval *);
+void stime_arg1(char *, set_ts *);
+void stime_arg2(char *, int, set_ts *);
+void stime_file(char *, set_ts *);
int timeoffset(char *);
void usage(char *);
main(int argc, char *argv[])
{
struct stat sb;
- struct timeval tv[2];
int (*stat_f)(const char *, struct stat *);
int (*utimes_f)(const char *, const struct timeval *);
int Aflag, aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset;
char *p;
char *myname;
+ struct attrlist ts_req = {
+ .bitmapcount = ATTR_BIT_MAP_COUNT,
+ .commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME,
+ };
+ set_ts ts_struct = {};
myname = basename(argv[0]);
Aflag = aflag = cflag = fflag = mflag = timeset = 0;
stat_f = stat;
utimes_f = utimes;
- if (gettimeofday(&tv[0], NULL))
- err(1, "gettimeofday");
+ if (clock_gettime(CLOCK_REALTIME, &ts_struct.mtime))
+ err(1, "clock_gettime");
+ ts_struct.atime = ts_struct.mtime;
while ((ch = getopt(argc, argv, "A:acfhmr:t:")) != -1)
switch(ch) {
break;
case 'r':
timeset = 1;
- stime_file(optarg, tv);
+ stime_file(optarg, &ts_struct);
break;
case 't':
timeset = 1;
- stime_arg1(optarg, tv);
+ stime_arg1(optarg, &ts_struct);
break;
case '?':
default:
* that time once and for all here.
*/
if (aflag)
- tv[0].tv_sec += Aflag;
+ ts_struct.atime.tv_sec += Aflag;
if (mflag)
- tv[1].tv_sec += Aflag;
+ ts_struct.mtime.tv_sec += Aflag;
Aflag = 0; /* done our job */
}
} else {
len = p - argv[0];
if (*p == '\0' && (len == 8 || len == 10)) {
timeset = 1;
- stime_arg2(*argv++, len == 10, tv);
+ stime_arg2(*argv++, len == 10, &ts_struct);
}
}
- /* Both times default to the same. */
- tv[1] = tv[0];
}
if (*argv == NULL)
}
if (!aflag)
- TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
+ ts_struct.atime = sb.st_atimespec;
if (!mflag)
- TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
+ ts_struct.mtime = sb.st_mtimespec;
/*
* We're adjusting the times based on the file times, not a
*/
if (Aflag) {
if (aflag) {
- TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
- tv[0].tv_sec += Aflag;
+ ts_struct.atime = sb.st_atimespec;
+ ts_struct.atime.tv_sec += Aflag;
}
if (mflag) {
- TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
- tv[1].tv_sec += Aflag;
+ ts_struct.mtime = sb.st_mtimespec;
+ ts_struct.mtime.tv_sec += Aflag;
}
}
- /* Try utimes(2). */
- if (!utimes_f(*argv, tv))
+ /* Try setattrlist(2). */
+ if (!setattrlist(*argv, &ts_req, &ts_struct, sizeof(ts_struct), 0))
continue;
/* If the user specified a time, nothing else we can do. */
#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
void
-stime_arg1(char *arg, struct timeval *tvp)
+stime_arg1(char *arg, set_ts *tsp)
{
time_t now;
struct tm *t;
int yearset;
char *p;
/* Start with the current time. */
- now = tvp[0].tv_sec;
+ now = tsp->atime.tv_sec;
if ((t = localtime(&now)) == NULL)
err(1, "localtime");
/* [[CC]YY]MMDDhhmm[.SS] */
}
t->tm_isdst = -1; /* Figure out DST. */
- tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
- if (tvp[0].tv_sec == -1)
+ tsp->atime.tv_sec = tsp->mtime.tv_sec = mktime(t);
+ if (tsp->atime.tv_sec == -1)
terr: errx(1,
"out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
- tvp[0].tv_usec = tvp[1].tv_usec = 0;
+ tsp->atime.tv_nsec = tsp->mtime.tv_nsec = 0;
}
void
-stime_arg2(char *arg, int year, struct timeval *tvp)
+stime_arg2(char *arg, int year, set_ts *tsp)
{
time_t now;
struct tm *t;
/* Start with the current time. */
- now = tvp[0].tv_sec;
+ now = tsp->atime.tv_sec;
if ((t = localtime(&now)) == NULL)
err(1, "localtime");
}
t->tm_isdst = -1; /* Figure out DST. */
- tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
- if (tvp[0].tv_sec == -1)
+ tsp->atime.tv_sec = tsp->mtime.tv_sec = mktime(t);
+ if (tsp->atime.tv_sec == -1)
errx(1,
"out of range or illegal time specification: MMDDhhmm[yy]");
- tvp[0].tv_usec = tvp[1].tv_usec = 0;
+ tsp->atime.tv_nsec = tsp->mtime.tv_nsec = 0;
}
/* Calculate a time offset in seconds, given an arg of the format [-]HHMMSS. */
}
void
-stime_file(char *fname, struct timeval *tvp)
+stime_file(char *fname, set_ts *ts_struct)
{
struct stat sb;
if (stat(fname, &sb))
err(1, "%s", fname);
- TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);
- TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);
+ ts_struct->atime = sb.st_atimespec;
+ ts_struct->mtime = sb.st_mtimespec;
}
int