From f13ef9e9ea6354a202b8db1eec81922600756733 Mon Sep 17 00:00:00 2001 From: Apple Date: Sat, 29 Aug 2020 01:51:24 +0000 Subject: [PATCH] file_cmds-321.40.3.tar.gz --- chmod/chmod_acl.h | 5 + cksum/extern.h | 5 + cp/extern.h | 5 + dd/dd.h | 5 + dd/extern.h | 5 + file_cmds.xcodeproj/project.pbxproj | 59 ++++ install/pathnames.h | 5 + ln/ln.1 | 26 +- ln/ln.c | 6 +- ls/extern.h | 5 + ls/ls.c | 13 +- ls/ls.h | 5 + mtree/commoncrypto.c | 153 ++++++++++- mtree/commoncrypto.h | 25 +- mtree/compare.c | 399 +++++++++++++++++++++------- mtree/create.c | 209 +++++++++++---- mtree/excludes.c | 8 +- mtree/extern.h | 12 +- mtree/fix_failure_locations.py | 77 ++++++ mtree/metrics.c | 155 +++++++++++ mtree/metrics.h | 48 ++++ mtree/misc.c | 24 +- mtree/mtree.c | 199 ++++++++++++-- mtree/mtree.h | 9 + mtree/spec.c | 174 +++++++++--- mtree/specspec.c | 50 +++- mtree/verify.c | 141 +++++++--- mv/pathnames.h | 5 + pax/cache.h | 5 + pax/cpio.h | 5 + pax/extern.h | 5 + pax/ftree.h | 5 + pax/gen_subs.c | 23 +- pax/options.h | 5 + pax/pat_rep.c | 149 +++++++++++ pax/pat_rep.h | 5 + pax/pax.c | 5 + pax/pax.h | 6 + pax/pax_format.h | 5 + pax/sel_subs.h | 5 + pax/tables.h | 5 + pax/tar.h | 5 + rm/rm.c | 12 +- 43 files changed, 1784 insertions(+), 293 deletions(-) create mode 100755 mtree/fix_failure_locations.py create mode 100644 mtree/metrics.c create mode 100644 mtree/metrics.h diff --git a/chmod/chmod_acl.h b/chmod/chmod_acl.h index c76d076..b4667e9 100644 --- a/chmod/chmod_acl.h +++ b/chmod/chmod_acl.h @@ -31,6 +31,9 @@ * SUCH DAMAGE. */ +#ifndef _CHMOD_ACL_H_ +#define _CHMOD_ACL_H_ + #ifdef __APPLE__ #include #include @@ -83,3 +86,5 @@ extern int modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, extern int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow); extern uuid_t *name_to_uuid(char *tok, int nametype); #endif /* __APPLE__*/ + +#endif /* _CHMOD_ACL_H_ */ diff --git a/cksum/extern.h b/cksum/extern.h index e9b594a..3e9c8c6 100644 --- a/cksum/extern.h +++ b/cksum/extern.h @@ -34,6 +34,9 @@ * $FreeBSD: src/usr.bin/cksum/extern.h,v 1.6 2003/03/13 23:32:28 robert Exp $ */ +#ifndef _CKSUM_EXTERN_H_ +#define _CKSUM_EXTERN_H_ + #include __BEGIN_DECLS @@ -45,3 +48,5 @@ int csum1(int, uint32_t *, off_t *); int csum2(int, uint32_t *, off_t *); int crc32(int, uint32_t *, off_t *); __END_DECLS + +#endif /* _CKSUM_EXTERN_H_ */ diff --git a/cp/extern.h b/cp/extern.h index c0ca0bc..521091d 100644 --- a/cp/extern.h +++ b/cp/extern.h @@ -30,6 +30,9 @@ * $FreeBSD: src/bin/cp/extern.h,v 1.20 2005/09/05 04:36:08 csjp Exp $ */ +#ifndef _CP_EXTERN_H_ +#define _CP_EXTERN_H_ + typedef struct { char *p_end; /* pointer to NULL at end of path */ char *target_end; /* pointer to end of target base */ @@ -54,3 +57,5 @@ int preserve_dir_acls(struct stat *, char *, char *); int preserve_fd_acls(int, int); void usage(void); __END_DECLS + +#endif /* _CP_EXTERN_H_ */ diff --git a/dd/dd.h b/dd/dd.h index d7e60a0..c516ad2 100644 --- a/dd/dd.h +++ b/dd/dd.h @@ -38,6 +38,9 @@ * $FreeBSD: src/bin/dd/dd.h,v 1.17 2002/02/22 20:51:00 markm Exp $ */ +#ifndef _DD_H_ +#define _DD_H_ + /* Input/output stream state. */ typedef struct { u_char *db; /* buffer address */ @@ -95,3 +98,5 @@ typedef struct { #define C_UNBLOCK 0x80000 #define C_OSYNC 0x100000 #define C_SPARSE 0x200000 + +#endif /* _DD_H_ */ diff --git a/dd/extern.h b/dd/extern.h index 278c368..0ae6330 100644 --- a/dd/extern.h +++ b/dd/extern.h @@ -38,6 +38,9 @@ * $FreeBSD: src/bin/dd/extern.h,v 1.12 2002/02/02 06:24:12 imp Exp $ */ +#ifndef _DD_EXTERN_H_ +#define _DD_EXTERN_H_ + #include void block(void); @@ -66,3 +69,5 @@ extern const u_char a2e_32V[], a2e_POSIX[]; extern const u_char e2a_32V[], e2a_POSIX[]; extern const u_char a2ibm_32V[], a2ibm_POSIX[]; extern u_char casetab[]; + +#endif /* _DD_EXTERN_H_ */ diff --git a/file_cmds.xcodeproj/project.pbxproj b/file_cmds.xcodeproj/project.pbxproj index c6e9610..e4e94ec 100644 --- a/file_cmds.xcodeproj/project.pbxproj +++ b/file_cmds.xcodeproj/project.pbxproj @@ -19,6 +19,17 @@ name = tests; productName = tests; }; + 729D07252347EC4D000716E5 /* macos_host_tools */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 729D07682347EC4D000716E5 /* Build configuration list for PBXAggregateTarget "macos_host_tools" */; + buildPhases = ( + ); + dependencies = ( + 729D074E2347EC4D000716E5 /* PBXTargetDependency */, + ); + name = macos_host_tools; + productName = macos_host_tools; + }; FC8A8C3C14B64A9D001B97AD /* shar */ = { isa = PBXAggregateTarget; buildConfigurationList = FC8A8C3D14B64A9D001B97AD /* Build configuration list for PBXAggregateTarget "shar" */; @@ -200,6 +211,8 @@ 3E59B9311D4A767600D3128C /* futimens.c in Sources */ = {isa = PBXBuildFile; fileRef = 3E59B9301D4A767600D3128C /* futimens.c */; }; 3E966CEE1FB2216F0019F7A1 /* file_cmds.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3E966CEC1FB2214F0019F7A1 /* file_cmds.plist */; }; 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 */; }; 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 */; }; @@ -311,6 +324,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 729D074F2347EC4D000716E5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B8C14B648ED001B97AD; + remoteInfo = mtree; + }; FC8A8C4914B64DE1001B97AD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; @@ -1180,6 +1200,9 @@ 3E59B9301D4A767600D3128C /* futimens.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = futimens.c; sourceTree = ""; }; 3E966CEB1FB2214F0019F7A1 /* chgrp.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = chgrp.sh; sourceTree = ""; }; 3E966CEC1FB2214F0019F7A1 /* file_cmds.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = file_cmds.plist; sourceTree = ""; }; + 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 = ""; }; + 7D0A20E92499364700F0F6D7 /* metrics.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = metrics.c; sourceTree = ""; }; 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; }; @@ -1490,6 +1513,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 729D06D8230B5E42000716E5 /* CoreFoundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1571,6 +1595,14 @@ path = tests; sourceTree = ""; }; + 729D06D6230B5E42000716E5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 729D06D7230B5E42000716E5 /* CoreFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; FCB1BDAD14B645D00070FACB = { isa = PBXGroup; children = ( @@ -1606,6 +1638,7 @@ FCB1BE8614B6460C0070FACB /* touch */, FDAD94A71808BCB700B4D5A0 /* Libraries */, FCB1BDB914B645D10070FACB /* Products */, + 729D06D6230B5E42000716E5 /* Frameworks */, ); indentWidth = 8; sourceTree = ""; @@ -1870,6 +1903,8 @@ FCB1BE4114B6460C0070FACB /* specspec.c */, FCB1BE4214B6460C0070FACB /* test */, FCB1BE4814B6460C0070FACB /* verify.c */, + 7D0A20E82499364700F0F6D7 /* metrics.h */, + 7D0A20E92499364700F0F6D7 /* metrics.c */, ); path = mtree; sourceTree = ""; @@ -2548,6 +2583,7 @@ FC8A8CC814B65F92001B97AD /* uncompress */, FC8A8C5014B650CF001B97AD /* unlink */, 3E966CE71FB2211F0019F7A1 /* tests */, + 729D07252347EC4D000716E5 /* macos_host_tools */, ); }; /* End PBXProject section */ @@ -2863,6 +2899,7 @@ FC8A8C1914B64A1A001B97AD /* create.c in Sources */, FC8A8C1A14B64A22001B97AD /* excludes.c in Sources */, FC8A8C1B14B64A27001B97AD /* misc.c in Sources */, + 7D0A20EA2499364700F0F6D7 /* metrics.c in Sources */, FC8A8C1C14B64A2D001B97AD /* mtree.c in Sources */, FC8A8C1D14B64A31001B97AD /* spec.c in Sources */, FC8A8C1E14B64A34001B97AD /* specspec.c in Sources */, @@ -2962,6 +2999,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 729D074E2347EC4D000716E5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B8C14B648ED001B97AD /* mtree */; + targetProxy = 729D074F2347EC4D000716E5 /* PBXContainerItemProxy */; + }; FC8A8C4A14B64DE1001B97AD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = FC8A8BC414B648EF001B97AD /* stat */; @@ -3328,6 +3370,14 @@ }; name = Release; }; + 729D07692347EC4D000716E5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /usr/bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; FC8A8B1114B648D7001B97AD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3465,6 +3515,7 @@ ENABLE_SHA1, ENABLE_SHA256, ); + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES; INSTALL_PATH = /usr/sbin; "OTHER_LDFLAGS[sdk=macosx*]" = "-lCrashReporterClient"; }; @@ -3631,6 +3682,14 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 729D07682347EC4D000716E5 /* Build configuration list for PBXAggregateTarget "macos_host_tools" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 729D07692347EC4D000716E5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; FC8A8B1014B648D7001B97AD /* Build configuration list for PBXNativeTarget "chmod" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/install/pathnames.h b/install/pathnames.h index 853b83c..5099eb0 100644 --- a/install/pathnames.h +++ b/install/pathnames.h @@ -33,4 +33,9 @@ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 */ +#ifndef _INSTALL_PATHNAMES_H_ +#define _INSTALL_PATHNAMES_H_ + #define _PATH_STRIP "/usr/bin/strip" + +#endif /* _INSTALL_PATHNAMES_H_ */ diff --git a/ln/ln.1 b/ln/ln.1 index 37100d3..d9edf00 100644 --- a/ln/ln.1 +++ b/ln/ln.1 @@ -32,7 +32,7 @@ .\" @(#)ln.1 8.2 (Berkeley) 12/30/93 .\" $FreeBSD: src/bin/ln/ln.1,v 1.31 2006/02/14 11:08:05 glebius Exp $ .\" -.Dd February 14, 2006 +.Dd July 12, 2019 .Dt LN 1 .Os .Sh NAME @@ -43,13 +43,13 @@ .Nm ln .Op Fl Ffhinsv .Ar source_file -.Op Ar target_file +.Op Ar link_name .Nm ln .Op Fl Ffhinsv .Ar source_file ... -.Ar target_dir +.Ar link_dirname .Nm link -.Ar source_file Ar target_file +.Ar source_file Ar link_name .Sh DESCRIPTION The .Nm ln @@ -70,7 +70,7 @@ The options are as follows: .Bl -tag -width flag .\" ========== .It Fl F -If the target file already exists and is a directory, then remove it +If the proposed link (link_name) already exists and is a directory, then remove it so that the link may occur. The .Fl F @@ -89,16 +89,16 @@ option is a no-op unless option is specified. .It Fl h If the -.Ar target_file +.Ar link_name or -.Ar target_dir +.Ar link_dirname is a symbolic link, do not follow it. This is most useful with the .Fl f option, to replace a symlink which may point to a directory. .\" ========== .It Fl f -If the target file already exists, +If the proposed link (link_name) already exists, then unlink it so that the link may occur. (The .Fl f @@ -109,12 +109,12 @@ options.) .It Fl i Cause .Nm ln -to write a prompt to standard error if the target file exists. +to write a prompt to standard error if the proposed link exists. If the response from the standard input begins with the character .Sq Li y or .Sq Li Y , -then unlink the target file so that the link may occur. +then unlink the proposed link so that the link may occur. Otherwise, do not attempt the link. (The .Fl i @@ -168,9 +168,9 @@ Given one or two arguments, creates a link to an existing file .Ar source_file . If -.Ar target_file +.Ar link_name is given, the link has that name; -.Ar target_file +.Ar link_name may also be a directory in which to place the link; otherwise it is placed in the current directory. If only the directory is specified, the link will be made @@ -180,7 +180,7 @@ to the last component of Given more than two arguments, .Nm ln makes links in -.Ar target_dir +.Ar link_dirname to all the named source files. The links made will have the same name as the files being linked to. .Pp diff --git a/ln/ln.c b/ln/ln.c index 183800e..0455dce 100644 --- a/ln/ln.c +++ b/ln/ln.c @@ -265,8 +265,8 @@ void usage(void) { (void)fprintf(stderr, "%s\n%s\n%s\n", - "usage: ln [-Ffhinsv] source_file [target_file]", - " ln [-Ffhinsv] source_file ... target_dir", - " link source_file target_file"); + "usage: ln [-Ffhinsv] source_file [link_name]", + " ln [-Ffhinsv] source_file ... linkname_dir", + " link source_file link_name"); exit(1); } diff --git a/ls/extern.h b/ls/extern.h index 6c77558..a33bd97 100644 --- a/ls/extern.h +++ b/ls/extern.h @@ -34,6 +34,9 @@ * $FreeBSD: src/bin/ls/extern.h,v 1.19 2002/05/19 02:51:36 tjr Exp $ */ +#ifndef _LS_EXTERN_H_ +#define _LS_EXTERN_H_ + int acccmp(const FTSENT *, const FTSENT *); int revacccmp(const FTSENT *, const FTSENT *); int modcmp(const FTSENT *, const FTSENT *); @@ -66,3 +69,5 @@ extern char *ansi_coloff; extern char *attrs_off; extern char *enter_bold; #endif + +#endif /* _LS_EXTERN_H_ */ diff --git a/ls/ls.c b/ls/ls.c index 7823b55..e079333 100644 --- a/ls/ls.c +++ b/ls/ls.c @@ -89,6 +89,8 @@ __RCSID("$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $"); */ #define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) +#define IS_DATALESS(sp) (f_dataless && (sp) && ((sp)->st_flags & SF_DATALESS)) + static void display(FTSENT *, FTSENT *); static u_quad_t makenines(u_quad_t); static int mastercmp(const FTSENT **, const FTSENT **); @@ -417,11 +419,11 @@ main(int argc, char *argv[]) #endif /* - * If not -F, -i, -l, -s or -t options, don't require stat + * If not -F, -i, -l, -s, -t or -% options, don't require stat * information, unless in color mode in which case we do * need this to determine which colors to display. */ - if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type && !f_sizesort + if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type && !f_sizesort && !f_dataless #ifdef COLORLS && !f_color #endif @@ -560,6 +562,11 @@ traverse(int argc, char *argv[], int options) break; } + if (IS_DATALESS(p->fts_statp)) { + fts_set(ftsp, p, FTS_SKIP); + break; + } + /* * If already output something, put out a newline as * a separator. If multiple arguments, precede each @@ -851,7 +858,7 @@ display(FTSENT *p, FTSENT *list) } else { np->mode_suffix = ' '; } - if (f_dataless && (sp->st_flags & SF_DATALESS)) { + if (IS_DATALESS(sp)) { np->mode_suffix = '%'; } if (!f_acl) { diff --git a/ls/ls.h b/ls/ls.h index 5b4769b..d1ef037 100644 --- a/ls/ls.h +++ b/ls/ls.h @@ -37,6 +37,9 @@ * $FreeBSD: src/bin/ls/ls.h,v 1.18 2002/05/19 02:51:36 tjr Exp $ */ +#ifndef _LS_H_ +#define _LS_H_ + #define NO_PRINT 1 extern long blocksize; /* block size units */ @@ -101,3 +104,5 @@ typedef struct { #endif /* __APPLE__ */ char data[1]; } NAMES; + +#endif /* _LS_H_ */ diff --git a/mtree/commoncrypto.c b/mtree/commoncrypto.c index f755631..11e97ce 100644 --- a/mtree/commoncrypto.c +++ b/mtree/commoncrypto.c @@ -10,8 +10,13 @@ #include #include #include +#include +#include +#include #include "commoncrypto.h" +#include "extern.h" +#include "metrics.h" const int kSHA256NullTerminatedBuffLen = 65; static const char hex[] = "0123456789abcdef"; @@ -52,6 +57,7 @@ Digest_File(CCDigestAlg algorithm, const char *filename, char *buf) io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) { if (error != 0) { s_error = error; + RECORD_FAILURE(27440, s_error); } (void)close(fd); (void)dispatch_semaphore_signal(sema); @@ -67,6 +73,7 @@ Digest_File(CCDigestAlg algorithm, const char *filename, char *buf) if (error != 0) { s_error = error; + RECORD_FAILURE(27441, s_error); } }); dispatch_release(io); // it will close on its own @@ -94,11 +101,28 @@ Digest_File(CCDigestAlg algorithm, const char *filename, char *buf) return buf; } -char *SHA256_Path_XATTRs(char *path, char *buf) +xattr_info * +SHA256_Path_XATTRs(char *path, char *buf) { + xattr_info *ai = NULL; + + if (mflag) { + ai = get_xdstream_privateid(path, buf); + } else { + ai = calculate_SHA256_XATTRs(path, buf); + } + + return ai; +} + + +xattr_info * +calculate_SHA256_XATTRs(char *path, char *buf) { + errno_t error = 0; char *xattrsSummary = NULL; int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW; ssize_t nameBufSize = listxattr(path, NULL, 0, options); + uint64_t xd_obj_id = 0; if (nameBufSize > 0) { char *nameBuf = malloc(nameBufSize); @@ -133,6 +157,8 @@ char *SHA256_Path_XATTRs(char *path, char *buf) char *digest; ssize_t result = 0; char *oldSummary = NULL; + //XXX Make xattr_info an array of structs if necessary + xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info)); for (int i = 0; i < xattrIndex; i++) { char *name = xattrs[i]; ssize_t xlen = getxattr(path, name, NULL, 0, 0, options); @@ -142,8 +168,11 @@ char *SHA256_Path_XATTRs(char *path, char *buf) } bzero(xattrBuf, xattrBufLen); result = getxattr(path, name, xattrBuf, xattrBufLen, 0, options); - if (result < 0) - err(1, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name, path ); + if (result < 0) { + error = errno; + RECORD_FAILURE(27442, error); + errc(1, error, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name, path); + } digest = SHA256_Data(xattrBuf, xattrBufLen, buf); if (!digest) @@ -157,6 +186,27 @@ char *SHA256_Path_XATTRs(char *path, char *buf) asprintf(&xattrsSummary, "%s, %s:%s", oldSummary, name, digest); free(oldSummary); } +#ifdef APFSIOC_XDSTREAM_OBJ_ID + // System volume has stream based xattrs only in form of resource forks + if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) { + struct xdstream_obj_id x_obj; + x_obj.xdi_name = name; + x_obj.xdi_xdtream_obj_id = 0; + + result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0); + if (!result) { + xd_obj_id = x_obj.xdi_xdtream_obj_id; + } else if (errno == ENOTTY) { + // Not an apfs filesystem, return zero. + xd_obj_id = 0; + } else { + error = errno; + RECORD_FAILURE(27444, error); + errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error); + } + } +#endif + ai->xdstream_priv_id = xd_obj_id; } free(xattrBuf); @@ -166,15 +216,82 @@ char *SHA256_Path_XATTRs(char *path, char *buf) digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf); if (!digest) err(1, "%s", xattrsSummary); + + ai->digest = digest; free(xattrsSummary); - return digest; + return ai; + } + return NULL; +} + +xattr_info * +get_xdstream_privateid(char *path, char *buf) { + errno_t error = 0; + int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW; + ssize_t nameBufSize = listxattr(path, NULL, 0, options); + uint64_t xd_obj_id = 0; + + if (nameBufSize > 0) { + //XXX Make xattr_info an array of structs if necessary + xattr_info *ai = (xattr_info *) malloc(sizeof(xattr_info)); + char *nameBuf = malloc(nameBufSize); + int result = 0; + + 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; + } + + for (int i = 0; i < xattrIndex; i++) { + char *name = xattrs[i]; + // System volume has stream based xattrs only in form of resource forks + if (!strncmp(name, XATTR_RESOURCEFORK_NAME, XATTR_MAXNAMELEN)) { + struct xdstream_obj_id x_obj; + x_obj.xdi_name = name; + x_obj.xdi_xdtream_obj_id = 0; + + result = fsctl(path, APFSIOC_XDSTREAM_OBJ_ID, &x_obj, 0); + if (!result && x_obj.xdi_xdtream_obj_id != 0) { + xd_obj_id = x_obj.xdi_xdtream_obj_id; + } else if (errno == ENOTTY) { + // Not an apfs filesystem, return zero. + xd_obj_id = 0; + } else { + error = errno; + RECORD_FAILURE(29983, error); + errc(1, error, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path, error); + } + } + } + + ai->xdstream_priv_id = xd_obj_id; + // insert a dummy value as digest is not used in presence of mflag + ai->digest = "authapfs"; + + free(nameBuf); + free(xattrs); + return ai; } - return kNone; + + return NULL; } char *SHA256_Path_ACL(char *path, char *buf) { + errno_t error = 0; int result = 0; char *data = NULL; char *digest = NULL; @@ -195,8 +312,11 @@ char *SHA256_Path_ACL(char *path, char *buf) result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW); - if (result) - err(1, "SHA256_Path_ACL: getattrlist"); + if (result) { + error = errno; + RECORD_FAILURE(27445, error); + errc(1, error, "SHA256_Path_ACL: getattrlist"); + } // if the path does not have an acl, return none if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) ) @@ -237,3 +357,22 @@ Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) { return buf; } +uint64_t +get_sibling_id(const char *path) +{ + struct attrlist attr_list = {0}; + struct attrbuf attr_buf = {0}; + errno_t error = 0; + + attr_list.bitmapcount = ATTR_BIT_MAP_COUNT; + attr_list.forkattr = ATTR_CMNEXT_LINKID; + + error = getattrlist(path, &attr_list, &attr_buf, sizeof(attr_buf), FSOPT_ATTR_CMN_EXTENDED | FSOPT_NOFOLLOW); + if (error) { + error = errno; + RECORD_FAILURE(27447, error); + errc(1, error, "get_sibling_id: getattrlist failed for %s\n", path); + } + + return attr_buf.sibling_id; +} diff --git a/mtree/commoncrypto.h b/mtree/commoncrypto.h index a894749..c547035 100644 --- a/mtree/commoncrypto.h +++ b/mtree/commoncrypto.h @@ -1,3 +1,7 @@ + +#ifndef _COMMON_CRYPTO_H_ +#define _COMMON_CRYPTO_H_ + #include #define kNone "none" @@ -9,7 +13,24 @@ extern const int kSHA256NullTerminatedBuffLen; #define RIPEMD160_File(f, b) Digest_File(kCCDigestRMD160, f, b) #define SHA256_File(f, b) Digest_File(kCCDigestSHA256, f, b) +typedef struct { + char *digest; + uint64_t xdstream_priv_id; +} xattr_info; + +struct attrbuf { + uint32_t info_length; + uint64_t sibling_id; +} __attribute__((aligned, packed)); + +typedef struct attrbuf attrbuf_t; + 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 +xattr_info *calculate_SHA256_XATTRs(char *path, char *buf); +xattr_info *SHA256_Path_XATTRs(char *path, char *buf); +xattr_info *get_xdstream_privateid(char *path, char *buf); +char *SHA256_Path_ACL(char *path, char *buf); +uint64_t get_sibling_id(const char *path); + +#endif /* _COMMON_CRYPTO_H_ */ diff --git a/mtree/compare.c b/mtree/compare.c index 7413d2f..e585928 100644 --- a/mtree/compare.c +++ b/mtree/compare.c @@ -35,6 +35,7 @@ static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93"; #include __FBSDID("$FreeBSD: src/usr.sbin/mtree/compare.c,v 1.34 2005/03/29 11:44:17 tobez Exp $"); +#include #include #include #include @@ -63,6 +64,7 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/compare.c,v 1.34 2005/03/29 11:44:17 tobe #include #include +#include "metrics.h" #include "mtree.h" #include "extern.h" @@ -77,9 +79,79 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/compare.c,v 1.34 2005/03/29 11:44:17 tobe tab = "\t"; \ } +extern CFMutableDictionaryRef dict; + +// max/min times apfs can store on disk +#define APFS_MAX_TIME 0x7fffffffffffffffLL +#define APFS_MIN_TIME (-0x7fffffffffffffffLL-1) + +static uint64_t +timespec_to_apfs_timestamp(struct timespec *ts) +{ + int64_t total; + int64_t seconds; + + // `tv_nsec' can be > one billion, so we split it into two components: + // seconds and actual nanoseconds + // this allows us to detect overflow on the *total* number of nanoseconds + // e.g. if (MAX_SECONDS+2, -2billion) is passed in, we return MAX_SECONDS + seconds = ((int64_t)ts->tv_nsec / (int64_t)NSEC_PER_SEC); + + // compute total nanoseconds, checking for overflow: + // seconds = sec + (ns/10e9) + // total = seconds*10e9 + ns%10e9 + if (__builtin_saddll_overflow(ts->tv_sec, seconds, &seconds) || + __builtin_smulll_overflow(seconds, NSEC_PER_SEC, &total) || + __builtin_saddll_overflow(((int64_t)ts->tv_nsec % (int64_t)NSEC_PER_SEC), total, &total)) { + // checking the sign of "seconds" tells us whether to cap the value at + // the max or min time + total = (ts->tv_sec > 0) ? APFS_MAX_TIME : APFS_MIN_TIME; + } + + return (uint64_t)total; +} + +static void +set_key_value_pair(void *in_key, uint64_t *in_val, bool is_string) +{ + CFStringRef key; + CFNumberRef val; + + if (is_string) { + key = CFStringCreateWithCString(NULL, (const char*)in_key, kCFStringEncodingUTF8); + + } else { + key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%llu"), *(uint64_t*)in_key); + } + + val = CFNumberCreate(NULL, kCFNumberSInt64Type, in_val); + + // we always expect the key to be not present + if (key && val) { + CFDictionaryAddValue(dict, key, val); + } else { + if (key) { + CFRelease(key); + } + if (val) { + CFRelease(val); + } + RECORD_FAILURE(1, EINVAL); + errx(1, "set_key_value_pair: key/value is null"); + } + + if (key) { + CFRelease(key); + } + if (val) { + CFRelease(val); + } +} + int compare(char *name __unused, NODE *s, FTSENT *p) { + int error = 0; struct timeval tv[2]; uint32_t val; int fd, label; @@ -92,31 +164,44 @@ compare(char *name __unused, NODE *s, FTSENT *p) label = 0; switch(s->type) { case F_BLOCK: - if (!S_ISBLK(p->fts_statp->st_mode)) + if (!S_ISBLK(p->fts_statp->st_mode)) { + RECORD_FAILURE(2, EINVAL); goto typeerr; + } break; case F_CHAR: - if (!S_ISCHR(p->fts_statp->st_mode)) + if (!S_ISCHR(p->fts_statp->st_mode)) { + RECORD_FAILURE(3, EINVAL); goto typeerr; + } break; case F_DIR: - if (!S_ISDIR(p->fts_statp->st_mode)) + if (!S_ISDIR(p->fts_statp->st_mode)) { + RECORD_FAILURE(4, EINVAL); goto typeerr; + } break; case F_FIFO: - if (!S_ISFIFO(p->fts_statp->st_mode)) + if (!S_ISFIFO(p->fts_statp->st_mode)) { + RECORD_FAILURE(5, EINVAL); goto typeerr; + } break; case F_FILE: - if (!S_ISREG(p->fts_statp->st_mode)) + if (!S_ISREG(p->fts_statp->st_mode)) { + RECORD_FAILURE(6, EINVAL); goto typeerr; + } break; case F_LINK: - if (!S_ISLNK(p->fts_statp->st_mode)) + if (!S_ISLNK(p->fts_statp->st_mode)) { + RECORD_FAILURE(7, EINVAL); goto typeerr; + } break; case F_SOCK: if (!S_ISSOCK(p->fts_statp->st_mode)) { + RECORD_FAILURE(8, EINVAL); typeerr: LABEL; (void)printf("\ttype expected %s found %s\n", ftype(s->type), inotype(p->fts_statp->st_mode)); @@ -129,28 +214,36 @@ typeerr: LABEL; LABEL; (void)printf("%suser expected %lu found %lu", tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid); - if (uflag) - if (chown(p->fts_accpath, s->st_uid, -1)) + if (uflag) { + if (chown(p->fts_accpath, s->st_uid, -1)) { + error = errno; + RECORD_FAILURE(9, error); (void)printf(" not modified: %s\n", - strerror(errno)); - else + strerror(error)); + } else { (void)printf(" modified\n"); - else + } + } else { (void)printf("\n"); + } tab = "\t"; } if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { LABEL; (void)printf("%sgid expected %lu found %lu", tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid); - if (uflag) - if (chown(p->fts_accpath, -1, s->st_gid)) + if (uflag) { + if (chown(p->fts_accpath, -1, s->st_gid)) { + error = errno; + RECORD_FAILURE(10, error); (void)printf(" not modified: %s\n", - strerror(errno)); - else + strerror(error)); + } else { (void)printf(" modified\n"); - else + } + } else { (void)printf("\n"); + } tab = "\t"; } if (s->flags & F_MODE && @@ -159,14 +252,18 @@ typeerr: LABEL; LABEL; (void)printf("%spermissions expected %#o found %#o", tab, s->st_mode, p->fts_statp->st_mode & MBITS); - if (uflag) - if (chmod(p->fts_accpath, s->st_mode)) + if (uflag) { + if (chmod(p->fts_accpath, s->st_mode)) { + error = errno; + RECORD_FAILURE(11, error); (void)printf(" not modified: %s\n", - strerror(errno)); - else + strerror(error)); + } else { (void)printf(" modified\n"); - else + } + } else { (void)printf("\n"); + } tab = "\t"; } if (s->flags & F_NLINK && s->type != F_DIR && @@ -186,35 +283,51 @@ typeerr: LABEL; 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.%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; - tv[1] = tv[0]; - if (utimes(p->fts_accpath, tv)) - (void)printf(" not modified: %s\n", - strerror(errno)); - else - (void)printf(" modified\n"); - } else - (void)printf("\n"); - tab = "\t"; + if (!mflag) { + LABEL; + (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; + tv[1] = tv[0]; + if (utimes(p->fts_accpath, tv)) { + error = errno; + RECORD_FAILURE(12, error); + (void)printf(" not modified: %s\n", + strerror(error)); + } else { + (void)printf(" modified\n"); + } + } else { + (void)printf("\n"); + } + tab = "\t"; + } + if (!insert_mod && mflag) { + uint64_t s_mod_time = timespec_to_apfs_timestamp(&s->st_mtimespec); + char *mod_string = "MODIFICATION"; + set_key_value_pair(mod_string, &s_mod_time, true); + insert_mod = 1; + } } if (s->flags & F_CKSUM) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { LABEL; + error = errno; + RECORD_FAILURE(13, error); (void)printf("%scksum: %s: %s\n", - tab, p->fts_accpath, strerror(errno)); + tab, p->fts_accpath, strerror(error)); tab = "\t"; } else if (crc(fd, &val, &len)) { (void)close(fd); LABEL; + error = errno; + RECORD_FAILURE(14, error); (void)printf("%scksum: %s: %s\n", - tab, p->fts_accpath, strerror(errno)); + tab, p->fts_accpath, strerror(error)); tab = "\t"; } else { (void)close(fd); @@ -245,26 +358,37 @@ typeerr: LABEL; (void)printf(" found \"%s\"", fflags); free(fflags); - if (uflag) - if (chflags(p->fts_accpath, (u_int)s->st_flags)) + if (uflag) { + if (chflags(p->fts_accpath, (u_int)s->st_flags)) { + error = errno; + RECORD_FAILURE(15, error); (void)printf(" not modified: %s\n", - strerror(errno)); - else + strerror(error)); + } else { (void)printf(" modified\n"); - else - (void)printf("\n"); + } + } else { + (void)printf("\n"); + } tab = "\t"; } } #ifdef ENABLE_MD5 if (s->flags & F_MD5) { char *new_digest, buf[33]; - +#ifdef __clang__ +/* clang doesn't like MD5 due to security concerns, but it's used for file data/metadata integrity.. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" new_digest = MD5File(p->fts_accpath, buf); +#pragma clang diagnostic pop +#endif if (!new_digest) { LABEL; + error = errno; + RECORD_FAILURE(16, error); printf("%sMD5: %s: %s\n", tab, p->fts_accpath, - strerror(errno)); + strerror(error)); tab = "\t"; } else if (strcmp(new_digest, s->md5digest)) { LABEL; @@ -277,12 +401,19 @@ typeerr: LABEL; #ifdef ENABLE_SHA1 if (s->flags & F_SHA1) { char *new_digest, buf[41]; - +#ifdef __clang__ +/* clang doesn't like SHA1 due to security concerns, but it's used for file data/metadata integrity.. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" new_digest = SHA1_File(p->fts_accpath, buf); +#pragma clang diagnostic pop +#endif if (!new_digest) { LABEL; + error = errno; + RECORD_FAILURE(17, error); printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath, - strerror(errno)); + strerror(error)); tab = "\t"; } else if (strcmp(new_digest, s->sha1digest)) { LABEL; @@ -295,12 +426,19 @@ typeerr: LABEL; #ifdef ENABLE_RMD160 if (s->flags & F_RMD160) { char *new_digest, buf[41]; - +#ifdef __clang__ +/* clang doesn't like RIPEMD160 due to security concerns, but it's used for file data/metadata integrity.. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" new_digest = RIPEMD160_File(p->fts_accpath, buf); +#pragma clang diagnostic pop +#endif if (!new_digest) { LABEL; + error = errno; + RECORD_FAILURE(18, error); printf("%sRIPEMD160: %s: %s\n", tab, - p->fts_accpath, strerror(errno)); + p->fts_accpath, strerror(error)); tab = "\t"; } else if (strcmp(new_digest, s->rmd160digest)) { LABEL; @@ -317,8 +455,10 @@ typeerr: LABEL; new_digest = SHA256_File(p->fts_accpath, buf); if (!new_digest) { LABEL; + error = errno; + RECORD_FAILURE(19, error); printf("%sSHA-256: %s: %s\n", tab, p->fts_accpath, - strerror(errno)); + strerror(error)); tab = "\t"; } else if (strcmp(new_digest, s->sha256digest)) { LABEL; @@ -338,32 +478,57 @@ typeerr: LABEL; 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 (!mflag) { + 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 (!insert_birth && mflag) { + uint64_t s_create_time = timespec_to_apfs_timestamp(&s->st_birthtimespec); + char *birth_string = "BIRTH"; + set_key_value_pair(birth_string, &s_create_time, true); + insert_birth = 1; + } } 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 (!mflag) { + 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 (!insert_access && mflag) { + uint64_t s_access_time = timespec_to_apfs_timestamp(&s->st_atimespec); + char *access_string = "ACCESS"; + set_key_value_pair(access_string, &s_access_time, true); + insert_access = 1; + + } } 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 (!mflag) { + 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 (!insert_change && mflag) { + uint64_t s_mod_time = timespec_to_apfs_timestamp(&s->st_ctimespec); + char *change_string = "CHANGE"; + set_key_value_pair(change_string, &s_mod_time, true); + insert_change = 1; + } } if (s->flags & F_PTIME) { int supported; @@ -371,38 +536,59 @@ typeerr: LABEL; 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, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec); tab = "\t"; + } else if (supported && ((s->st_ptimespec.tv_sec != ptimespec.tv_sec) || + (s->st_ptimespec.tv_nsec != ptimespec.tv_nsec))) { + if (!mflag) { + 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"; + } else if (!insert_parent && mflag) { + uint64_t s_added_time = timespec_to_apfs_timestamp(&s->st_ptimespec); + char *added_string = "DATEADDED"; + set_key_value_pair(added_string, &s_added_time, true); + insert_parent = 1; + } } } 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"; + char buf[kSHA256NullTerminatedBuffLen]; + xattr_info *ai; + ai = SHA256_Path_XATTRs(p->fts_accpath, buf); + if (!mflag) { + if (ai && !ai->digest) { + LABEL; + printf("%sxattrsdigest missing, expected: %s\n", tab, s->xattrsdigest); + tab = "\t"; + } else if (ai && strcmp(ai->digest, s->xattrsdigest)) { + LABEL; + printf("%sxattrsdigest expected %s found %s\n", + tab, s->xattrsdigest, ai->digest); + tab = "\t"; + } + } + if (mflag) { + if (ai && ai->xdstream_priv_id != s->xdstream_priv_id) { + set_key_value_pair((void*)&ai->xdstream_priv_id, &s->xdstream_priv_id, false); + } } + free(ai); } 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 (!mflag) { + LABEL; + (void)printf("%sinode expected %llu found %llu\n", + tab, s->st_ino, p->fts_statp->st_ino); + tab = "\t"; + } + if (mflag) { + set_key_value_pair((void*)&p->fts_statp->st_ino, &s->st_ino, false); + } } if (s->flags & F_ACL) { char *new_digest, buf[kSHA256NullTerminatedBuffLen]; @@ -418,6 +604,21 @@ typeerr: LABEL; tab = "\t"; } } + if (s->flags & F_SIBLINGID) { + uint64_t new_sibling_id = get_sibling_id(p->fts_accpath); + new_sibling_id = (new_sibling_id != p->fts_statp->st_ino) ? new_sibling_id : 0; + if (new_sibling_id != s->sibling_id) { + if (!mflag) { + LABEL; + (void)printf("%ssibling id expected %llu found %llu\n", + tab, s->sibling_id, new_sibling_id); + tab = "\t"; + } + if (mflag) { + set_key_value_pair((void*)&new_sibling_id, &s->sibling_id, false); + } + } + } return (label); } @@ -473,11 +674,15 @@ ftype(u_int type) char * rlink(char *name) { + int error = 0; static char lbuf[MAXPATHLEN]; ssize_t len; - if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1) - err(1, "line %d: %s", lineno, name); + if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1) { + error = errno; + RECORD_FAILURE(20, error); + errc(1, error, "line %d: %s", lineno, name); + } lbuf[len] = '\0'; return (lbuf); } diff --git a/mtree/create.c b/mtree/create.c index 66992a7..9300eaa 100644 --- a/mtree/create.c +++ b/mtree/create.c @@ -63,6 +63,7 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/create.c,v 1.37 2005/03/29 11:44:17 tobez #include #include #include +#include "metrics.h" #include "mtree.h" #include "extern.h" @@ -79,15 +80,17 @@ static mode_t mode; static u_long flags = 0xffffffff; static char *xattrs = kNone; static char *acl = kNone; +static u_quad_t xdstream_id; 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 *, char **, char **); +static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *, char **, char **, u_quad_t *); static void statf(int, FTSENT *); void cwalk(void) { + int error = 0; FTS *t; FTSENT *p; time_t cl; @@ -109,8 +112,11 @@ cwalk(void) argv[0] = dot; argv[1] = NULL; - if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) - err(1, "fts_open()"); + if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) { + error = errno; + RECORD_FAILURE(76, error); + errc(1, error, "fts_open()"); + } while ((p = fts_read(t))) { if (iflag) indent = p->fts_level * 4; @@ -127,7 +133,7 @@ cwalk(void) (void)printf("# %s\n", path); free(path); } - statd(t, p, &uid, &gid, &mode, &flags, &xattrs, &acl); + statd(t, p, &uid, &gid, &mode, &flags, &xattrs, &acl, &xdstream_id); statf(indent, p); break; case FTS_DP: @@ -153,13 +159,16 @@ cwalk(void) } } (void)fts_close(t); - if (sflag && keys & F_CKSUM) + if (sflag && keys & F_CKSUM) { + RECORD_FAILURE(77, WARN_CHECKSUM); warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); + } } static void statf(int indent, FTSENT *p) { + int error = 0; struct group *gr; struct passwd *pw; uint32_t val; @@ -169,8 +178,10 @@ statf(int indent, FTSENT *p) char *escaped_name; escaped_name = calloc(1, p->fts_namelen * 4 + 1); - if (escaped_name == NULL) + if (escaped_name == NULL) { + RECORD_FAILURE(78, ENOMEM); errx(1, "statf(): calloc() failed"); + } strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL | VIS_GLOB); if (iflag || S_ISDIR(p->fts_statp->st_mode)) @@ -190,15 +201,18 @@ statf(int indent, FTSENT *p) if (p->fts_statp->st_uid != uid) { if (keys & F_UNAME) { pw = getpwuid(p->fts_statp->st_uid); - if (pw != NULL) + if (pw != NULL) { output(indent, &offset, "uname=%s", pw->pw_name); - else if (wflag) + } else if (wflag) { + RECORD_FAILURE(27448, WARN_UNAME); warnx("Could not get uname for uid=%u", p->fts_statp->st_uid); - else + } else { + RECORD_FAILURE(79, EINVAL); errx(1, "Could not get uname for uid=%u", p->fts_statp->st_uid); + } } if (keys & F_UID) output(indent, &offset, "uid=%u", p->fts_statp->st_uid); @@ -206,15 +220,18 @@ statf(int indent, FTSENT *p) if (p->fts_statp->st_gid != gid) { if (keys & F_GNAME) { gr = getgrgid(p->fts_statp->st_gid); - if (gr != NULL) + if (gr != NULL) { output(indent, &offset, "gname=%s", gr->gr_name); - else if (wflag) + } else if (wflag) { + RECORD_FAILURE(27449, WARN_UNAME); warnx("Could not get gname for gid=%u", p->fts_statp->st_gid); - else + } else { + RECORD_FAILURE(80, EINVAL); errx(1, "Could not get gname for gid=%u", p->fts_statp->st_gid); + } } if (keys & F_GID) output(indent, &offset, "gid=%u", p->fts_statp->st_gid); @@ -226,44 +243,79 @@ statf(int indent, FTSENT *p) if (keys & F_SIZE) output(indent, &offset, "size=%jd", (intmax_t)p->fts_statp->st_size); - if (keys & F_TIME) - output(indent, &offset, "time=%ld.%09ld", - (long)p->fts_statp->st_mtimespec.tv_sec, - p->fts_statp->st_mtimespec.tv_nsec); + if (keys & F_TIME) { + if (tflag && !insert_mod) { + output(indent, &offset, "time=%ld.%09ld", + (long)ts.tv_sec, ts.tv_nsec); + insert_mod = 1; + } + if (!tflag) { + 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)) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || - crc(fd, &val, &len)) - err(1, "%s", p->fts_accpath); + crc(fd, &val, &len)) { + error = errno; + RECORD_FAILURE(27450, error); + errc(1, error, "%s", p->fts_accpath); + } (void)close(fd); output(indent, &offset, "cksum=%lu", (unsigned long)val); } #ifdef ENABLE_MD5 if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[33]; - +#ifdef __clang__ +/* clang doesn't like MD5 due to security concerns, but it's used for file data/metadata integrity.. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" digest = MD5File(p->fts_accpath, buf); - if (!digest) - err(1, "%s", p->fts_accpath); +#pragma clang diagnostic pop +#endif + if (!digest) { + error = errno; + RECORD_FAILURE(81, error); + errc(1, error, "%s", p->fts_accpath); + } output(indent, &offset, "md5digest=%s", digest); } #endif /* ENABLE_MD5 */ #ifdef ENABLE_SHA1 if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[41]; - +#ifdef __clang__ +/* clang doesn't like SHA1 due to security concerns, but it's used for file data/metadata integrity.. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" digest = SHA1_File(p->fts_accpath, buf); - if (!digest) - err(1, "%s", p->fts_accpath); +#pragma clang diagnostic pop +#endif + if (!digest) { + error = errno; + RECORD_FAILURE(82, error); + errc(1, error, "%s", p->fts_accpath); + } output(indent, &offset, "sha1digest=%s", digest); } #endif /* ENABLE_SHA1 */ #ifdef ENABLE_RMD160 if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[41]; - +#ifdef __clang__ +/* clang doesn't like RIPEMD160 due to security concerns, but it's used for file data/metadata integrity.. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" digest = RIPEMD160_File(p->fts_accpath, buf); - if (!digest) - err(1, "%s", p->fts_accpath); +#pragma clang diagnostic pop +#endif + if (!digest) { + error = errno; + RECORD_FAILURE(83, error); + errc(1, error, "%s", p->fts_accpath); + } output(indent, &offset, "ripemd160digest=%s", digest); } #endif /* ENABLE_RMD160 */ @@ -272,8 +324,11 @@ statf(int indent, FTSENT *p) char *digest, buf[kSHA256NullTerminatedBuffLen]; digest = SHA256_File(p->fts_accpath, buf); - if (!digest) - err(1, "%s", p->fts_accpath); + if (!digest) { + error = errno; + RECORD_FAILURE(84, error); + errc(1, error, "%s", p->fts_accpath); + } output(indent, &offset, "sha256digest=%s", digest); } #endif /* ENABLE_SHA256 */ @@ -290,38 +345,69 @@ statf(int indent, FTSENT *p) 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); + if (tflag && !insert_birth) { + output(indent, &offset, "btime=%ld.%09ld", + ts.tv_sec, ts.tv_nsec); + insert_birth = 1; + } + if (!tflag) { + 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 (tflag && !insert_access) { + output(indent, &offset, "atime=%ld.%09ld", + ts.tv_sec, ts.tv_nsec); + insert_access = 1; + } + if (!tflag) { + 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); + if (tflag && !insert_change) { + output(indent, &offset, "ctime=%ld.%09ld", + ts.tv_sec, ts.tv_nsec); + insert_change = 1; + } + if (!tflag) { + 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) { + if (tflag && !insert_parent) { + output(indent, &offset, "ptime=%ld.%09ld", + ts.tv_sec, ts.tv_nsec); + insert_parent = 1; + } + if (!tflag && supported) { output(indent, &offset, "ptime=%ld.%09ld", ptimespec.tv_sec, ptimespec.tv_nsec); } } if (keys & F_XATTRS) { - char *digest, buf[kSHA256NullTerminatedBuffLen]; + char buf[kSHA256NullTerminatedBuffLen]; + xattr_info *ai; - digest = SHA256_Path_XATTRs(p->fts_accpath, buf); - if (digest && (strcmp(digest, xattrs) != 0)) { - output(indent, &offset, "xattrsdigest=%s", digest); + ai = SHA256_Path_XATTRs(p->fts_accpath, buf); + if (ai && ai->digest) { + if ((strcmp(ai->digest, xattrs) != 0) || (ai->xdstream_priv_id != xdstream_id)) { + output(indent, &offset, "xattrsdigest=%s.%llu", ai->digest, ai->xdstream_priv_id); + } + free(ai); + ai = NULL; } } if (keys & F_INODE) { @@ -335,6 +421,11 @@ statf(int indent, FTSENT *p) output(indent, &offset, "acldigest=%s", digest); } } + if (keys & F_SIBLINGID) { + uint64_t sibling_id = get_sibling_id(p->fts_accpath); + sibling_id = (sibling_id != p->fts_statp->st_ino) ? sibling_id : 0; + output(indent, &offset, "siblingid=%llu", sibling_id); + } (void)putchar('\n'); } @@ -346,8 +437,9 @@ 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, char **pxattrs, char **pacl) +statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *pflags, char **pxattrs, char **pacl, u_quad_t *xdstream_id) { + int error = 0; FTSENT *p; gid_t sgid; uid_t suid; @@ -361,14 +453,18 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *p u_long saveflags = *pflags; char *savexattrs = *pxattrs; char *saveacl = *pacl; + u_quad_t savexdstream_id = *xdstream_id; u_short maxgid, maxuid, maxmode, maxflags; u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS]; char *fflags; static int first = 1; if ((p = fts_children(t, 0)) == NULL) { - if (errno) - err(1, "%s", RP(parent)); + error = errno; + if (error) { + RECORD_FAILURE(85, error); + errc(1, error, "%s", RP(parent)); + } return (1); } @@ -434,23 +530,29 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *p (void)printf("/set type=file"); if (keys & F_UNAME) { pw = getpwuid(saveuid); - if (pw != NULL) + if (pw != NULL) { (void)printf(" uname=%s", pw->pw_name); - else if (wflag) + } else if (wflag) { + RECORD_FAILURE(27451, WARN_UNAME); warnx( "Could not get uname for uid=%u", saveuid); - else + } else { + RECORD_FAILURE(86, EINVAL); errx(1, "Could not get uname for uid=%u", saveuid); + } } if (keys & F_UID) (void)printf(" uid=%lu", (u_long)saveuid); if (keys & F_GNAME) { gr = getgrgid(savegid); - if (gr != NULL) + if (gr != NULL) { (void)printf(" gname=%s", gr->gr_name); - else if (wflag) + } else if (wflag) { + RECORD_FAILURE(27452, WARN_UNAME); warnx("Could not get gname for gid=%u", savegid); - else + } else { + RECORD_FAILURE(87, EINVAL); errx(1, "Could not get gname for gid=%u", savegid); + } } if (keys & F_GID) (void)printf(" gid=%lu", (u_long)savegid); @@ -464,7 +566,7 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *p free(fflags); } if (keys & F_XATTRS) - (void)printf(" xattrsdigest=%s", savexattrs); + (void)printf(" xattrsdigest=%s.%llu", savexattrs, savexdstream_id); if (keys & F_ACL) (void)printf(" acldigest=%s", saveacl); (void)printf("\n"); @@ -474,6 +576,7 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *p *pflags = saveflags; *pxattrs = savexattrs; *pacl = saveacl; + *xdstream_id = savexdstream_id; } return (0); } diff --git a/mtree/excludes.c b/mtree/excludes.c index 8904771..cb432bf 100644 --- a/mtree/excludes.c +++ b/mtree/excludes.c @@ -31,17 +31,17 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/excludes.c,v 1.8 2003/10/21 08:27:05 phk Exp $"); #include -#include /* XXX for mtree.h */ #include #include +#include #include #include #include #include #include -#include "mtree.h" /* XXX for extern.h */ +#include "metrics.h" #include "extern.h" /* @@ -81,8 +81,10 @@ read_excludes_file(const char *name) str = malloc(len + 1); e = malloc(sizeof *e); - if (str == 0 || e == 0) + if (str == 0 || e == 0) { + RECORD_FAILURE(59, ENOMEM); errx(1, "memory allocation error"); + } e->glob = str; memcpy(str, line, len); str[len] = '\0'; diff --git a/mtree/extern.h b/mtree/extern.h index ff40b84..47533c2 100644 --- a/mtree/extern.h +++ b/mtree/extern.h @@ -29,6 +29,12 @@ * @(#)extern.h 8.1 (Berkeley) 6/6/93 * $FreeBSD: src/usr.sbin/mtree/extern.h,v 1.13 2004/01/11 19:38:48 phk Exp $ */ + +#ifndef _EXTERN_H_ +#define _EXTERN_H_ + +#include "mtree.h" + extern uint32_t crc_total; #ifdef _FTS_H_ @@ -55,7 +61,11 @@ const char * ftype(u_int type); extern int ftsoptions; extern u_int keys; extern int lineno; -extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag; +extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag, mflag, tflag; +extern int insert_mod, insert_birth, insert_access, insert_change, insert_parent; +extern struct timespec ts; #ifdef MAXPATHLEN extern char fullpath[MAXPATHLEN]; #endif + +#endif /* _EXTERN_H_ */ diff --git a/mtree/fix_failure_locations.py b/mtree/fix_failure_locations.py new file mode 100755 index 0000000..a49906e --- /dev/null +++ b/mtree/fix_failure_locations.py @@ -0,0 +1,77 @@ +#!/usr/bin/python + +# +# This script is used to automatically fix up the location numbers in +# calls to RECORD_FAILURE(). When adding a new call to RECORD_FAILURE, +# write it like: +# RECORD_FAILURE(0, ...); +# Don't put any white space between the open parenthesis, zero and comma. +# Once you have added the new calls to RECORD_FAILURE, then run this script, +# passing it the path to the directory, like this: +# python3 mtree/fix_failure_locations.py mtree/ +# +# This script will edit the files, changing the "0" to the next available +# location number. It will also detect and complain if you have duplicate +# location numbers. +# +# DO NOT reuse location numbers! It is best if locations are consistent across +# all versions that have that RECORD_FAILURE call. +# + +import sys +import os +import re +from collections import defaultdict +from datetime import datetime,timezone + +class LocationUpdater(object): + epoch = datetime(2020, 6, 17, 23, 22, 46, 562458, tzinfo=timezone.utc) + location_base = int((datetime.now(timezone.utc) - epoch).total_seconds() / 60) + # Match the number in "RECORD_FAILURE(," + fail_re = (re.compile('(?<=\\bRECORD_FAILURE\\()\\d+(?=,)'),re.compile('(?<=\\bRECORD_FAILURE_MSG\\()\\d+(?=,)')) + + def __init__(self, path): + self.location = self.location_base + self.path = path + # Counters for how often each location number was found + self.counts = defaultdict(int) + self.locations_changed = 0 + + # Replace the "0" in "RECORD_FAILURE(0," with next location number, in *.c + def fixLocations(self): + def replace_loc(match): + location = int(match.group(0)) + if location == 0: + # Replace location 0 with the next available location + self.location += 1 + self.locations_changed += 1 + location = self.location + # Count the number of times this location number was used + self.counts[location] += 1 + # Return the (possibly updated) location number + return str(location) + rootpath = self.path + for dirpath, dirnames, filenames in os.walk(rootpath): + for filename in filenames: + if filename.endswith(".c") or filename.endswith(".cpp"): + path = os.path.join(dirpath, filename) + content = open(path, "r").read() + for fail_re in self.fail_re: + if fail_re.search(content): + locations_changed_before = self.locations_changed + content = fail_re.sub(replace_loc, content) + if self.locations_changed != locations_changed_before: + # We updated a location number, so write the changed file + print("Updating file {}".format(path)) + open(path,"w").write(content) + + def duplicates(self): + # Return the list of keys whose count is greater than 1 + return [k for (k,v) in iter(self.counts.items()) if v > 1] + +updater = LocationUpdater(sys.argv[1]) +updater.fixLocations() +dups = updater.duplicates() +if len(dups): + print("WARNING! Duplicate location numbers: {}".format(dups)) + sys.exit(1) diff --git a/mtree/metrics.c b/mtree/metrics.c new file mode 100644 index 0000000..01a9fe2 --- /dev/null +++ b/mtree/metrics.c @@ -0,0 +1,155 @@ +/* +* Copyright (c) 2020 Apple Inc. All rights reserved. +* +* @APPLE_LICENSE_HEADER_START@ +* +* This file contains Original Code and/or Modifications of Original Code +* as defined in and that are subject to the Apple Public Source License +* Version 2.0 (the 'License'). You may not use this file except in +* compliance with the License. Please obtain a copy of the License at +* http://www.opensource.apple.com/apsl/ and read it before using this +* file. +* +* The Original Code and all software distributed under the License are +* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +* Please see the License for the specific language governing rights and +* limitations under the License. +* +* @APPLE_LICENSE_HEADER_END@ +*/ + +#include +#include + +#include "metrics.h" + +#define MAX_WARNINGS_LOGGED 5 +#define MAX_ERRORS_LOGGED 5 +#define WARN_FIRST -1 + +#ifndef ROUNDUP +#define ROUNDUP(COUNT, MULTIPLE) ((((COUNT) + (MULTIPLE) - 1) / (MULTIPLE)) * (MULTIPLE)) +#endif + +typedef struct failure_info { + int location; + int code; +} failure_info_t; + +typedef struct metrics { + FILE *file; + time_t start_time; + int warning_count; + failure_info_t warnings[MAX_WARNINGS_LOGGED]; + int error_count; + failure_info_t errors[MAX_ERRORS_LOGGED]; + int last_error_location; + char *path; + int result; +} metrics_t; +metrics_t metrics = {}; + +void +set_metrics_file(FILE *file) +{ + metrics.file = file; +} + +void +set_metric_start_time(time_t time) +{ + metrics.start_time = time; +} + +void +set_metric_path(char *path) +{ + metrics.path = strdup(path); +} + +void +mtree_record_failure(int location, int code) +{ + if (code <= WARN_FIRST) { + if (metrics.warning_count < MAX_WARNINGS_LOGGED) { + metrics.warning_count++; + } else { + // Shift up the warnings to make space for the latest one. + for (int index = 0; index < MAX_ERRORS_LOGGED - 1; index++) { + metrics.warnings[index] = metrics.warnings[index + 1]; + } + } + metrics.warnings[metrics.warning_count - 1].location = location; + metrics.warnings[metrics.warning_count - 1].code = code; + } else { + int error_index = -1; + if (metrics.error_count <= MAX_ERRORS_LOGGED) { + if (metrics.error_count > 0) { + // Log all but the last error which occured in the location and + // code arrays. The last (location, error) is logged in + // (metrics.last_error_location, metrics.error) + error_index = metrics.error_count - 1; + } + metrics.error_count++; + } else { + // Shift up the errors to make space for the latest one. + for (int index = 0; index < MAX_ERRORS_LOGGED - 1; index++) { + metrics.errors[index] = metrics.errors[index + 1]; + } + error_index = MAX_ERRORS_LOGGED - 1; + } + if (error_index >= 0) { + metrics.errors[error_index].location = metrics.last_error_location; + metrics.errors[error_index].code = metrics.result; + } + metrics.last_error_location = location; + metrics.result = code; + } +} +/* + * Note on format of metric string + * 1) dev points to the path + * 2) result is the overall result code from mtree + * 3) warnings and errors (upto 5 each) are printed in the format : + * w:(location1:code1),(location2:code2).... and + * e:(location1:code1),(location2:code2).... respectively. + * 4) fl is the last failure location of the run which is 0 if there is no failure + * 5) time is the total time taken for the run + */ +void +print_metrics_to_file(void) +{ + if (metrics.file == NULL) { + return; + } + + fprintf(metrics.file, "dev=%s result=%d ", + metrics.path ? metrics.path : "", metrics.result); + if (metrics.warning_count) { + fprintf(metrics.file, "w:"); + for (int index = 0; index < metrics.warning_count; index++) { + fprintf(metrics.file, "(%d:%d)", + metrics.warnings[index].location, metrics.warnings[index].code); + } + fprintf(metrics.file, " "); + } + if (metrics.error_count > 1) { + fprintf(metrics.file, "e:"); + for (int index = 0; index < metrics.error_count - 1; index++) { + fprintf(metrics.file, "(%d:%d)", + metrics.errors[index].location, metrics.errors[index].code); + } + fprintf(metrics.file, " "); + } + fprintf(metrics.file, "fl=%d time=%ld\n", + metrics.last_error_location, ROUNDUP((time(NULL) - metrics.start_time), 60) / 60); + + fclose(metrics.file); + if (metrics.path) { + free(metrics.path); + metrics.path = NULL; + } +} diff --git a/mtree/metrics.h b/mtree/metrics.h new file mode 100644 index 0000000..35dca43 --- /dev/null +++ b/mtree/metrics.h @@ -0,0 +1,48 @@ +/* +* Copyright (c) 2020 Apple Inc. All rights reserved. +* +* @APPLE_LICENSE_HEADER_START@ +* +* This file contains Original Code and/or Modifications of Original Code +* as defined in and that are subject to the Apple Public Source License +* Version 2.0 (the 'License'). You may not use this file except in +* compliance with the License. Please obtain a copy of the License at +* http://www.opensource.apple.com/apsl/ and read it before using this +* file. +* +* The Original Code and all software distributed under the License are +* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +* Please see the License for the specific language governing rights and +* limitations under the License. +* +* @APPLE_LICENSE_HEADER_END@ +*/ + +#ifndef _METRICS_H_ +#define _METRICS_H_ + +#include +#include + +// mtree error logging +enum mtree_result { + SUCCESS = 0, + WARN_TIME = -1, + WARN_USAGE = -2, + WARN_CHECKSUM = -3, + WARN_MISMATCH = -4, + WARN_UNAME = -5, + /* Could also be a POSIX errno value */ +}; + +void set_metrics_file(FILE *file); +void set_metric_start_time(time_t time); +void set_metric_path(char *path); +#define RECORD_FAILURE(location, error) mtree_record_failure(location, error) +void mtree_record_failure(int location, int code); +void print_metrics_to_file(void); + +#endif /* _METRICS_H_ */ diff --git a/mtree/misc.c b/mtree/misc.c index 4851868..66e566e 100644 --- a/mtree/misc.c +++ b/mtree/misc.c @@ -33,6 +33,7 @@ static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; #endif /*not lint */ #endif #include +#include __FBSDID("$FreeBSD: src/usr.sbin/mtree/misc.c,v 1.16 2005/03/29 11:44:17 tobez Exp $"); #include @@ -42,6 +43,7 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/misc.c,v 1.16 2005/03/29 11:44:17 tobez E #include #include #include +#include "metrics.h" #include "mtree.h" #include "extern.h" #import @@ -84,6 +86,7 @@ static KEY keylist[] = { #ifdef ENABLE_SHA256 {"sha256digest", F_SHA256, NEEDVALUE}, #endif + {"siblingid", F_SIBLINGID, NEEDVALUE}, {"size", F_SIZE, NEEDVALUE}, {"time", F_TIME, NEEDVALUE}, {"type", F_TYPE, NEEDVALUE}, @@ -102,8 +105,10 @@ parsekey(char *name, int *needvaluep) tmp.name = name; k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), sizeof(KEY), keycompare); - if (k == NULL) + if (k == NULL) { + RECORD_FAILURE(107, EINVAL); errx(1, "line %d: unknown keyword %s", lineno, name); + } if (needvaluep) *needvaluep = k->flags & NEEDVALUE ? 1 : 0; @@ -119,6 +124,7 @@ keycompare(const void *a, const void *b) char * flags_to_string(u_long fflags) { + int error = 0; char *string; string = fflagstostr(fflags); @@ -126,8 +132,11 @@ flags_to_string(u_long fflags) free(string); string = strdup("none"); } - if (string == NULL) - err(1, NULL); + if (string == NULL) { + error = errno; + RECORD_FAILURE(108, error); + errc(1, error, NULL); + } return string; } @@ -137,8 +146,10 @@ char * escape_path(char *string) { char *escapedPath = calloc(1, strlen(string) * 4 + 1); - if (escapedPath == NULL) + if (escapedPath == NULL) { + RECORD_FAILURE(109, ENOMEM); errx(1, "escape_path(): calloc() failed"); + } strvis(escapedPath, string, VIS_NL | VIS_CSTYLE | VIS_OCTAL); return escapedPath; @@ -154,6 +165,7 @@ struct ptimebuf { struct timespec ptime(char *path, int *supported) { + int error = 0; int ret = 0; struct ptimebuf buf; struct attrlist list = { @@ -162,7 +174,9 @@ ptime(char *path, int *supported) { }; ret = getattrlist(path, &list, &buf, sizeof(buf), FSOPT_NOFOLLOW); if (ret) { - err(1, "ptime: getattrlist"); + error = errno; + RECORD_FAILURE(110, error); + errc(1, error, "ptime: getattrlist"); } *supported = 0; diff --git a/mtree/mtree.c b/mtree/mtree.c index 22bb6f0..edf0cce 100644 --- a/mtree/mtree.c +++ b/mtree/mtree.c @@ -41,6 +41,7 @@ static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93"; #include __FBSDID("$FreeBSD: src/usr.sbin/mtree/mtree.c,v 1.29 2004/06/04 19:29:28 ru Exp $"); +#include #include #include #include @@ -49,31 +50,58 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/mtree.c,v 1.29 2004/06/04 19:29:28 ru Exp #include #include #include +#include "metrics.h" #include "mtree.h" #include "extern.h" +#define SECONDS_IN_A_DAY (60 * 60 * 24) + int ftsoptions = FTS_PHYSICAL; -int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag; +int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag, mflag, tflag; +int insert_mod, insert_birth, insert_access, insert_change, insert_parent; +struct timespec ts; u_int keys; char fullpath[MAXPATHLEN]; +CFMutableDictionaryRef dict; +char *filepath; static void usage(void); +static bool write_plist_to_file(void); + +static void +do_cleanup(void) { + + if (mflag) { + if (dict) + CFRelease(dict); + if (filepath) + free(filepath); + } +} int main(int argc, char *argv[]) { + int error = 0; int ch; char *dir, *p; int status; FILE *spec1, *spec2; + char *timestamp = NULL; + char *timeformat = "%FT%T"; + FILE *file = NULL; dir = NULL; keys = KEYDEFAULT; init_excludes(); spec1 = stdin; spec2 = NULL; + set_metric_start_time(time(NULL)); - while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:")) != -1) + atexit(do_cleanup); + atexit(print_metrics_to_file); + + while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:m:F:t:E:")) != -1) switch((char)ch) { case 'c': cflag = 1; @@ -87,14 +115,22 @@ main(int argc, char *argv[]) case 'f': if (spec1 == stdin) { spec1 = fopen(optarg, "r"); - if (spec1 == NULL) - err(1, "%s", optarg); + if (spec1 == NULL) { + error = errno; + RECORD_FAILURE(88, error); + errc(1, error, "%s", optarg); + } } else if (spec2 == NULL) { spec2 = fopen(optarg, "r"); - if (spec2 == NULL) - err(1, "%s", optarg); - } else + if (spec2 == NULL) { + error = errno; + RECORD_FAILURE(89, error); + errc(1, error, "%s", optarg); + } + } else { + RECORD_FAILURE(90, WARN_USAGE); usage(); + } break; case 'i': iflag = 1; @@ -133,8 +169,10 @@ main(int argc, char *argv[]) case 's': sflag = 1; crc_total = (uint32_t)~strtoul(optarg, &p, 0); - if (*p) + if (*p) { + RECORD_FAILURE(91, WARN_USAGE); errx(1, "illegal seed value -- %s", optarg); + } break; case 'U': Uflag = 1; @@ -152,32 +190,105 @@ main(int argc, char *argv[]) case 'X': read_excludes_file(optarg); break; + case 'm': + mflag = 1; + filepath = strdup(optarg); + dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + insert_access = insert_mod = insert_birth = insert_change = 0; + break; + case 'F': + timeformat = optarg; + break; + case 't': + timestamp = optarg; + tflag = 1; + break; + case 'E': + if (!strcmp(optarg, "-")) { + file = stdout; + } else { + file = fopen(optarg, "w"); + } + if (file == NULL) { + warnx("Could not open metrics log file %s", optarg); + } else { + set_metrics_file(file); + } + break; + case '?': default: + RECORD_FAILURE(92, WARN_USAGE); usage(); } argc -= optind; // argv += optind; - if (argc) + if (argc) { + RECORD_FAILURE(93, WARN_USAGE); usage(); + } - if (dir && chdir(dir)) - err(1, "%s", dir); + if (timestamp) { + struct tm t = {}; + char *r = strptime(timestamp, timeformat, &t); + if (r && r[0] == '\0') { + ts.tv_sec = mktime(&t); + if ((ts.tv_sec - time(NULL)) > 30 * SECONDS_IN_A_DAY) { + RECORD_FAILURE(94, WARN_TIME); + errx(1, "Time is more then 30 days in the future"); + } else if (ts.tv_sec < 0) { + RECORD_FAILURE(95, WARN_TIME); + errx(1, "Time is too far in the past"); + } + } else { + RECORD_FAILURE(96, WARN_TIME); + errx(1,"Cannot parse timestamp '%s' using format \"%s\"\n", timestamp, timeformat); + } + } + + if (dir && chdir(dir)) { + error = errno; + RECORD_FAILURE(97, error); + errc(1, error, "%s", dir); + } - if ((cflag || sflag) && !getwd(fullpath)) + if ((cflag || sflag) && !getwd(fullpath)) { + RECORD_FAILURE(98, errno); errx(1, "%s", fullpath); + } + + if (dir) { + set_metric_path(dir); + } if (cflag) { cwalk(); exit(0); } - if (spec2 != NULL) + if (spec2 != NULL) { status = mtree_specspec(spec1, spec2); - else + if (Uflag & (status == MISMATCHEXIT)) { + status = 0; + } else { + RECORD_FAILURE(99, status); + } + } else { status = mtree_verifyspec(spec1); - if (Uflag & (status == MISMATCHEXIT)) - status = 0; + if (Uflag & (status == MISMATCHEXIT)) { + status = 0; + } else { + RECORD_FAILURE(100, status); + } + if (mflag && CFDictionaryGetCount(dict)) { + if (!write_plist_to_file()) { + RECORD_FAILURE(101, EIO); + errx(1, "could not write manifest to the file\n"); + } + } + } exit(status); } @@ -189,3 +300,59 @@ usage(void) "\t[-X excludes]\n"); exit(1); } + +static bool +write_plist_to_file(void) +{ + if (!dict || !filepath) { + RECORD_FAILURE(102, EINVAL); + return false; + } + + CFIndex bytes_written = 0; + bool status = true; + + CFStringRef file_path_str = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)filepath, kCFStringEncodingUTF8); + + // Create a URL specifying the file to hold the XML data. + CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, + file_path_str, // file path name + kCFURLPOSIXPathStyle, // interpret as POSIX path + false); // is it a directory? + + if (!fileURL) { + CFRelease(file_path_str); + RECORD_FAILURE(103, EINVAL); + return false; + } + + CFWriteStreamRef output_stream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, fileURL); + + if (!output_stream) { + CFRelease(file_path_str); + CFRelease(fileURL); + RECORD_FAILURE(104, EIO); + return false; + } + + if ((status = CFWriteStreamOpen(output_stream))) { + bytes_written = CFPropertyListWrite((CFPropertyListRef)dict, output_stream, kCFPropertyListXMLFormat_v1_0, 0, NULL); + CFWriteStreamClose(output_stream); + } else { + status = false; + RECORD_FAILURE(105, EIO); + goto out; + } + + if (!bytes_written) { + status = false; + RECORD_FAILURE(106, EIO); + } + +out: + CFRelease(output_stream); + CFRelease(fileURL); + CFRelease(file_path_str); + + return status; +} diff --git a/mtree/mtree.h b/mtree/mtree.h index 02d1728..16c927b 100644 --- a/mtree/mtree.h +++ b/mtree/mtree.h @@ -30,6 +30,10 @@ * $FreeBSD: src/usr.sbin/mtree/mtree.h,v 1.7 2005/03/29 11:44:17 tobez Exp $ */ +#ifndef _MTREE_H_ +#define _MTREE_H_ + +#include #include #include @@ -60,8 +64,10 @@ typedef struct _node { struct timespec st_ctimespec; /* metadata modification time */ struct timespec st_ptimespec; /* time added to parent folder */ char *xattrsdigest; /* digest of extended attributes */ + u_quad_t xdstream_priv_id; /* private id of the xattr data stream */ ino_t st_ino; /* inode */ char *acldigest; /* digest of access control list */ + u_quad_t sibling_id; /* sibling id */ #define F_CKSUM 0x00000001 /* check sum */ #define F_DONE 0x00000002 /* directory done */ @@ -92,6 +98,7 @@ typedef struct _node { #define F_XATTRS 0x02000000 /* digest of extended attributes */ #define F_INODE 0x04000000 /* inode */ #define F_ACL 0x08000000 /* digest of access control list */ +#define F_SIBLINGID 0x10000000 /* sibling id */ u_int flags; /* items set */ #define F_BLOCK 0x001 /* block special */ @@ -109,3 +116,5 @@ typedef struct _node { #define RP(p) \ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ (p)->fts_path + 2 : (p)->fts_path) + +#endif /* _MTREE_H_ */ diff --git a/mtree/spec.c b/mtree/spec.c index 332accc..f15d857 100644 --- a/mtree/spec.c +++ b/mtree/spec.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/spec.c,v 1.22 2005/03/29 11:44:17 tobez E #include #include #include +#include "metrics.h" #include "mtree.h" #include "extern.h" @@ -74,8 +75,10 @@ mtree_readspec(FILE *fi) continue; /* Find end of line. */ - if ((p = index(buf, '\n')) == NULL) + if ((p = index(buf, '\n')) == NULL) { + RECORD_FAILURE(21, ERANGE); errx(1, "line %d too long", lineno); + } /* See if next line is continuation line. */ if (p[-1] == '\\') { @@ -102,8 +105,10 @@ mtree_readspec(FILE *fi) } /* Grab file name, "$", "set", or "unset". */ - if ((p = strtok(p, "\n\t ")) == NULL) + if ((p = strtok(p, "\n\t ")) == NULL) { + RECORD_FAILURE(22, EINVAL); errx(1, "line %d: missing field", lineno); + } if (p[0] == '/') switch(p[1]) { @@ -119,9 +124,11 @@ mtree_readspec(FILE *fi) continue; } - if (index(p, '/')) + if (index(p, '/')) { + RECORD_FAILURE(23, EINVAL); errx(1, "line %d: slash character in file name", lineno); + } if (!strcmp(p, "..")) { /* Don't go up, if haven't gone down. */ @@ -135,17 +142,22 @@ mtree_readspec(FILE *fi) last->flags |= F_DONE; continue; -noparent: errx(1, "line %d: no parent node", lineno); +noparent: RECORD_FAILURE(24, EINVAL); + errx(1, "line %d: no parent node", lineno); } - if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) + if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) { + RECORD_FAILURE(25, ENOMEM); errx(1, "calloc"); + } *centry = ginfo; #define MAGIC "?*[" if (strpbrk(p, MAGIC)) centry->flags |= F_MAGIC; - if (strunvis(centry->name, p) == -1) + if (strunvis(centry->name, p) == -1) { + RECORD_FAILURE(26, EILSEQ); errx(1, "filename %s is ill-encoded", p); + } set(NULL, centry); if (!root) { @@ -166,6 +178,7 @@ noparent: errx(1, "line %d: no parent node", lineno); static void set(char *t, NODE *ip) { + int error = 0; int type; char *kw, *val = NULL; struct group *gr; @@ -176,90 +189,122 @@ set(char *t, NODE *ip) for (; (kw = strtok(t, "= \t\n")); t = NULL) { ip->flags |= type = parsekey(kw, &value); - if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL) + if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL) { + RECORD_FAILURE(27, EINVAL); errx(1, "line %d: missing value", lineno); + } switch(type) { case F_CKSUM: ip->cksum = strtoul(val, &ep, 10); - if (*ep) + if (*ep) { + RECORD_FAILURE(28, EINVAL); errx(1, "line %d: invalid checksum %s", lineno, val); + } break; case F_MD5: ip->md5digest = strdup(val); - if(!ip->md5digest) + if (!ip->md5digest) { + RECORD_FAILURE(29, ENOMEM); errx(1, "strdup"); + } break; case F_SHA1: ip->sha1digest = strdup(val); - if(!ip->sha1digest) + if (!ip->sha1digest) { + RECORD_FAILURE(30, ENOMEM); errx(1, "strdup"); + } break; case F_SHA256: ip->sha256digest = strdup(val); - if(!ip->sha256digest) + if (!ip->sha256digest) { + RECORD_FAILURE(31, ENOMEM); errx(1, "strdup"); + } break; case F_RMD160: ip->rmd160digest = strdup(val); - if(!ip->rmd160digest) + if (!ip->rmd160digest) { + RECORD_FAILURE(32, ENOMEM); errx(1, "strdup"); + } break; case F_FLAGS: - if (strcmp("none", val) == 0) + if (strcmp("none", val) == 0) { ip->st_flags = 0; - else if (strtofflags(&val, &ip->st_flags, NULL) != 0) + } else if (strtofflags(&val, &ip->st_flags, NULL) != 0) { + RECORD_FAILURE(33, EINVAL); errx(1, "line %d: invalid flag %s",lineno, val); + } break; case F_GID: ip->st_gid = (gid_t)strtoul(val, &ep, 10); - if (*ep) + if (*ep) { + RECORD_FAILURE(34, EINVAL); errx(1, "line %d: invalid gid %s", lineno, val); + } break; case F_GNAME: - if ((gr = getgrnam(val)) == NULL) + if ((gr = getgrnam(val)) == NULL) { + RECORD_FAILURE(35, EINVAL); 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) + if ((m = setmode(val)) == NULL) { + RECORD_FAILURE(36, EINVAL); 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) + if (*ep) { + RECORD_FAILURE(37, EINVAL); errx(1, "line %d: invalid link count %s", lineno, val); + } break; case F_SIZE: ip->st_size = strtoq(val, &ep, 10); - if (*ep) + if (*ep) { + RECORD_FAILURE(38, EINVAL); errx(1, "line %d: invalid size %s", lineno, val); + } break; case F_SLINK: ip->slink = malloc(strlen(val) + 1); - if (ip->slink == NULL) + if (ip->slink == NULL) { + RECORD_FAILURE(39, ENOMEM); errx(1, "malloc"); - if (strunvis(ip->slink, val) == -1) + } + if (strunvis(ip->slink, val) == -1) { + RECORD_FAILURE(40, EILSEQ); errx(1, "symlink %s is ill-encoded", val); + } break; case F_TIME: ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); - if (*ep != '.') + if (*ep != '.') { + RECORD_FAILURE(41, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } val = ep + 1; ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); - if (*ep) + if (*ep) { + RECORD_FAILURE(42, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } break; case F_TYPE: switch(*val) { @@ -290,78 +335,127 @@ set(char *t, NODE *ip) ip->type = F_SOCK; break; default: + RECORD_FAILURE(43, EINVAL); 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) + if (*ep) { + RECORD_FAILURE(44, EINVAL); errx(1, "line %d: invalid uid %s", lineno, val); + } break; case F_UNAME: - if ((pw = getpwnam(val)) == NULL) + if ((pw = getpwnam(val)) == NULL) { + RECORD_FAILURE(45, EINVAL); 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 != '.') + if (*ep != '.') { + RECORD_FAILURE(46, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } val = ep + 1; ip->st_birthtimespec.tv_nsec = strtoul(val, &ep, 10); - if (*ep) + if (*ep) { + RECORD_FAILURE(47, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } break; case F_ATIME: ip->st_atimespec.tv_sec = strtoul(val, &ep, 10); - if (*ep != '.') + if (*ep != '.') { + RECORD_FAILURE(48, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } val = ep + 1; ip->st_atimespec.tv_nsec = strtoul(val, &ep, 10); - if (*ep) + if (*ep) { + RECORD_FAILURE(49, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } break; case F_CTIME: ip->st_ctimespec.tv_sec = strtoul(val, &ep, 10); - if (*ep != '.') + if (*ep != '.') { + RECORD_FAILURE(50, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } val = ep + 1; ip->st_ctimespec.tv_nsec = strtoul(val, &ep, 10); - if (*ep) + if (*ep) { + RECORD_FAILURE(51, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } break; case F_PTIME: ip->st_ptimespec.tv_sec = strtoul(val, &ep, 10); - if (*ep != '.') + if (*ep != '.') { + RECORD_FAILURE(52, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } val = ep + 1; ip->st_ptimespec.tv_nsec = strtoul(val, &ep, 10); - if (*ep) + if (*ep) { + RECORD_FAILURE(53, EINVAL); errx(1, "line %d: invalid time %s", lineno, val); + } break; case F_XATTRS: - ip->xattrsdigest = strdup(val); - if(!ip->xattrsdigest) - err(1, "strdup"); + ep = strtok(val,"."); + ip->xattrsdigest = strdup(ep); + if (!ip->xattrsdigest) { + error = errno; + RECORD_FAILURE(54, error); + errc(1, error, "strdup"); + } + val = strtok(NULL,"."); + if (val) { + ip->xdstream_priv_id = strtoull(val, &ep, 10); + if (*ep) { + RECORD_FAILURE(55, EINVAL); + errx(1, "line %d: invalid private id %s", + lineno, val); + } + } else { + ip->xdstream_priv_id = 0; + } break; case F_INODE: ip->st_ino = (ino_t)strtoull(val, &ep, 10); - if (*ep) - errx(1, "line %d: invalid inode %s", lineno, val); + if (*ep) { + RECORD_FAILURE(56, EINVAL); + errx(1, "line %d: invalid inode %s", + lineno, val); + } break; case F_ACL: ip->acldigest = strdup(val); - if(!ip->acldigest) - err(1, "strdup"); + if (!ip->acldigest) { + error = errno; + RECORD_FAILURE(57, error); + errc(1, error, "strdup"); + } + break; + case F_SIBLINGID: + ip->sibling_id = (quad_t)strtoull(val, &ep, 10); + if (*ep) { + RECORD_FAILURE(58, EINVAL); + errx(1, "line %d: invalid sibling id %s", lineno, val); + } } } } diff --git a/mtree/specspec.c b/mtree/specspec.c index bbcd795..cb08384 100644 --- a/mtree/specspec.c +++ b/mtree/specspec.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/specspec.c,v 1.6 2005/03/29 11:44:17 tobe #include #include #include +#include "metrics.h" #include "mtree.h" #include "extern.h" @@ -98,11 +99,13 @@ shownode(NODE *n, int f, char const *path) 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); + printf(" xattrsdigest=%s.%llu", n->xattrsdigest, n->xdstream_priv_id); if (f & F_INODE) printf(" inode=%llu", n->st_ino); if (f & F_ACL) printf(" acldigest=%s", n->acldigest); + if (f & F_SIBLINGID) + printf(" siblingid=%llu", n->sibling_id); printf("\n"); } @@ -143,14 +146,17 @@ compare_nodes(NODE *n1, NODE *n2, char const *path) return 0; } else if (n1 == NULL) { differs = n2->flags; + RECORD_FAILURE(111, WARN_MISMATCH); mismatch(n1, n2, differs, path); return (1); } else if (n2 == NULL) { differs = n1->flags; + RECORD_FAILURE(112, WARN_MISMATCH); mismatch(n1, n2, differs, path); return (1); } else if (n1->type != n2->type) { differs = 0; + RECORD_FAILURE(113, WARN_MISMATCH); mismatch(n1, n2, differs, path); return (1); } @@ -198,8 +204,11 @@ compare_nodes(NODE *n1, NODE *n2, char const *path) differs |= F_INODE; if (FS(n1, n2, F_ACL, acldigest)) differs |= F_ACL; + if (FF(n1, n2, F_SIBLINGID, sibling_id)) + differs |= F_SIBLINGID; if (differs) { + RECORD_FAILURE(114, WARN_MISMATCH); mismatch(n1, n2, differs, path); return (1); } @@ -208,7 +217,7 @@ compare_nodes(NODE *n1, NODE *n2, char const *path) static int walk_in_the_forest(NODE *t1, NODE *t2, char const *path) { - int r, i; + int r, i, c; NODE *c1, *c2, *n1, *n2; char *np; @@ -249,22 +258,49 @@ walk_in_the_forest(NODE *t1, NODE *t2, char const *path) if (c1 == NULL && c2->type == F_DIR) { asprintf(&np, "%s%s/", path, c2->name); i = walk_in_the_forest(c1, c2, np); + if (i) { + RECORD_FAILURE(115, WARN_MISMATCH); + } free(np); - i += compare_nodes(c1, c2, path); + c = compare_nodes(c1, c2, path); + if (c) { + RECORD_FAILURE(116, WARN_MISMATCH); + } + i += c; } else if (c2 == NULL && c1->type == F_DIR) { asprintf(&np, "%s%s/", path, c1->name); i = walk_in_the_forest(c1, c2, np); + if (i) { + RECORD_FAILURE(117, WARN_MISMATCH); + } free(np); - i += compare_nodes(c1, c2, path); + c = compare_nodes(c1, c2, path); + if (c) { + RECORD_FAILURE(118, WARN_MISMATCH); + } + i += c; } else if (c1 == NULL || c2 == NULL) { i = compare_nodes(c1, c2, path); + if (i) { + RECORD_FAILURE(119, WARN_MISMATCH); + } } else if (c1->type == F_DIR && c2->type == F_DIR) { asprintf(&np, "%s%s/", path, c1->name); i = walk_in_the_forest(c1, c2, np); + if (i) { + RECORD_FAILURE(120, WARN_MISMATCH); + } free(np); - i += compare_nodes(c1, c2, path); + c = compare_nodes(c1, c2, path); + if (c) { + RECORD_FAILURE(121, WARN_MISMATCH); + } + i += c; } else { i = compare_nodes(c1, c2, path); + if (i) { + RECORD_FAILURE(122, WARN_MISMATCH); + } } r += i; c1 = n1; @@ -283,7 +319,9 @@ mtree_specspec(FILE *fi, FILE *fj) root2 = mtree_readspec(fj); rval = walk_in_the_forest(root1, root2, ""); rval += compare_nodes(root1, root2, ""); - if (rval > 0) + if (rval > 0) { + RECORD_FAILURE(123, WARN_MISMATCH); return (MISMATCHEXIT); + } return (0); } diff --git a/mtree/verify.c b/mtree/verify.c index 84a37ff..7471652 100644 --- a/mtree/verify.c +++ b/mtree/verify.c @@ -45,33 +45,40 @@ __FBSDID("$FreeBSD: src/usr.sbin/mtree/verify.c,v 1.24 2005/08/11 15:43:55 brian #include #include #include +#include +#include "metrics.h" #include "mtree.h" #include "extern.h" static NODE *root; static char path[MAXPATHLEN]; -static int miss(NODE *, char *); +static int miss(NODE *, char *, size_t path_length); static int vwalk(void); int mtree_verifyspec(FILE *fi) { int rval, mval; + size_t path_length = 0; root = mtree_readspec(fi); rval = vwalk(); - mval = miss(root, path); + mval = miss(root, path, path_length); - if (rval != 0) + if (rval != 0) { + RECORD_FAILURE(60, WARN_MISMATCH); return rval; - else + } else { + RECORD_FAILURE(61, WARN_MISMATCH); return mval; + } } static int vwalk(void) { + int error = 0; FTS *t; FTSENT *p; NODE *ep, *level; @@ -81,8 +88,11 @@ vwalk(void) argv[0] = dot; argv[1] = NULL; - if ((t = fts_open(argv, ftsoptions, NULL)) == NULL) - err(1, "line %d: fts_open", lineno); + if ((t = fts_open(argv, ftsoptions, NULL)) == NULL) { + error = errno; + RECORD_FAILURE(62, error); + errc(1, error, "line %d: fts_open", lineno); + } level = root; specdepth = rval = 0; while ((p = fts_read(t))) { @@ -96,6 +106,7 @@ vwalk(void) break; case FTS_DP: if (level == NULL) { + RECORD_FAILURE(63, EINVAL); errx(1 , "invalid root in vwalk"); } if (specdepth > p->fts_level) { @@ -122,8 +133,10 @@ vwalk(void) !strcmp(ep->name, p->fts_name)) { ep->flags |= F_VISIT; if ((ep->flags & F_NOCHANGE) == 0 && - compare(ep->name, ep, p)) + compare(ep->name, ep, p)) { + RECORD_FAILURE(64, WARN_MISMATCH); rval = MISMATCHEXIT; + } if (ep->flags & F_IGN) (void)fts_set(t, p, FTS_SKIP); else if (ep->child && ep->type == F_DIR && @@ -139,26 +152,61 @@ vwalk(void) extra: if (!eflag) { (void)printf("%s extra", RP(p)); + if (rflag) { - if ((S_ISDIR(p->fts_statp->st_mode) - ? rmdir : unlink)(p->fts_accpath)) { - (void)printf(", not removed: %s", - strerror(errno)); - } else - (void)printf(", removed"); + /* rflag implies: delete stuff if "extra" is observed" */ + if (mflag) { + /* -mflag is used for sealing & verification -- use removefile for recursive behavior */ + removefile_state_t rmstate; + rmstate = removefile_state_alloc(); + if (removefile(p->fts_accpath, rmstate, (REMOVEFILE_RECURSIVE))) { + error = errno; + RECORD_FAILURE(65, error); + errx (1, "\n error deleting item (or descendant) at path %s (%s)", RP(p), strerror(error)); + } + else { + /* removefile success */ + (void) printf(", removed"); + } + removefile_state_free(rmstate); + + } + else { + /* legacy: use rmdir/unlink if "-m" not specified */ + int syserr = 0; + + if (S_ISDIR(p->fts_statp->st_mode)){ + syserr = rmdir(p->fts_accpath); + } + else { + syserr = unlink(p->fts_accpath); + } + + /* log failures */ + if (syserr) { + error = errno; + RECORD_FAILURE(66, error); + (void) printf(", not removed :%s", strerror(error)); + } + } + } else if (mflag) { + RECORD_FAILURE(68956, WARN_MISMATCH); + errx(1, "cannot generate the XML dictionary"); } (void)putchar('\n'); } (void)fts_set(t, p, FTS_SKIP); } (void)fts_close(t); - if (sflag) + if (sflag) { + RECORD_FAILURE(67, WARN_CHECKSUM); warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); + } return (rval); } static int -miss(NODE *p, char *tail) +miss(NODE *p, char *tail, size_t path_length) { int create; char *tp; @@ -166,10 +214,17 @@ miss(NODE *p, char *tail) int serr; int rval = 0; int rrval = 0; + size_t file_name_length = 0; for (; p; p = p->next) { if (p->type != F_DIR && (dflag || p->flags & F_VISIT)) continue; + file_name_length = strnlen(p->name, MAXPATHLEN); + path_length += file_name_length; + if (path_length >= MAXPATHLEN) { + RECORD_FAILURE(61971, ENAMETOOLONG); + continue; + } (void)strcpy(tail, p->name); if (!(p->flags & F_VISIT)) { /* Don't print missing message if file exists as a @@ -180,6 +235,7 @@ miss(NODE *p, char *tail) p->flags |= F_VISIT; } else { (void)printf("%s missing", path); + RECORD_FAILURE(68, WARN_MISMATCH); rval = MISMATCHEXIT; } } @@ -194,16 +250,19 @@ miss(NODE *p, char *tail) else type = "directory"; if (!(p->flags & F_VISIT) && uflag) { - if (!(p->flags & (F_UID | F_UNAME))) + if (!(p->flags & (F_UID | F_UNAME))) { (void)printf(" (%s not created: user not specified)", type); - else if (!(p->flags & (F_GID | F_GNAME))) + } else if (!(p->flags & (F_GID | F_GNAME))) { (void)printf(" (%s not created: group not specified)", type); - else if (p->type == F_LINK) { - if (symlink(p->slink, path)) + } else if (p->type == F_LINK) { + if (symlink(p->slink, path)) { + serr = errno; + RECORD_FAILURE(69, serr); (void)printf(" (symlink not created: %s)\n", - strerror(errno)); - else + strerror(serr)); + } else { (void)printf(" (created)\n"); + } if (lchown(path, p->st_uid, p->st_gid) == -1) { serr = errno; if (p->st_uid == (uid_t)-1) @@ -215,16 +274,20 @@ miss(NODE *p, char *tail) what = "user"; errno = serr; } + serr = errno; + RECORD_FAILURE(70, serr); (void)printf("%s: %s not modified: %s" - "\n", path, what, strerror(errno)); + "\n", path, what, strerror(serr)); } continue; - } else if (!(p->flags & F_MODE)) + } else if (!(p->flags & F_MODE)) { (void)printf(" (directory not created: mode not specified)"); - else if (mkdir(path, S_IRWXU)) + } else if (mkdir(path, S_IRWXU)) { + serr = errno; + RECORD_FAILURE(71, serr); (void)printf(" (directory not created: %s)", - strerror(errno)); - else { + strerror(serr)); + } else { create = 1; (void)printf(" (created)"); } @@ -234,9 +297,13 @@ miss(NODE *p, char *tail) for (tp = tail; *tp; ++tp); *tp = '/'; - rrval = miss(p->child, tp + 1); - if (rrval != 0) + ++path_length; + rrval = miss(p->child, tp + 1, path_length); + if (rrval != 0) { + RECORD_FAILURE(72, WARN_MISMATCH); rval = rrval; + } + path_length -= (file_name_length + 1); *tp = '\0'; if (!create) @@ -251,16 +318,24 @@ miss(NODE *p, char *tail) what = "user"; errno = serr; } + serr = errno; + RECORD_FAILURE(73, serr); (void)printf("%s: %s not modified: %s\n", - path, what, strerror(errno)); + path, what, strerror(serr)); } - if (chmod(path, p->st_mode)) + if (chmod(path, p->st_mode)) { + serr = errno; + RECORD_FAILURE(74, serr); (void)printf("%s: permissions not set: %s\n", - path, strerror(errno)); + path, strerror(serr)); + } if ((p->flags & F_FLAGS) && p->st_flags && - chflags(path, (u_int)p->st_flags)) + chflags(path, (u_int)p->st_flags)) { + serr = errno; + RECORD_FAILURE(75, serr); (void)printf("%s: file flags not set: %s\n", - path, strerror(errno)); + path, strerror(serr)); + } } return rval; } diff --git a/mv/pathnames.h b/mv/pathnames.h index 514678f..fe1d6cd 100644 --- a/mv/pathnames.h +++ b/mv/pathnames.h @@ -34,7 +34,12 @@ * $FreeBSD: src/bin/mv/pathnames.h,v 1.6 2002/05/17 11:38:48 jmallett Exp $ */ +#ifndef _MV_PATHNAMES_H_ +#define _MV_PATHNAMES_H_ + #define _PATH_RM "/bin/rm" #ifdef __APPLE__ #define _PATH_CP "/bin/cp" #endif /* __APPLE__ */ + +#endif /* _MV_PATHNAMES_H_ */ diff --git a/pax/cache.h b/pax/cache.h index 5c7b9b8..1a269d4 100644 --- a/pax/cache.h +++ b/pax/cache.h @@ -36,6 +36,9 @@ * @(#)cache.h 8.1 (Berkeley) 5/31/93 */ +#ifndef _CACHE_H_ +#define _CACHE_H_ + /* * Constants and data structures used to implement group and password file * caches. Traditional passwd/group cache routines perform quite poorly with @@ -71,3 +74,5 @@ typedef struct gidc { char name[GNMLEN]; /* gid name */ gid_t gid; /* cached gid */ } GIDC; + +#endif /* _CACHE_H_ */ diff --git a/pax/cpio.h b/pax/cpio.h index dfbd03f..93617b4 100644 --- a/pax/cpio.h +++ b/pax/cpio.h @@ -36,6 +36,9 @@ * @(#)cpio.h 8.1 (Berkeley) 5/31/93 */ +#ifndef _CPIO_H_ +#define _CPIO_H_ + /* * Defines common to all versions of cpio */ @@ -148,3 +151,5 @@ typedef struct { #define VCPIO_PAD(x) ((4 - ((x) & 3)) & 3) /* pad to next 4 byte word */ #define VCPIO_MASK 0xffffffff /* mask for dev/ino fields */ #endif /* _PAX_ */ + +#endif /* _CPIO_H_ */ diff --git a/pax/extern.h b/pax/extern.h index 5dad640..16d3195 100644 --- a/pax/extern.h +++ b/pax/extern.h @@ -36,6 +36,9 @@ * @(#)extern.h 8.2 (Berkeley) 4/18/94 */ +#ifndef _PAX_EXTERN_H_ +#define _PAX_EXTERN_H_ + /* * External references from each source file */ @@ -340,3 +343,5 @@ void tty_prnt(const char *, ...); int tty_read(char *, int); void paxwarn(int, const char *, ...); void syswarn(int, int, const char *, ...); + +#endif /* _PAX_EXTERN_H_ */ diff --git a/pax/ftree.h b/pax/ftree.h index 9b4cffc..0203815 100644 --- a/pax/ftree.h +++ b/pax/ftree.h @@ -36,6 +36,9 @@ * @(#)ftree.h 8.1 (Berkeley) 5/31/93 */ +#ifndef _FTREE_H_ +#define _FTREE_H_ + /* * Data structure used by the ftree.c routines to store the file args to be * handed to fts(). It keeps a reference count of which args generated a @@ -49,3 +52,5 @@ typedef struct ftree { int chflg; /* change directory flag */ struct ftree *fow; /* pointer to next entry on list */ } FTREE; + +#endif /* _FTREE_H_ */ diff --git a/pax/gen_subs.c b/pax/gen_subs.c index 0b75b0d..cf821fb 100644 --- a/pax/gen_subs.c +++ b/pax/gen_subs.c @@ -216,17 +216,26 @@ ls_tty(ARCHD *arcn) void safe_print(const char *str, FILE *fp) { - char visbuf[5]; - const char *cp; - /* - * if printing to a tty, use vis(3) to print special characters. + * if printing to a tty, use strvis(3) to print special characters. */ if (isatty(fileno(fp))) { - for (cp = str; *cp; cp++) { - (void)vis(visbuf, cp[0], VIS_CSTYLE, cp[1]); - (void)fputs(visbuf, fp); + /* + * The size of visbuf must be four times the number + * of bytes encoded from str (plus one for the NUL). + */ + char *visbuf = (char *) malloc(sizeof(char) * (4 * strlen(str) + 1)); + if (visbuf == NULL) { + paxwarn(1, "Out of memory"); + return; } + /* + * using strvis(3) instead of vis(3) to account for multibyte + * characters + */ + (void)strvis(visbuf, str, VIS_CSTYLE); + (void)fputs(visbuf, fp); + free(visbuf); } else { (void)fputs(str, fp); } diff --git a/pax/options.h b/pax/options.h index 873d8d5..89eeaa5 100644 --- a/pax/options.h +++ b/pax/options.h @@ -36,6 +36,9 @@ * @(#)options.h 8.2 (Berkeley) 4/18/94 */ +#ifndef _OPTIONS_H_ +#define _OPTIONS_H_ + /* * argv[0] names. Used for tar and cpio emulation */ @@ -111,3 +114,5 @@ #define BDARCH (CF|KF|LF|NF|PF|RF|CDF|CEF|CYF|CZF) #define BDCOPY (AF|BF|FF|CBF|CEF) #define BDLIST (AF|BF|IF|KF|LF|PF|RF|TF|UF|WF|XF|CBF|CDF|CHF|CLF|CPF|CXF|CYF|CZF) + +#endif /* _OPTIONS_H_ */ diff --git a/pax/pat_rep.c b/pax/pat_rep.c index ff2819e..fa778b9 100644 --- a/pax/pat_rep.c +++ b/pax/pat_rep.c @@ -74,6 +74,10 @@ static int rep_name(char *, size_t, int *, int); int tty_rename(ARCHD *); static int fix_path(char *, int *, char *, int); static int fn_match(char *, char *, char **); +#ifdef _HAVE_REGCOMP_ +static char* extract_equiv_pat(char *, char **); +static int regex_match(char *, char *, char **); +#endif static char * range_match(char *, int); static int resub(regex_t *, regmatch_t *, char *, char *, char *, char *); @@ -495,6 +499,10 @@ fn_match(char *pattern, char *string, char **pend) { char c; char test; +#ifdef _HAVE_REGCOMP_ + char *equiv_pat; + char *pat_pend = NULL; +#endif *pend = NULL; for (;;) { @@ -549,10 +557,33 @@ fn_match(char *pattern, char *string, char **pend) /* * range match */ +#ifdef _HAVE_REGCOMP_ + /* + * Check for equivalence class and use regex_match to + * handle this case. Note pattern should include the + * opening bracket '[' + */ + equiv_pat = extract_equiv_pat(pattern-1, &pat_pend); + if (equiv_pat) { + if (regex_match(equiv_pat, string, &string) == -1) { + free (equiv_pat); + return (-1); + } + + free(equiv_pat); + + /* + * Update the pattern string + */ + pattern = pat_pend; + break; + } +#endif if (((test = *string++) == '\0') || ((pattern = range_match(pattern, test)) == NULL)) return (-1); break; + case '\\': default: if (c != *string++) @@ -563,6 +594,124 @@ fn_match(char *pattern, char *string, char **pend) /* NOTREACHED */ } +#ifdef _HAVE_REGCOMP_ +static char* +extract_equiv_pat(char *pattern, char **pend) +{ + int pat_len = 2; + int found = 0; + int is_double_bracket = 0; + char* equiv_pat = NULL; + + if (*pattern == '\0' || pattern[1] == '\0' || pattern[2] == '\0') + return NULL; + + /* + * check if the pattern is + * "[= =]", "[[= =][= =]]", "[: :]", or "[[: :][: :]]" + * note that the full "pattern" string needs to be passed in + */ + is_double_bracket = (*pattern == '[' && pattern[1] == '['); + if (!(*pattern == '[') && !is_double_bracket) { + return NULL; + } + + pattern ++; + + if (is_double_bracket) { + pattern ++; + pat_len ++; + } + + if (!(*pattern == ':') && !(*pattern == '=')) { + return NULL; + } + + pattern ++; + + + for(; *pattern != '\0'; pat_len++, pattern++) { + if (!is_double_bracket) { + if ((*pattern == '=' || *pattern == ':') + && pattern[1] == ']') { + found = 1; + pattern += 2; + pat_len += 2; + break; + } + + } else { + if ((*pattern == '=' || *pattern == ':') + && pattern[1] == ']' && pattern[2] == ']') { + found = 1; + pattern += 3; + pat_len += 3; + break; + } + + } + } + + if (!found) { + return NULL; + } + + equiv_pat = strndup(pattern-pat_len, pat_len); + + if (equiv_pat == NULL) { + paxwarn(1, "Out of memory"); + return NULL; + } + + /* + * set pend to the remaining pattern to be matched + */ + if (pend != NULL) { + *pend = pattern; + } + + return equiv_pat; +} + +static int +regex_match(char *pattern, char *string, char **pend) +{ + int res; + regex_t preg; + regmatch_t pmatch; + char rebuf[BUFSIZ]; + + if ((res = regcomp(&(preg), pattern, REG_EXTENDED)) != 0) { + regerror(res, &(preg), rebuf, sizeof(rebuf)); + paxwarn(1, "%s while compiling pattern %s", rebuf, pattern); + return(-1); + } + + if (regexec(&(preg), string, 1, &(pmatch), 0) != 0) { + regfree(&(preg)); + return(-1); + } + + regfree(&(preg)); + + /* + * starting position of the match must be 0 + */ + if (pmatch.rm_so != 0) { + return(-1); + } + + /* + * set pend to the remaining string to be matched + */ + if (pend != NULL) { + *pend = string + pmatch.rm_eo; + } + + return(0); +} +#endif + static char * range_match(char *pattern, int test) { diff --git a/pax/pat_rep.h b/pax/pat_rep.h index 9138d22..4d65a8e 100644 --- a/pax/pat_rep.h +++ b/pax/pat_rep.h @@ -36,6 +36,9 @@ * @(#)pat_rep.h 8.1 (Berkeley) 5/31/93 */ +#ifndef _PAT_REP_H_ +#define _PAT_REP_H_ + /* * data structure for storing user supplied replacement strings (-s) */ @@ -49,3 +52,5 @@ typedef struct replace { } REPLACE; int tty_rename(ARCHD *); /* used for -o invalid=rename recovery */ + +#endif /* _PAT_REP_H_ */ diff --git a/pax/pax.c b/pax/pax.c index 7c278f0..ec180b7 100644 --- a/pax/pax.c +++ b/pax/pax.c @@ -63,6 +63,7 @@ __used static const char rcsid[] = "$OpenBSD: pax.c,v 1.28 2005/08/04 10:02:44 m #include #include #include +#include #include "pax.h" #include "extern.h" static int gen_init(void); @@ -236,6 +237,10 @@ main(int argc, char **argv) { char *tmpdir; size_t tdlen; +#ifdef _HAVE_REGCOMP_ + setlocale(LC_CTYPE, ""); + setlocale(LC_COLLATE, ""); +#endif listf = stderr; /* diff --git a/pax/pax.h b/pax/pax.h index 4d74aa0..1caff0c 100644 --- a/pax/pax.h +++ b/pax/pax.h @@ -36,6 +36,9 @@ * @(#)pax.h 8.2 (Berkeley) 4/18/94 */ +#ifndef _PAX_H_ +#define _PAX_H_ + /* * BSD PAX global data structures and constants. */ @@ -246,4 +249,7 @@ typedef struct oplist { #define HEX 16 #define OCT 8 #define _PAX_ 1 +#define _HAVE_REGCOMP_ 1 #define _TFILE_BASE "paxXXXXXXXXXX" + +#endif /* _PAX_H_ */ diff --git a/pax/pax_format.h b/pax/pax_format.h index 506ce58..4ac4094 100644 --- a/pax/pax_format.h +++ b/pax/pax_format.h @@ -36,6 +36,9 @@ * @(#)tar.h 8.2 (Berkeley) 4/18/94 */ +#ifndef _PAX_FORMAT_H_ +#define _PAX_FORMAT_H_ + /* * defines and data structures common to all tar formats */ @@ -151,3 +154,5 @@ typedef struct { char devminor[8]; /* minor device number */ char prefix[TPFSZ]; /* linked to name */ } HD_USTAR; + +#endif /* _PAX_FORMAT_H_ */ diff --git a/pax/sel_subs.h b/pax/sel_subs.h index b699ee8..4516545 100644 --- a/pax/sel_subs.h +++ b/pax/sel_subs.h @@ -36,6 +36,9 @@ * @(#)sel_subs.h 8.1 (Berkeley) 5/31/93 */ +#ifndef _SEL_SUBS_H_ +#define _SEL_SUBS_H_ + /* * data structure for storing uid/grp selects (-U, -G non standard options) */ @@ -70,3 +73,5 @@ typedef struct time_rng { #define CMPBOTH (CMPMTME|CMPCTME) /* compare inode and mod time */ struct time_rng *fow; /* next pattern */ } TIME_RNG; + +#endif /* _SEL_SUBS_H_ */ diff --git a/pax/tables.h b/pax/tables.h index bc19f06..3b1970f 100644 --- a/pax/tables.h +++ b/pax/tables.h @@ -36,6 +36,9 @@ * @(#)tables.h 8.1 (Berkeley) 5/31/93 */ +#ifndef _TABLES_H_ +#define _TABLES_H_ + /* * data structures and constants used by the different databases kept by pax */ @@ -168,3 +171,5 @@ typedef struct dirdata { u_int16_t mode; /* file mode to restore */ u_int16_t frc_mode; /* do we force mode settings? */ } DIRDATA; + +#endif /* _TABLES_H_ */ diff --git a/pax/tar.h b/pax/tar.h index 61bc6e1..a78813c 100644 --- a/pax/tar.h +++ b/pax/tar.h @@ -36,6 +36,9 @@ * @(#)tar.h 8.2 (Berkeley) 4/18/94 */ +#ifndef _TAR_H_ +#define _TAR_H_ + /* * defines and data structures common to all tar formats */ @@ -154,3 +157,5 @@ typedef struct { char devminor[8]; /* minor device number */ char prefix[TPFSZ]; /* linked to name */ } HD_USTAR; + +#endif /* _TAR_H_ */ diff --git a/rm/rm.c b/rm/rm.c index 956c245..ef457c5 100644 --- a/rm/rm.c +++ b/rm/rm.c @@ -286,12 +286,16 @@ rm_tree(argv) case FTS_DP: case FTS_DNR: #if __APPLE__ - if (p->fts_statp != NULL && (p->fts_statp->st_flags & SF_DATALESS) != 0) - rval = unlinkat(AT_FDCWD, p->fts_accpath, AT_REMOVEDIR_DATALESS); - else + rval = unlinkat(AT_FDCWD, p->fts_accpath, AT_REMOVEDIR_DATALESS); + if (rval == -1 && errno == EINVAL) { + /* + * Kernel rejected AT_REMOVEDIR_DATALESS? + * I guess we fall back on the painful + * route (but it's better than failing). + */ rval = rmdir(p->fts_accpath); + } #else - rval = rmdir(p->fts_accpath); #endif if (rval == 0 || (fflag && errno == ENOENT)) { -- 2.45.2