* SUCH DAMAGE.
*/
+#ifndef _CHMOD_ACL_H_
+#define _CHMOD_ACL_H_
+
#ifdef __APPLE__
#include <pwd.h>
#include <grp.h>
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_ */
* $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 <sys/cdefs.h>
__BEGIN_DECLS
int csum2(int, uint32_t *, off_t *);
int crc32(int, uint32_t *, off_t *);
__END_DECLS
+
+#endif /* _CKSUM_EXTERN_H_ */
* $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 */
int preserve_fd_acls(int, int);
void usage(void);
__END_DECLS
+
+#endif /* _CP_EXTERN_H_ */
* $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 */
#define C_UNBLOCK 0x80000
#define C_OSYNC 0x100000
#define C_SPARSE 0x200000
+
+#endif /* _DD_H_ */
* $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 <sys/cdefs.h>
void block(void);
extern const u_char e2a_32V[], e2a_POSIX[];
extern const u_char a2ibm_32V[], a2ibm_POSIX[];
extern u_char casetab[];
+
+#endif /* _DD_EXTERN_H_ */
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" */;
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 */; };
/* 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 */;
3E59B9301D4A767600D3128C /* futimens.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = futimens.c; sourceTree = "<group>"; };
3E966CEB1FB2214F0019F7A1 /* chgrp.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = chgrp.sh; sourceTree = "<group>"; };
3E966CEC1FB2214F0019F7A1 /* file_cmds.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = file_cmds.plist; sourceTree = "<group>"; };
+ 729D06D7230B5E42000716E5 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+ 7D0A20E82499364700F0F6D7 /* metrics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metrics.h; sourceTree = "<group>"; };
+ 7D0A20E92499364700F0F6D7 /* metrics.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = metrics.c; sourceTree = "<group>"; };
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; };
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 729D06D8230B5E42000716E5 /* CoreFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
path = tests;
sourceTree = "<group>";
};
+ 729D06D6230B5E42000716E5 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 729D06D7230B5E42000716E5 /* CoreFoundation.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
FCB1BDAD14B645D00070FACB = {
isa = PBXGroup;
children = (
FCB1BE8614B6460C0070FACB /* touch */,
FDAD94A71808BCB700B4D5A0 /* Libraries */,
FCB1BDB914B645D10070FACB /* Products */,
+ 729D06D6230B5E42000716E5 /* Frameworks */,
);
indentWidth = 8;
sourceTree = "<group>";
FCB1BE4114B6460C0070FACB /* specspec.c */,
FCB1BE4214B6460C0070FACB /* test */,
FCB1BE4814B6460C0070FACB /* verify.c */,
+ 7D0A20E82499364700F0F6D7 /* metrics.h */,
+ 7D0A20E92499364700F0F6D7 /* metrics.c */,
);
path = mtree;
sourceTree = "<group>";
FC8A8CC814B65F92001B97AD /* uncompress */,
FC8A8C5014B650CF001B97AD /* unlink */,
3E966CE71FB2211F0019F7A1 /* tests */,
+ 729D07252347EC4D000716E5 /* macos_host_tools */,
);
};
/* End PBXProject section */
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 */,
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 729D074E2347EC4D000716E5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FC8A8B8C14B648ED001B97AD /* mtree */;
+ targetProxy = 729D074F2347EC4D000716E5 /* PBXContainerItemProxy */;
+ };
FC8A8C4A14B64DE1001B97AD /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = FC8A8BC414B648EF001B97AD /* stat */;
};
name = Release;
};
+ 729D07692347EC4D000716E5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INSTALL_PATH = /usr/bin;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
FC8A8B1114B648D7001B97AD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ENABLE_SHA1,
ENABLE_SHA256,
);
+ GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
INSTALL_PATH = /usr/sbin;
"OTHER_LDFLAGS[sdk=macosx*]" = "-lCrashReporterClient";
};
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 = (
* @(#)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_ */
.\" @(#)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
.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
.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
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
.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
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
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
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);
}
* $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 *);
extern char *attrs_off;
extern char *enter_bold;
#endif
+
+#endif /* _LS_EXTERN_H_ */
*/
#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 **);
#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
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
} else {
np->mode_suffix = ' ';
}
- if (f_dataless && (sp->st_flags & SF_DATALESS)) {
+ if (IS_DATALESS(sp)) {
np->mode_suffix = '%';
}
if (!f_acl) {
* $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 */
#endif /* __APPLE__ */
char data[1];
} NAMES;
+
+#endif /* _LS_H_ */
#include <stdio.h>
#include <sys/attr.h>
#include <unistd.h>
+#include <sys/xattr.h>
+#include <sys/mount.h>
+#include <apfs/apfs_fsctl.h>
#include "commoncrypto.h"
+#include "extern.h"
+#include "metrics.h"
const int kSHA256NullTerminatedBuffLen = 65;
static const char hex[] = "0123456789abcdef";
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);
if (error != 0) {
s_error = error;
+ RECORD_FAILURE(27441, s_error);
}
});
dispatch_release(io); // it will close on its own
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);
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);
}
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)
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);
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;
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 ) )
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;
+}
+
+#ifndef _COMMON_CRYPTO_H_
+#define _COMMON_CRYPTO_H_
+
#include <CommonCrypto/CommonDigestSPI.h>
#define kNone "none"
#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_ */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/usr.sbin/mtree/compare.c,v 1.34 2005/03/29 11:44:17 tobez Exp $");
+#include <CoreFoundation/CoreFoundation.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <vis.h>
+#include "metrics.h"
#include "mtree.h"
#include "extern.h"
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;
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));
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 &&
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 &&
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);
(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;
#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;
#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;
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;
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;
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];
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);
}
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);
}
#include <time.h>
#include <unistd.h>
#include <vis.h>
+#include "metrics.h"
#include "mtree.h"
#include "extern.h"
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;
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;
(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:
}
}
(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;
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))
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);
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);
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 */
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 */
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) {
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');
}
#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;
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);
}
(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);
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");
*pflags = saveflags;
*pxattrs = savexattrs;
*pacl = saveacl;
+ *xdstream_id = savexdstream_id;
}
return (0);
}
__FBSDID("$FreeBSD: src/usr.sbin/mtree/excludes.c,v 1.8 2003/10/21 08:27:05 phk Exp $");
#include <sys/types.h>
-#include <sys/time.h> /* XXX for mtree.h */
#include <sys/queue.h>
#include <err.h>
+#include <errno.h>
#include <fnmatch.h>
#include <fts.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
-#include "mtree.h" /* XXX for extern.h */
+#include "metrics.h"
#include "extern.h"
/*
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';
* @(#)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_
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_ */
--- /dev/null
+#!/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(<number>,"
+ 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)
--- /dev/null
+/*
+* 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 <stdlib.h>
+#include <string.h>
+
+#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;
+ }
+}
--- /dev/null
+/*
+* 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 <sys/time.h>
+#include <stdio.h>
+
+// 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_ */
#endif /*not lint */
#endif
#include <sys/cdefs.h>
+#include <errno.h>
__FBSDID("$FreeBSD: src/usr.sbin/mtree/misc.c,v 1.16 2005/03/29 11:44:17 tobez Exp $");
#include <sys/types.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
+#include "metrics.h"
#include "mtree.h"
#include "extern.h"
#import <sys/attr.h>
#ifdef ENABLE_SHA256
{"sha256digest", F_SHA256, NEEDVALUE},
#endif
+ {"siblingid", F_SIBLINGID, NEEDVALUE},
{"size", F_SIZE, NEEDVALUE},
{"time", F_TIME, NEEDVALUE},
{"type", F_TYPE, NEEDVALUE},
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;
char *
flags_to_string(u_long fflags)
{
+ int error = 0;
char *string;
string = fflagstostr(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;
}
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;
struct timespec
ptime(char *path, int *supported) {
+ int error = 0;
int ret = 0;
struct ptimebuf buf;
struct attrlist list = {
};
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;
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/usr.sbin/mtree/mtree.c,v 1.29 2004/06/04 19:29:28 ru Exp $");
+#include <CoreFoundation/CoreFoundation.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <err.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
+#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;
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;
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;
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);
}
"\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;
+}
* $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 <sys/time.h>
#include <string.h>
#include <stdlib.h>
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 */
#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 */
#define RP(p) \
((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
(p)->fts_path + 2 : (p)->fts_path)
+
+#endif /* _MTREE_H_ */
#include <stdint.h>
#include <unistd.h>
#include <vis.h>
+#include "metrics.h"
#include "mtree.h"
#include "extern.h"
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] == '\\') {
}
/* 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]) {
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. */
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) {
static void
set(char *t, NODE *ip)
{
+ int error = 0;
int type;
char *kw, *val = NULL;
struct group *gr;
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) {
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);
+ }
}
}
}
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
+#include "metrics.h"
#include "mtree.h"
#include "extern.h"
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");
}
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);
}
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);
}
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;
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;
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);
}
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
+#include <removefile.h>
+#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;
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))) {
break;
case FTS_DP:
if (level == NULL) {
+ RECORD_FAILURE(63, EINVAL);
errx(1 , "invalid root in vwalk");
}
if (specdepth > p->fts_level) {
!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 &&
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;
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
p->flags |= F_VISIT;
} else {
(void)printf("%s missing", path);
+ RECORD_FAILURE(68, WARN_MISMATCH);
rval = MISMATCHEXIT;
}
}
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)
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)");
}
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)
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;
}
* $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_ */
* @(#)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
char name[GNMLEN]; /* gid name */
gid_t gid; /* cached gid */
} GIDC;
+
+#endif /* _CACHE_H_ */
* @(#)cpio.h 8.1 (Berkeley) 5/31/93
*/
+#ifndef _CPIO_H_
+#define _CPIO_H_
+
/*
* Defines common to all versions of cpio
*/
#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_ */
* @(#)extern.h 8.2 (Berkeley) 4/18/94
*/
+#ifndef _PAX_EXTERN_H_
+#define _PAX_EXTERN_H_
+
/*
* External references from each source file
*/
int tty_read(char *, int);
void paxwarn(int, const char *, ...);
void syswarn(int, int, const char *, ...);
+
+#endif /* _PAX_EXTERN_H_ */
* @(#)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
int chflg; /* change directory flag */
struct ftree *fow; /* pointer to next entry on list */
} FTREE;
+
+#endif /* _FTREE_H_ */
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);
}
* @(#)options.h 8.2 (Berkeley) 4/18/94
*/
+#ifndef _OPTIONS_H_
+#define _OPTIONS_H_
+
/*
* argv[0] names. Used for tar and cpio emulation
*/
#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_ */
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 *);
{
char c;
char test;
+#ifdef _HAVE_REGCOMP_
+ char *equiv_pat;
+ char *pat_pend = NULL;
+#endif
*pend = NULL;
for (;;) {
/*
* 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++)
/* 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)
{
* @(#)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)
*/
} REPLACE;
int tty_rename(ARCHD *); /* used for -o invalid=rename recovery */
+
+#endif /* _PAT_REP_H_ */
#include <err.h>
#include <fcntl.h>
#include <paths.h>
+#include <locale.h>
#include "pax.h"
#include "extern.h"
static int gen_init(void);
{
char *tmpdir;
size_t tdlen;
+#ifdef _HAVE_REGCOMP_
+ setlocale(LC_CTYPE, "");
+ setlocale(LC_COLLATE, "");
+#endif
listf = stderr;
/*
* @(#)pax.h 8.2 (Berkeley) 4/18/94
*/
+#ifndef _PAX_H_
+#define _PAX_H_
+
/*
* BSD PAX global data structures and constants.
*/
#define HEX 16
#define OCT 8
#define _PAX_ 1
+#define _HAVE_REGCOMP_ 1
#define _TFILE_BASE "paxXXXXXXXXXX"
+
+#endif /* _PAX_H_ */
* @(#)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
*/
char devminor[8]; /* minor device number */
char prefix[TPFSZ]; /* linked to name */
} HD_USTAR;
+
+#endif /* _PAX_FORMAT_H_ */
* @(#)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)
*/
#define CMPBOTH (CMPMTME|CMPCTME) /* compare inode and mod time */
struct time_rng *fow; /* next pattern */
} TIME_RNG;
+
+#endif /* _SEL_SUBS_H_ */
* @(#)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
*/
u_int16_t mode; /* file mode to restore */
u_int16_t frc_mode; /* do we force mode settings? */
} DIRDATA;
+
+#endif /* _TABLES_H_ */
* @(#)tar.h 8.2 (Berkeley) 4/18/94
*/
+#ifndef _TAR_H_
+#define _TAR_H_
+
/*
* defines and data structures common to all tar formats
*/
char devminor[8]; /* minor device number */
char prefix[TPFSZ]; /* linked to name */
} HD_USTAR;
+
+#endif /* _TAR_H_ */
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)) {