From e0055cbeb11250c98bfd1c08a6aca12c11026716 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 19 Aug 2016 02:39:10 +0000 Subject: [PATCH] file_cmds-264.1.1.tar.gz --- chmod/chmod_acl.c | 39 +++++-- ipcrm/ipcrm.c | 4 +- mtree/commoncrypto.c | 161 +++++++++++++++++++++++++++- mtree/commoncrypto.h | 7 ++ mtree/compare.c | 147 ++++++++++++++++++++----- mtree/create.c | 88 +++++++++++++-- mtree/extern.h | 2 + mtree/misc.c | 87 +++++++++++---- mtree/mtree.8 | 14 +++ mtree/mtree.h | 56 ++++++---- mtree/spec.c | 296 ++++++++++++++++++++++++++++++--------------------- mtree/specspec.c | 32 ++++++ mtree/verify.c | 27 +++-- mv/mv.c | 2 +- rm/rm.c | 20 +++- 15 files changed, 761 insertions(+), 221 deletions(-) diff --git a/chmod/chmod_acl.c b/chmod/chmod_acl.c index 34708fc..5ac4c17 100644 --- a/chmod/chmod_acl.c +++ b/chmod/chmod_acl.c @@ -676,7 +676,7 @@ modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int pos unsigned aindex = 0, flag_new_acl = 0; acl_entry_t newent = NULL; acl_entry_t entry = NULL; - unsigned retval = 0 ; + unsigned retval = 0; extern int fflag; @@ -699,17 +699,36 @@ modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int pos if (optflags & ACL_CLEAR_FLAG) { filesec_t fsec = filesec_init(); - if (fsec == NULL) + if (fsec == NULL) { err(1, "filesec_init() failed"); - if (filesec_set_property(fsec, FILESEC_ACL, - _FILESEC_REMOVE_ACL) != 0) + } + if (filesec_set_property(fsec, FILESEC_ACL, _FILESEC_REMOVE_ACL) != 0) { err(1, "filesec_set_property() failed"); - if (chmodx_np(path, fsec) != 0) { - if (!fflag) - warn("Failed to clear ACL on file %s", path); - retval = 1; - } else - retval = 0; + } + if (follow) { + if (chmodx_np(path, fsec) != 0) { + if (!fflag) { + warn("Failed to clear ACL on file %s", path); + } + retval = 1; + } + } else { + int fd = open(path, O_SYMLINK); + if (fd != -1) { + if (fchmodx_np(fd, fsec) != 0) { + if (!fflag) { + warn("Failed to clear ACL on file %s", path); + } + retval = 1; + } + close(fd); + } else { + if (!fflag) { + warn("Failed to open file %s", path); + } + retval = 1; + } + } filesec_free(fsec); return (retval); } diff --git a/ipcrm/ipcrm.c b/ipcrm/ipcrm.c index 2a401f3..e90aaa1 100644 --- a/ipcrm/ipcrm.c +++ b/ipcrm/ipcrm.c @@ -83,14 +83,12 @@ static int shmrm(key_t key, int id) static int semrm(key_t key, int id) { - union semun arg; - if (key) { id = semget(key, 0, 0); if (id == -1) return -1; } - return semctl(id, 0, IPC_RMID, arg); + return semctl(id, 0, IPC_RMID); } static void not_configured(__unused int unused) diff --git a/mtree/commoncrypto.c b/mtree/commoncrypto.c index 0dda1f0..88efada 100644 --- a/mtree/commoncrypto.c +++ b/mtree/commoncrypto.c @@ -2,14 +2,29 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include "commoncrypto.h" +const int kSHA256NullTerminatedBuffLen = 65; +static const char hex[] = "0123456789abcdef"; + +/* Functions for SHA256_File_XATTRs */ +#define SHA256_Data(d, s, b) Digest_Data(kCCDigestSHA256, d, s, b) +char *Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf); +void Quicksort(char **array, int num); + /* Generic version of libmd's *_File() functions. */ char * Digest_File(CCDigestAlg algorithm, const char *filename, char *buf) { - static const char hex[] = "0123456789abcdef"; int fd; __block CCDigestCtx ctx; dispatch_queue_t queue; @@ -75,6 +90,150 @@ Digest_File(CCDigestAlg algorithm, const char *filename, char *buf) buf[i+i+1] = hex[digest[i] & 0x0f]; } buf[i+i] = '\0'; + + return buf; +} + +char *SHA256_Path_XATTRs(char *path, char *buf) +{ + char *xattrsSummary = NULL; + int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW; + ssize_t nameBufSize = listxattr(path, NULL, 0, options); + if (nameBufSize > 0) { + char *nameBuf = malloc(nameBufSize); + + listxattr(path, nameBuf, nameBufSize, options); + + size_t xattrsLen = 1; + size_t xattrIndex = 0; + char **xattrs = malloc(xattrsLen * sizeof(char *)); + char *nextName = nameBuf; + while (nextName < nameBuf + nameBufSize) + { + char *name = nextName; + if (xattrIndex == xattrsLen) { + xattrsLen *= 2; + xattrs = realloc(xattrs, xattrsLen * sizeof(char *)); + } + xattrs[xattrIndex++] = name; + nextName += strlen(name) + 1; + } + + // sort the xattr array as they're not guaranteed to come in the same order + qsort_b(xattrs, xattrIndex, sizeof(char *), ^(const void *l, const void *r) { + char *left = *(char **)l; + char *right = *(char **)r; + return strcmp(left, right); + }); + + // gather the data for the xattrs + bool didAddXATTR = false; + int xattrBufLen = kSHA256NullTerminatedBuffLen; + void *xattrBuf = malloc(xattrBufLen); // resized if necessary + char *digest; + ssize_t result = 0; + char *oldSummary = NULL; + for (int i = 0; i < xattrIndex; i++) { + char *name = xattrs[i]; + ssize_t xlen = getxattr(path, name, NULL, 0, 0, options); + if (xlen > xattrBufLen) { + xattrBufLen = xlen; + xattrBuf = realloc(xattrBuf, xattrBufLen); + } + bzero(xattrBuf, xattrBufLen); + result = getxattr(path, name, xattrBuf, xattrBufLen, 0, options); + if (result < 0) + err(1, "SHA256_Path_XATTRs: getxattr"); + + digest = SHA256_Data(xattrBuf, xattrBufLen, buf); + if (!digest) + err(1, "%s", xattrsSummary); + if (!didAddXATTR) + { + didAddXATTR = true; + asprintf(&xattrsSummary, "%s:%s", name, digest); + } else { + oldSummary = xattrsSummary; + asprintf(&xattrsSummary, "%s, %s:%s", oldSummary, name, digest); + free(oldSummary); + } + } + + free(xattrBuf); + free(nameBuf); + free(xattrs); + + digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf); + if (!digest) + err(1, "%s", xattrsSummary); + + free(xattrsSummary); + return digest; + } + return kNone; +} + +char *SHA256_Path_ACL(char *path, char *buf) +{ + int result = 0; + char *data = NULL; + char *digest = NULL; + + struct attrlist list = { + .bitmapcount = ATTR_BIT_MAP_COUNT, + .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_EXTENDED_SECURITY, + }; + + struct ACLBuf { + uint32_t len; + attribute_set_t returned_attrs; + attrreference_t acl; + char buf[8192]; // current acls are up to 3116 bytes, but they may increase in the future + } __attribute__((aligned(4), packed)); + + struct ACLBuf aclBuf; + + result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW); + + if (result) + err(1, "SHA256_Path_ACL: getattrlist"); + + // if the path does not have an acl, return none + if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) ) + || ( aclBuf.acl.attr_length == 0 ) ) { + return kNone; + } + + data = ((char*)&aclBuf.acl) + aclBuf.acl.attr_dataoffset; + + digest = SHA256_Data(data, aclBuf.acl.attr_length, buf); + if (!digest) + err(1, "SHA256_Path_ACL: SHA256_Data"); + + return digest; +} +/* Functions for Digest_Path_* */ +char * +Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) { + + uint8_t digest[32]; // SHA256 is the biggest + CCDigestCtx ctx; + size_t i, length; + + (void)os_assumes_zero(CCDigestInit(algorithm, &ctx)); + (void)os_assumes_zero(CCDigestUpdate(&ctx, data, size)); + + /* Finalize and convert to hex. */ + (void)os_assumes_zero(CCDigestFinal(&ctx, digest)); + length = CCDigestOutputSize(&ctx); + os_assert(length <= sizeof(digest)); + for (i = 0; i < length; i++) { + buf[i+i] = hex[digest[i] >> 4]; + buf[i+i+1] = hex[digest[i] & 0x0f]; + } + buf[i+i] = '\0'; + return buf; } + diff --git a/mtree/commoncrypto.h b/mtree/commoncrypto.h index cbef44a..a894749 100644 --- a/mtree/commoncrypto.h +++ b/mtree/commoncrypto.h @@ -1,8 +1,15 @@ #include +#define kNone "none" + +extern const int kSHA256NullTerminatedBuffLen; + #define MD5File(f, b) Digest_File(kCCDigestMD5, f, b) #define SHA1_File(f, b) Digest_File(kCCDigestSHA1, f, b) #define RIPEMD160_File(f, b) Digest_File(kCCDigestRMD160, f, b) #define SHA256_File(f, b) Digest_File(kCCDigestSHA256, f, b) char *Digest_File(CCDigestAlg algorithm, const char *filename, char *buf); + +char *SHA256_Path_XATTRs(char *path, char *buf); +char *SHA256_Path_ACL(char *path, char *buf); \ No newline at end of file diff --git a/mtree/compare.c b/mtree/compare.c index 1bf7d51..7413d2f 100644 --- a/mtree/compare.c +++ b/mtree/compare.c @@ -86,7 +86,8 @@ compare(char *name __unused, NODE *s, FTSENT *p) off_t len; char *cp; const char *tab = ""; - char *fflags; + char *fflags, *badflags; + u_long flags; label = 0; switch(s->type) { @@ -182,18 +183,14 @@ typeerr: LABEL; (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size); tab = "\t"; } - /* - * XXX - * Catches nano-second differences, but doesn't display them. - */ if ((s->flags & F_TIME) && ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) || (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) { LABEL; - (void)printf("%smodification time expected %.24s ", - tab, ctime(&s->st_mtimespec.tv_sec)); - (void)printf("found %.24s", - ctime(&p->fts_statp->st_mtimespec.tv_sec)); + (void)printf("%smodification time expected %.24s.%09ld ", + tab, ctime(&s->st_mtimespec.tv_sec), s->st_mtimespec.tv_nsec); + (void)printf("found %.24s.%09ld", + ctime(&p->fts_statp->st_mtimespec.tv_sec), p->fts_statp->st_mtimespec.tv_nsec); if (uflag) { tv[0].tv_sec = s->st_mtimespec.tv_sec; tv[0].tv_usec = s->st_mtimespec.tv_nsec / 1000; @@ -229,25 +226,35 @@ typeerr: LABEL; } } } - if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) { - LABEL; - fflags = flags_to_string(s->st_flags); - (void)printf("%sflags expected \"%s\"", tab, fflags); - free(fflags); - - fflags = flags_to_string(p->fts_statp->st_flags); - (void)printf(" found \"%s\"", fflags); + if (s->flags & F_FLAGS) { + // There are unpublished flags that should not fail comparison + // we convert to string and back to filter them out + fflags = badflags = flags_to_string(p->fts_statp->st_flags); + if (strcmp("none", fflags) == 0) { + flags = 0; + } else if (strtofflags(&badflags, &flags, NULL) != 0) + errx(1, "invalid flag %s", badflags); free(fflags); - - if (uflag) - if (chflags(p->fts_accpath, (u_int)s->st_flags)) - (void)printf(" not modified: %s\n", - strerror(errno)); - else - (void)printf(" modified\n"); - else - (void)printf("\n"); - tab = "\t"; + if (s->st_flags != flags) { + LABEL; + fflags = flags_to_string(s->st_flags); + (void)printf("%sflags expected \"%s\"", tab, fflags); + free(fflags); + + fflags = flags_to_string(flags); + (void)printf(" found \"%s\"", fflags); + free(fflags); + + if (uflag) + if (chflags(p->fts_accpath, (u_int)s->st_flags)) + (void)printf(" not modified: %s\n", + strerror(errno)); + else + (void)printf(" modified\n"); + else + (void)printf("\n"); + tab = "\t"; + } } #ifdef ENABLE_MD5 if (s->flags & F_MD5) { @@ -305,7 +312,7 @@ typeerr: LABEL; #endif /* ENABLE_RMD160 */ #ifdef ENABLE_SHA256 if (s->flags & F_SHA256) { - char *new_digest, buf[65]; + char *new_digest, buf[kSHA256NullTerminatedBuffLen]; new_digest = SHA256_File(p->fts_accpath, buf); if (!new_digest) { @@ -328,6 +335,90 @@ typeerr: LABEL; (void)printf("%slink_ref expected %s found %s\n", tab, s->slink, cp); } + if ((s->flags & F_BTIME) && + ((s->st_birthtimespec.tv_sec != p->fts_statp->st_birthtimespec.tv_sec) || + (s->st_birthtimespec.tv_nsec != p->fts_statp->st_birthtimespec.tv_nsec))) { + LABEL; + (void)printf("%sbirth time expected %.24s.%09ld ", + tab, ctime(&s->st_birthtimespec.tv_sec), s->st_birthtimespec.tv_nsec); + (void)printf("found %.24s.%09ld\n", + ctime(&p->fts_statp->st_birthtimespec.tv_sec), p->fts_statp->st_birthtimespec.tv_nsec); + tab = "\t"; + } + if ((s->flags & F_ATIME) && + ((s->st_atimespec.tv_sec != p->fts_statp->st_atimespec.tv_sec) || + (s->st_atimespec.tv_nsec != p->fts_statp->st_atimespec.tv_nsec))) { + LABEL; + (void)printf("%saccess time expected %.24s.%09ld ", + tab, ctime(&s->st_atimespec.tv_sec), s->st_atimespec.tv_nsec); + (void)printf("found %.24s.%09ld\n", + ctime(&p->fts_statp->st_atimespec.tv_sec), p->fts_statp->st_atimespec.tv_nsec); + tab = "\t"; + } + if ((s->flags & F_CTIME) && + ((s->st_ctimespec.tv_sec != p->fts_statp->st_ctimespec.tv_sec) || + (s->st_ctimespec.tv_nsec != p->fts_statp->st_ctimespec.tv_nsec))) { + LABEL; + (void)printf("%smetadata modification time expected %.24s.%09ld ", + tab, ctime(&s->st_ctimespec.tv_sec), s->st_ctimespec.tv_nsec); + (void)printf("found %.24s.%09ld\n", + ctime(&p->fts_statp->st_ctimespec.tv_sec), p->fts_statp->st_ctimespec.tv_nsec); + tab = "\t"; + } + if (s->flags & F_PTIME) { + 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 ((s->st_ptimespec.tv_sec != ptimespec.tv_sec) || + (s->st_ptimespec.tv_nsec != ptimespec.tv_nsec)) { + LABEL; + (void)printf("%stime added to parent folder expected %.24s.%09ld ", + tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec); + (void)printf("found %.24s.%09ld\n", + ctime(&ptimespec.tv_sec), ptimespec.tv_nsec); + tab = "\t"; + } + } + if (s->flags & F_XATTRS) { + char *new_digest, buf[kSHA256NullTerminatedBuffLen]; + new_digest = SHA256_Path_XATTRs(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sxattrsdigest missing, expected: %s\n", tab, s->xattrsdigest); + tab = "\t"; + } else if (strcmp(new_digest, s->xattrsdigest)) { + LABEL; + printf("%sxattrsdigest expected %s found %s\n", + tab, s->xattrsdigest, new_digest); + tab = "\t"; + } + } + if ((s->flags & F_INODE) && + (p->fts_statp->st_ino != s->st_ino)) { + LABEL; + (void)printf("%sinode expected %llu found %llu\n", + tab, s->st_ino, p->fts_ino); + tab = "\t"; + } + if (s->flags & F_ACL) { + char *new_digest, buf[kSHA256NullTerminatedBuffLen]; + new_digest = SHA256_Path_ACL(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sacldigest missing, expected: %s\n", tab, s->acldigest); + tab = "\t"; + } else if (strcmp(new_digest, s->acldigest)) { + LABEL; + printf("%sacldigest expected %s found %s\n", + tab, s->acldigest, new_digest); + tab = "\t"; + } + } + return (label); } diff --git a/mtree/create.c b/mtree/create.c index 44c3295..66992a7 100644 --- a/mtree/create.c +++ b/mtree/create.c @@ -77,10 +77,12 @@ static gid_t gid; static uid_t uid; static mode_t mode; static u_long flags = 0xffffffff; +static char *xattrs = kNone; +static char *acl = kNone; static int dsort(const FTSENT **, const FTSENT **); static void output(int, int *, const char *, ...) __printflike(3, 4); -static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *); +static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *, char **, char **); static void statf(int, FTSENT *); void @@ -92,6 +94,7 @@ cwalk(void) char *argv[2], host[MAXHOSTNAMELEN]; char dot[] = "."; int indent = 0; + char *path; if (!nflag) { (void)time(&cl); @@ -119,14 +122,20 @@ cwalk(void) case FTS_D: if (!dflag) (void)printf("\n"); - if (!nflag) - (void)printf("# %s\n", p->fts_path); - statd(t, p, &uid, &gid, &mode, &flags); + if (!nflag) { + path = escape_path(p->fts_path); + (void)printf("# %s\n", path); + free(path); + } + statd(t, p, &uid, &gid, &mode, &flags, &xattrs, &acl); statf(indent, p); break; case FTS_DP: - if (!nflag && (p->fts_level > 0)) - (void)printf("%*s# %s\n", indent, "", p->fts_path); + if (!nflag && (p->fts_level > 0)) { + path = escape_path(p->fts_path); + (void)printf("%*s# %s\n", indent, "", path); + free(path); + } (void)printf("%*s..\n", indent, ""); if (!dflag) (void)printf("\n"); @@ -218,7 +227,7 @@ statf(int indent, FTSENT *p) output(indent, &offset, "size=%jd", (intmax_t)p->fts_statp->st_size); if (keys & F_TIME) - output(indent, &offset, "time=%ld.%ld", + output(indent, &offset, "time=%ld.%09ld", (long)p->fts_statp->st_mtimespec.tv_sec, p->fts_statp->st_mtimespec.tv_nsec); if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { @@ -260,7 +269,7 @@ statf(int indent, FTSENT *p) #endif /* ENABLE_RMD160 */ #ifdef ENABLE_SHA256 if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) { - char *digest, buf[65]; + char *digest, buf[kSHA256NullTerminatedBuffLen]; digest = SHA256_File(p->fts_accpath, buf); if (!digest) @@ -280,6 +289,53 @@ statf(int indent, FTSENT *p) output(indent, &offset, "flags=%s", fflags); free(fflags); } + if (keys & F_BTIME) { + output(indent, &offset, "btime=%ld.%09ld", + p->fts_statp->st_birthtimespec.tv_sec, + p->fts_statp->st_birthtimespec.tv_nsec); + } + // only check access time on regular files, as traversing a folder will update its access time + if (keys & F_ATIME && S_ISREG(p->fts_statp->st_mode)) { + output(indent, &offset, "atime=%ld.%09ld", + p->fts_statp->st_atimespec.tv_sec, + p->fts_statp->st_atimespec.tv_nsec); + } + if (keys & F_CTIME) { + output(indent, &offset, "ctime=%ld.%09ld", + p->fts_statp->st_ctimespec.tv_sec, + p->fts_statp->st_ctimespec.tv_nsec); + } + // date added to parent folder is only supported for files and directories + if (keys & F_PTIME && (S_ISREG(p->fts_statp->st_mode) || + S_ISDIR(p->fts_statp->st_mode))) { + int supported; + struct timespec ptimespec = ptime(p->fts_accpath, &supported); + if (supported) { + output(indent, &offset, "ptime=%ld.%09ld", + ptimespec.tv_sec, + ptimespec.tv_nsec); + } + } + if (keys & F_XATTRS) { + char *digest, buf[kSHA256NullTerminatedBuffLen]; + + digest = SHA256_Path_XATTRs(p->fts_accpath, buf); + if (digest && (strcmp(digest, xattrs) != 0)) { + output(indent, &offset, "xattrsdigest=%s", digest); + } + } + if (keys & F_INODE) { + output(indent, &offset, "inode=%llu", p->fts_statp->st_ino); + } + if (keys & F_ACL) { + char *digest, buf[kSHA256NullTerminatedBuffLen]; + + digest = SHA256_Path_ACL(p->fts_accpath, buf); + if (digest && (strcmp(digest, acl) != 0)) { + output(indent, &offset, "acldigest=%s", digest); + } + } + (void)putchar('\n'); } @@ -290,7 +346,7 @@ statf(int indent, FTSENT *p) #define MAXS 16 static int -statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *pflags) +statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *pflags, char **pxattrs, char **pacl) { FTSENT *p; gid_t sgid; @@ -303,6 +359,8 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *p uid_t saveuid = *puid; mode_t savemode = *pmode; u_long saveflags = *pflags; + char *savexattrs = *pxattrs; + char *saveacl = *pacl; u_short maxgid, maxuid, maxmode, maxflags; u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS]; char *fflags; @@ -337,6 +395,12 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *p saveuid = suid; maxuid = u[suid]; } + + /* + * XXX + * note that we don't count the most common xattr/acl digest + * so set will always the default value (none) + */ /* * XXX @@ -399,11 +463,17 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *p (void)printf(" flags=%s", fflags); free(fflags); } + if (keys & F_XATTRS) + (void)printf(" xattrsdigest=%s", savexattrs); + if (keys & F_ACL) + (void)printf(" acldigest=%s", saveacl); (void)printf("\n"); *puid = saveuid; *pgid = savegid; *pmode = savemode; *pflags = saveflags; + *pxattrs = savexattrs; + *pacl = saveacl; } return (0); } diff --git a/mtree/extern.h b/mtree/extern.h index 4c7adb2..ff40b84 100644 --- a/mtree/extern.h +++ b/mtree/extern.h @@ -37,6 +37,8 @@ int compare(char *, NODE *, FTSENT *); int crc(int, uint32_t *, off_t *); void cwalk(void); char *flags_to_string(u_long); +char *escape_path(char *string); +struct timespec ptime(char *path, int *supported); const char *inotype(u_int); u_int parsekey(char *, int *); diff --git a/mtree/misc.c b/mtree/misc.c index 4cf852f..4851868 100644 --- a/mtree/misc.c +++ b/mtree/misc.c @@ -44,6 +44,8 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/misc.c,v 1.16 2005/03/29 11:44:17 tobez E #include #include "mtree.h" #include "extern.h" +#import +#include typedef struct _key { const char *name; /* key name */ @@ -55,32 +57,39 @@ typedef struct _key { /* NB: the following table must be sorted lexically. */ static KEY keylist[] = { - {"cksum", F_CKSUM, NEEDVALUE}, - {"flags", F_FLAGS, NEEDVALUE}, - {"gid", F_GID, NEEDVALUE}, - {"gname", F_GNAME, NEEDVALUE}, - {"ignore", F_IGN, 0}, - {"link", F_SLINK, NEEDVALUE}, + {"acldigest", F_ACL, NEEDVALUE}, + {"atime", F_ATIME, NEEDVALUE}, + {"btime", F_BTIME, NEEDVALUE}, + {"cksum", F_CKSUM, NEEDVALUE}, + {"ctime", F_CTIME, NEEDVALUE}, + {"flags", F_FLAGS, NEEDVALUE}, + {"gid", F_GID, NEEDVALUE}, + {"gname", F_GNAME, NEEDVALUE}, + {"ignore", F_IGN, 0}, + {"inode", F_INODE, NEEDVALUE}, + {"link", F_SLINK, NEEDVALUE}, #ifdef ENABLE_MD5 - {"md5digest", F_MD5, NEEDVALUE}, + {"md5digest", F_MD5, NEEDVALUE}, #endif - {"mode", F_MODE, NEEDVALUE}, - {"nlink", F_NLINK, NEEDVALUE}, - {"nochange", F_NOCHANGE, 0}, + {"mode", F_MODE, NEEDVALUE}, + {"nlink", F_NLINK, NEEDVALUE}, + {"nochange", F_NOCHANGE, 0}, + {"ptime", F_PTIME, NEEDVALUE}, #ifdef ENABLE_RMD160 - {"ripemd160digest", F_RMD160, NEEDVALUE}, + {"ripemd160digest", F_RMD160, NEEDVALUE}, #endif #ifdef ENABLE_SHA1 - {"sha1digest", F_SHA1, NEEDVALUE}, + {"sha1digest", F_SHA1, NEEDVALUE}, #endif #ifdef ENABLE_SHA256 - {"sha256digest", F_SHA256, NEEDVALUE}, + {"sha256digest", F_SHA256, NEEDVALUE}, #endif - {"size", F_SIZE, NEEDVALUE}, - {"time", F_TIME, NEEDVALUE}, - {"type", F_TYPE, NEEDVALUE}, - {"uid", F_UID, NEEDVALUE}, - {"uname", F_UNAME, NEEDVALUE}, + {"size", F_SIZE, NEEDVALUE}, + {"time", F_TIME, NEEDVALUE}, + {"type", F_TYPE, NEEDVALUE}, + {"uid", F_UID, NEEDVALUE}, + {"uname", F_UNAME, NEEDVALUE}, + {"xattrsdigest", F_XATTRS, NEEDVALUE}, }; int keycompare(const void *, const void *); @@ -122,3 +131,45 @@ flags_to_string(u_long fflags) return string; } + +// escape path and always return a new string so it can be freed +char * +escape_path(char *string) +{ + char *escapedPath = calloc(1, strlen(string) * 4 + 1); + if (escapedPath == NULL) + errx(1, "escape_path(): calloc() failed"); + strvis(escapedPath, string, VIS_NL | VIS_CSTYLE | VIS_OCTAL); + + return escapedPath; +} + +struct ptimebuf { + uint32_t length; + attribute_set_t returned_attrs; + struct timespec st_ptimespec; +} __attribute__((aligned(4), packed)); + +// ptime is not supported on root filesystems or HFS filesystems older than the feature being introduced +struct timespec +ptime(char *path, int *supported) { + + int ret = 0; + struct ptimebuf buf; + struct attrlist list = { + .bitmapcount = ATTR_BIT_MAP_COUNT, + .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_ADDEDTIME, + }; + ret = getattrlist(path, &list, &buf, sizeof(buf), FSOPT_NOFOLLOW); + if (ret) { + err(1, "ptime: getattrlist"); + } + + *supported = 0; + if (buf.returned_attrs.commonattr & ATTR_CMN_ADDEDTIME) { + *supported = 1; + } + + return buf.st_ptimespec; + +} diff --git a/mtree/mtree.8 b/mtree/mtree.8 index 72536d3..1aa529a 100644 --- a/mtree/mtree.8 +++ b/mtree/mtree.8 @@ -246,6 +246,20 @@ The size, in bytes, of the file. The file the symbolic link is expected to reference. .It Cm time The last modification time of the file. +.It Cm btime +The creation (birth) time of the file. +.It Cm atime +The last access time of the file. +.It Cm ctime +The last metadata modification time of the file. +.It Cm ptime +The time the file was added to its parent folder. +.It Cm inode +The inode number of the file. +.It Cm xattrsdigest +Digest of the extended attributes of the file. +.It Cm acldigest +Digest of the access control list of the file. .It Cm type The type of the file; may be set to any one of the following: .Pp diff --git a/mtree/mtree.h b/mtree/mtree.h index 2b99797..02d1728 100644 --- a/mtree/mtree.h +++ b/mtree/mtree.h @@ -55,29 +55,43 @@ typedef struct _node { mode_t st_mode; /* mode */ u_long st_flags; /* flags */ nlink_t st_nlink; /* link count */ + struct timespec st_birthtimespec; /* birth time (creation time) */ + struct timespec st_atimespec; /* access time */ + struct timespec st_ctimespec; /* metadata modification time */ + struct timespec st_ptimespec; /* time added to parent folder */ + char *xattrsdigest; /* digest of extended attributes */ + ino_t st_ino; /* inode */ + char *acldigest; /* digest of access control list */ -#define F_CKSUM 0x0001 /* check sum */ -#define F_DONE 0x0002 /* directory done */ -#define F_GID 0x0004 /* gid */ -#define F_GNAME 0x0008 /* group name */ -#define F_IGN 0x0010 /* ignore */ -#define F_MAGIC 0x0020 /* name has magic chars */ -#define F_MODE 0x0040 /* mode */ -#define F_NLINK 0x0080 /* number of links */ -#define F_SIZE 0x0100 /* size */ -#define F_SLINK 0x0200 /* link count */ -#define F_TIME 0x0400 /* modification time */ -#define F_TYPE 0x0800 /* file type */ -#define F_UID 0x1000 /* uid */ -#define F_UNAME 0x2000 /* user name */ -#define F_VISIT 0x4000 /* file visited */ -#define F_MD5 0x8000 /* MD5 digest */ -#define F_NOCHANGE 0x10000 /* If owner/mode "wrong", do */ +#define F_CKSUM 0x00000001 /* check sum */ +#define F_DONE 0x00000002 /* directory done */ +#define F_GID 0x00000004 /* gid */ +#define F_GNAME 0x00000008 /* group name */ +#define F_IGN 0x00000010 /* ignore */ +#define F_MAGIC 0x00000020 /* name has magic chars */ +#define F_MODE 0x00000040 /* mode */ +#define F_NLINK 0x00000080 /* number of links */ +#define F_SIZE 0x00000100 /* size */ +#define F_SLINK 0x00000200 /* The file the symbolic link is expected to reference */ +#define F_TIME 0x00000400 /* modification time (mtime) */ +#define F_TYPE 0x00000800 /* file type */ +#define F_UID 0x00001000 /* uid */ +#define F_UNAME 0x00002000 /* user name */ +#define F_VISIT 0x00004000 /* file visited */ +#define F_MD5 0x00008000 /* MD5 digest */ +#define F_NOCHANGE 0x00010000 /* If owner/mode "wrong", do */ /* not change */ -#define F_SHA1 0x20000 /* SHA-1 digest */ -#define F_RMD160 0x40000 /* RIPEMD160 digest */ -#define F_FLAGS 0x80000 /* file flags */ -#define F_SHA256 0x100000 /* SHA-256 digest */ +#define F_SHA1 0x00020000 /* SHA-1 digest */ +#define F_RMD160 0x00040000 /* RIPEMD160 digest */ +#define F_FLAGS 0x00080000 /* file flags */ +#define F_SHA256 0x00100000 /* SHA-256 digest */ +#define F_BTIME 0x00200000 /* creation time */ +#define F_ATIME 0x00400000 /* access time */ +#define F_CTIME 0x00800000 /* metadata modification time (ctime) */ +#define F_PTIME 0x01000000 /* time added to parent folder */ +#define F_XATTRS 0x02000000 /* digest of extended attributes */ +#define F_INODE 0x04000000 /* inode */ +#define F_ACL 0x08000000 /* digest of access control list */ u_int flags; /* items set */ #define F_BLOCK 0x001 /* block special */ diff --git a/mtree/spec.c b/mtree/spec.c index 915858c..332accc 100644 --- a/mtree/spec.c +++ b/mtree/spec.c @@ -179,131 +179,189 @@ set(char *t, NODE *ip) if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL) errx(1, "line %d: missing value", lineno); switch(type) { - case F_CKSUM: - ip->cksum = strtoul(val, &ep, 10); - if (*ep) - errx(1, "line %d: invalid checksum %s", - lineno, val); - break; - case F_MD5: - ip->md5digest = strdup(val); - if(!ip->md5digest) - errx(1, "strdup"); - break; - case F_SHA1: - ip->sha1digest = strdup(val); - if(!ip->sha1digest) - errx(1, "strdup"); - break; - case F_SHA256: - ip->sha256digest = strdup(val); - if(!ip->sha256digest) - errx(1, "strdup"); - break; - case F_RMD160: - ip->rmd160digest = strdup(val); - if(!ip->rmd160digest) - errx(1, "strdup"); - break; - case F_FLAGS: - if (strcmp("none", val) == 0) - ip->st_flags = 0; - else if (strtofflags(&val, &ip->st_flags, NULL) != 0) - errx(1, "line %d: invalid flag %s",lineno, val); - break; - case F_GID: - ip->st_gid = (gid_t)strtoul(val, &ep, 10); - if (*ep) - errx(1, "line %d: invalid gid %s", lineno, val); - break; - case F_GNAME: - if ((gr = getgrnam(val)) == NULL) - errx(1, "line %d: unknown group %s", lineno, val); - ip->st_gid = gr->gr_gid; - break; - case F_IGN: - /* just set flag bit */ - break; - case F_MODE: - if ((m = setmode(val)) == NULL) - errx(1, "line %d: invalid file mode %s", - lineno, val); - ip->st_mode = getmode(m, 0); - free(m); - break; - case F_NLINK: - ip->st_nlink = strtoul(val, &ep, 10); - if (*ep) - errx(1, "line %d: invalid link count %s", - lineno, val); - break; - case F_SIZE: - ip->st_size = strtoq(val, &ep, 10); - if (*ep) - errx(1, "line %d: invalid size %s", - lineno, val); - break; - case F_SLINK: - ip->slink = malloc(strlen(val) + 1); - if (ip->slink == NULL) - errx(1, "malloc"); - if (strunvis(ip->slink, val) == -1) - errx(1, "symlink %s is ill-encoded", val); - break; - case F_TIME: - ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); - if (*ep != '.') - errx(1, "line %d: invalid time %s", - lineno, val); - val = ep + 1; - ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); - if (*ep) - errx(1, "line %d: invalid time %s", - lineno, val); - break; - case F_TYPE: - switch(*val) { - case 'b': - if (!strcmp(val, "block")) - ip->type = F_BLOCK; + case F_CKSUM: + ip->cksum = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid checksum %s", + lineno, val); break; - case 'c': - if (!strcmp(val, "char")) - ip->type = F_CHAR; + case F_MD5: + ip->md5digest = strdup(val); + if(!ip->md5digest) + errx(1, "strdup"); break; - case 'd': - if (!strcmp(val, "dir")) - ip->type = F_DIR; + case F_SHA1: + ip->sha1digest = strdup(val); + if(!ip->sha1digest) + errx(1, "strdup"); break; - case 'f': - if (!strcmp(val, "file")) - ip->type = F_FILE; - if (!strcmp(val, "fifo")) - ip->type = F_FIFO; + case F_SHA256: + ip->sha256digest = strdup(val); + if(!ip->sha256digest) + errx(1, "strdup"); break; - case 'l': - if (!strcmp(val, "link")) - ip->type = F_LINK; + case F_RMD160: + ip->rmd160digest = strdup(val); + if(!ip->rmd160digest) + errx(1, "strdup"); break; - case 's': - if (!strcmp(val, "socket")) - ip->type = F_SOCK; + case F_FLAGS: + if (strcmp("none", val) == 0) + ip->st_flags = 0; + else if (strtofflags(&val, &ip->st_flags, NULL) != 0) + errx(1, "line %d: invalid flag %s",lineno, val); break; - default: - errx(1, "line %d: unknown file type %s", - lineno, val); - } - break; - case F_UID: - ip->st_uid = (uid_t)strtoul(val, &ep, 10); - if (*ep) - errx(1, "line %d: invalid uid %s", lineno, val); - break; - case F_UNAME: - if ((pw = getpwnam(val)) == NULL) - errx(1, "line %d: unknown user %s", lineno, val); - ip->st_uid = pw->pw_uid; - break; + case F_GID: + ip->st_gid = (gid_t)strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid gid %s", lineno, val); + break; + case F_GNAME: + if ((gr = getgrnam(val)) == NULL) + errx(1, "line %d: unknown group %s", lineno, val); + ip->st_gid = gr->gr_gid; + break; + case F_IGN: + /* just set flag bit */ + break; + case F_MODE: + if ((m = setmode(val)) == NULL) + errx(1, "line %d: invalid file mode %s", + lineno, val); + ip->st_mode = getmode(m, 0); + free(m); + break; + case F_NLINK: + ip->st_nlink = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid link count %s", + lineno, val); + break; + case F_SIZE: + ip->st_size = strtoq(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid size %s", + lineno, val); + break; + case F_SLINK: + ip->slink = malloc(strlen(val) + 1); + if (ip->slink == NULL) + errx(1, "malloc"); + if (strunvis(ip->slink, val) == -1) + errx(1, "symlink %s is ill-encoded", val); + break; + case F_TIME: + ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_TYPE: + switch(*val) { + case 'b': + if (!strcmp(val, "block")) + ip->type = F_BLOCK; + break; + case 'c': + if (!strcmp(val, "char")) + ip->type = F_CHAR; + break; + case 'd': + if (!strcmp(val, "dir")) + ip->type = F_DIR; + break; + case 'f': + if (!strcmp(val, "file")) + ip->type = F_FILE; + if (!strcmp(val, "fifo")) + ip->type = F_FIFO; + break; + case 'l': + if (!strcmp(val, "link")) + ip->type = F_LINK; + break; + case 's': + if (!strcmp(val, "socket")) + ip->type = F_SOCK; + break; + default: + errx(1, "line %d: unknown file type %s", + lineno, val); + } + break; + case F_UID: + ip->st_uid = (uid_t)strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid uid %s", lineno, val); + break; + case F_UNAME: + if ((pw = getpwnam(val)) == NULL) + errx(1, "line %d: unknown user %s", lineno, val); + ip->st_uid = pw->pw_uid; + break; + case F_BTIME: + ip->st_birthtimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_birthtimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_ATIME: + ip->st_atimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_atimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_CTIME: + ip->st_ctimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_ctimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_PTIME: + ip->st_ptimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_ptimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_XATTRS: + ip->xattrsdigest = strdup(val); + if(!ip->xattrsdigest) + err(1, "strdup"); + break; + case F_INODE: + ip->st_ino = (ino_t)strtoull(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid inode %s", lineno, val); + break; + case F_ACL: + ip->acldigest = strdup(val); + if(!ip->acldigest) + err(1, "strdup"); } } } diff --git a/mtree/specspec.c b/mtree/specspec.c index 113d2fd..bbcd795 100644 --- a/mtree/specspec.c +++ b/mtree/specspec.c @@ -68,6 +68,8 @@ shownode(NODE *n, int f, char const *path) printf(" nlink=%d", n->st_nlink); if (f & F_SIZE) printf(" size=%jd", (intmax_t)n->st_size); + if (f & F_TIME) + printf(" time=%ld.%09ld", n->st_mtimespec.tv_sec, n->st_mtimespec.tv_nsec); if (f & F_UID) printf(" uid=%d", n->st_uid); if (f & F_UNAME) { @@ -87,6 +89,21 @@ shownode(NODE *n, int f, char const *path) printf(" sha256digest=%s", n->sha256digest); if (f & F_FLAGS) printf(" flags=%s", flags_to_string(n->st_flags)); + if (f & F_BTIME) + printf(" btime=%ld.%09ld", n->st_birthtimespec.tv_sec, n->st_birthtimespec.tv_nsec); + if (f & F_ATIME) + printf(" atime=%ld.%09ld", n->st_atimespec.tv_sec, n->st_atimespec.tv_nsec); + if (f & F_CTIME) + printf(" ctime=%ld.%09ld", n->st_ctimespec.tv_sec, n->st_ctimespec.tv_nsec); + if (f & F_PTIME) + printf(" ptime=%ld.%09ld", n->st_ptimespec.tv_sec, n->st_ptimespec.tv_nsec); + if (f & F_XATTRS) + printf(" xattrsdigest=%s", n->xattrsdigest); + if (f & F_INODE) + printf(" inode=%llu", n->st_ino); + if (f & F_ACL) + printf(" acldigest=%s", n->acldigest); + printf("\n"); } @@ -167,6 +184,21 @@ compare_nodes(NODE *n1, NODE *n2, char const *path) differs |= F_SHA256; if (FF(n1, n2, F_FLAGS, st_flags)) differs |= F_FLAGS; + if (FM(n1, n2, F_BTIME, st_birthtimespec)) + differs |= F_BTIME; + if (FM(n1, n2, F_ATIME, st_atimespec)) + differs |= F_ATIME; + if (FM(n1, n2, F_CTIME, st_ctimespec)) + differs |= F_CTIME; + if (FM(n1, n2, F_PTIME, st_ptimespec)) + differs |= F_PTIME; + if (FS(n1, n2, F_XATTRS, xattrsdigest)) + differs |= F_XATTRS; + if (FF(n1, n2, F_INODE, st_ino)) + differs |= F_INODE; + if (FS(n1, n2, F_ACL, acldigest)) + differs |= F_ACL; + if (differs) { mismatch(n1, n2, differs, path); return (1); diff --git a/mtree/verify.c b/mtree/verify.c index 0b3dc9b..84a37ff 100644 --- a/mtree/verify.c +++ b/mtree/verify.c @@ -51,18 +51,22 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/verify.c,v 1.24 2005/08/11 15:43:55 brian static NODE *root; static char path[MAXPATHLEN]; -static void miss(NODE *, char *); +static int miss(NODE *, char *); static int vwalk(void); int mtree_verifyspec(FILE *fi) { - int rval; + int rval, mval; root = mtree_readspec(fi); rval = vwalk(); - miss(root, path); - return (rval); + mval = miss(root, path); + + if (rval != 0) + return rval; + else + return mval; } static int @@ -153,13 +157,15 @@ extra: return (rval); } -static void +static int miss(NODE *p, char *tail) { int create; char *tp; const char *type, *what; int serr; + int rval = 0; + int rrval = 0; for (; p; p = p->next) { if (p->type != F_DIR && (dflag || p->flags & F_VISIT)) @@ -170,10 +176,12 @@ miss(NODE *p, char *tail) symbolic link and the -q flag is set. */ struct stat statbuf; - if (qflag && stat(path, &statbuf) == 0) + if (qflag && stat(path, &statbuf) == 0) { p->flags |= F_VISIT; - else + } else { (void)printf("%s missing", path); + rval = MISMATCHEXIT; + } } if (p->type != F_DIR && p->type != F_LINK) { putchar('\n'); @@ -226,7 +234,9 @@ miss(NODE *p, char *tail) for (tp = tail; *tp; ++tp); *tp = '/'; - miss(p->child, tp + 1); + rrval = miss(p->child, tp + 1); + if (rrval != 0) + rval = rrval; *tp = '\0'; if (!create) @@ -252,4 +262,5 @@ miss(NODE *p, char *tail) (void)printf("%s: file flags not set: %s\n", path, strerror(errno)); } + return rval; } diff --git a/mv/mv.c b/mv/mv.c index c1116d4..0576c43 100644 --- a/mv/mv.c +++ b/mv/mv.c @@ -443,7 +443,7 @@ copy(char *from, char *to) { int pid, status; - /* posix_spawn mv from to && rm from */ + /* posix_spawn cp from to && rm from */ if ((pid = fork()) == 0) { execl(_PATH_CP, "mv", vflg ? "-PRpv" : "-PRp", "--", from, to, diff --git a/rm/rm.c b/rm/rm.c index 4e78b46..ec13b2a 100644 --- a/rm/rm.c +++ b/rm/rm.c @@ -530,10 +530,24 @@ checkdot(argv) complained = 0; for (t = argv; *t;) { - if ((p = strrchr(*t, '/')) != NULL) - ++p; - else + size_t len = strlen(*t); + char truncated[len]; + + if ((p = strrchr(*t, '/')) != NULL) { + if (p[1] == '\0') { // trailing / -- treat as if not present + strlcpy(truncated, *t, len); + p = strrchr(truncated, '/'); + if (p) { + ++p; + } else { + p = truncated; + } + } else { + ++p; + } + } else { p = *t; + } if (ISDOT(p)) { if (!complained++) warnx("\".\" and \"..\" may not be removed"); -- 2.7.4