1 #include <dispatch/dispatch.h>
2 #include <os/assumes.h>
13 #include <sys/xattr.h>
14 #include <sys/mount.h>
15 #include <apfs/apfs_fsctl.h>
17 #include "commoncrypto.h"
21 const int kSHA256NullTerminatedBuffLen
= 65;
22 static const char hex
[] = "0123456789abcdef";
24 /* Functions for SHA256_File_XATTRs */
25 #define SHA256_Data(d, s, b) Digest_Data(kCCDigestSHA256, d, s, b)
26 char *Digest_Data(CCDigestAlg algorithm
, void *data
, size_t size
, char *buf
);
27 void Quicksort(char **array
, int num
);
29 /* Generic version of libmd's *_File() functions. */
31 Digest_File(CCDigestAlg algorithm
, const char *filename
, char *buf
)
34 __block CCDigestCtx ctx
;
35 dispatch_queue_t queue
;
36 dispatch_semaphore_t sema
;
38 __block
int s_error
= 0;
39 uint8_t digest
[32]; // SHA256 is the biggest
42 /* dispatch_io_create_with_path requires an absolute path */
43 fd
= open(filename
, O_RDONLY
);
48 (void)fcntl(fd
, F_NOCACHE
, 1);
50 (void)os_assumes_zero(CCDigestInit(algorithm
, &ctx
));
52 queue
= dispatch_queue_create("com.apple.mtree.io", NULL
);
54 sema
= dispatch_semaphore_create(0);
57 io
= dispatch_io_create(DISPATCH_IO_STREAM
, fd
, queue
, ^(int error
) {
60 RECORD_FAILURE(27440, s_error
);
63 (void)dispatch_semaphore_signal(sema
);
66 dispatch_io_read(io
, 0, SIZE_MAX
, queue
, ^(__unused
bool done
, dispatch_data_t data
, int error
) {
68 (void)dispatch_data_apply(data
, ^(__unused dispatch_data_t region
, __unused
size_t offset
, const void *buffer
, size_t size
) {
69 (void)os_assumes_zero(CCDigestUpdate(&ctx
, buffer
, size
));
76 RECORD_FAILURE(27441, s_error
);
79 dispatch_release(io
); // it will close on its own
81 (void)dispatch_semaphore_wait(sema
, DISPATCH_TIME_FOREVER
);
83 dispatch_release(queue
);
84 dispatch_release(sema
);
91 /* Finalize and convert to hex. */
92 (void)os_assumes_zero(CCDigestFinal(&ctx
, digest
));
93 length
= CCDigestOutputSize(&ctx
);
94 os_assert(length
<= sizeof(digest
));
95 for (i
= 0; i
< length
; i
++) {
96 buf
[i
+i
] = hex
[digest
[i
] >> 4];
97 buf
[i
+i
+1] = hex
[digest
[i
] & 0x0f];
105 SHA256_Path_XATTRs(char *path
, char *buf
) {
106 xattr_info
*ai
= NULL
;
108 // mflag is passed during manifest comparision while xflag is used to generate the specification
109 if (mflag
|| xflag
) {
110 ai
= get_xdstream_privateid(path
, buf
);
112 ai
= calculate_SHA256_XATTRs(path
, buf
);
120 calculate_SHA256_XATTRs(char *path
, char *buf
)
123 char *xattrsSummary
= NULL
;
124 int options
= XATTR_SHOWCOMPRESSION
| XATTR_NOFOLLOW
;
125 ssize_t nameBufSize
= listxattr(path
, NULL
, 0, options
);
126 uint64_t xd_obj_id
= 0;
127 if (nameBufSize
> 0) {
128 char *nameBuf
= malloc(nameBufSize
);
130 listxattr(path
, nameBuf
, nameBufSize
, options
);
132 size_t xattrsLen
= 1;
133 size_t xattrIndex
= 0;
134 char **xattrs
= malloc(xattrsLen
* sizeof(char *));
135 char *nextName
= nameBuf
;
136 while (nextName
< nameBuf
+ nameBufSize
)
138 char *name
= nextName
;
139 if (xattrIndex
== xattrsLen
) {
141 xattrs
= realloc(xattrs
, xattrsLen
* sizeof(char *));
143 xattrs
[xattrIndex
++] = name
;
144 nextName
+= strlen(name
) + 1;
147 // sort the xattr array as they're not guaranteed to come in the same order
148 qsort_b(xattrs
, xattrIndex
, sizeof(char *), ^(const void *l
, const void *r
) {
149 char *left
= *(char **)l
;
150 char *right
= *(char **)r
;
151 return strcmp(left
, right
);
154 // gather the data for the xattrs
155 bool didAddXATTR
= false;
156 int xattrBufLen
= kSHA256NullTerminatedBuffLen
;
157 void *xattrBuf
= malloc(xattrBufLen
); // resized if necessary
160 char *oldSummary
= NULL
;
161 //XXX Make xattr_info an array of structs if necessary
162 xattr_info
*ai
= (xattr_info
*) malloc(sizeof(xattr_info
));
163 for (int i
= 0; i
< xattrIndex
; i
++) {
164 char *name
= xattrs
[i
];
165 ssize_t xlen
= getxattr(path
, name
, NULL
, 0, 0, options
);
166 if (xlen
> xattrBufLen
) {
168 xattrBuf
= realloc(xattrBuf
, xattrBufLen
);
170 bzero(xattrBuf
, xattrBufLen
);
171 result
= getxattr(path
, name
, xattrBuf
, xattrBufLen
, 0, options
);
174 RECORD_FAILURE(27442, error
);
175 errc(1, error
, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name
, path
);
178 digest
= SHA256_Data(xattrBuf
, xattrBufLen
, buf
);
180 err(1, "%s", xattrsSummary
);
184 asprintf(&xattrsSummary
, "%s:%s", name
, digest
);
186 oldSummary
= xattrsSummary
;
187 asprintf(&xattrsSummary
, "%s, %s:%s", oldSummary
, name
, digest
);
190 #ifdef APFSIOC_XDSTREAM_OBJ_ID
191 // System volume has stream based xattrs only in form of resource forks
192 if (!strncmp(name
, XATTR_RESOURCEFORK_NAME
, XATTR_MAXNAMELEN
)) {
193 struct xdstream_obj_id x_obj
;
194 x_obj
.xdi_name
= name
;
195 x_obj
.xdi_xdtream_obj_id
= 0;
197 result
= fsctl(path
, APFSIOC_XDSTREAM_OBJ_ID
, &x_obj
, 0);
199 xd_obj_id
= x_obj
.xdi_xdtream_obj_id
;
200 } else if (errno
== ENOTTY
) {
201 // Not an apfs filesystem, return zero.
205 RECORD_FAILURE(27444, error
);
206 errc(1, error
, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path
, error
);
210 ai
->xdstream_priv_id
= xd_obj_id
;
217 digest
= SHA256_Data(xattrsSummary
, strlen(xattrsSummary
) * sizeof(char), buf
);
219 err(1, "%s", xattrsSummary
);
230 get_xdstream_privateid(char *path
, char *buf
) {
232 int options
= XATTR_SHOWCOMPRESSION
| XATTR_NOFOLLOW
;
233 ssize_t nameBufSize
= listxattr(path
, NULL
, 0, options
);
234 uint64_t xd_obj_id
= 0;
236 if (nameBufSize
> 0) {
237 //XXX Make xattr_info an array of structs if necessary
238 xattr_info
*ai
= (xattr_info
*) malloc(sizeof(xattr_info
));
239 char *nameBuf
= malloc(nameBufSize
);
242 listxattr(path
, nameBuf
, nameBufSize
, options
);
244 size_t xattrsLen
= 1;
245 size_t xattrIndex
= 0;
246 char **xattrs
= malloc(xattrsLen
* sizeof(char *));
247 char *nextName
= nameBuf
;
248 while (nextName
< nameBuf
+ nameBufSize
)
250 char *name
= nextName
;
251 if (xattrIndex
== xattrsLen
) {
253 xattrs
= realloc(xattrs
, xattrsLen
* sizeof(char *));
255 xattrs
[xattrIndex
++] = name
;
256 nextName
+= strlen(name
) + 1;
259 for (int i
= 0; i
< xattrIndex
; i
++) {
260 char *name
= xattrs
[i
];
261 // System volume has stream based xattrs only in form of resource forks
262 if (!strncmp(name
, XATTR_RESOURCEFORK_NAME
, XATTR_MAXNAMELEN
)) {
263 struct xdstream_obj_id x_obj
;
264 x_obj
.xdi_name
= name
;
265 x_obj
.xdi_xdtream_obj_id
= 0;
267 result
= fsctl(path
, APFSIOC_XDSTREAM_OBJ_ID
, &x_obj
, 0);
268 if (!result
&& x_obj
.xdi_xdtream_obj_id
!= 0) {
269 xd_obj_id
= x_obj
.xdi_xdtream_obj_id
;
270 } else if (errno
== ENOTTY
) {
271 // Not an apfs filesystem, return zero.
275 RECORD_FAILURE(29983, error
);
276 errc(1, error
, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path
, error
);
281 ai
->xdstream_priv_id
= xd_obj_id
;
282 // insert a dummy value as digest is not used in presence of mflag
283 ai
->digest
= "authapfs";
293 char *SHA256_Path_ACL(char *path
, char *buf
)
300 struct attrlist list
= {
301 .bitmapcount
= ATTR_BIT_MAP_COUNT
,
302 .commonattr
= ATTR_CMN_RETURNED_ATTRS
| ATTR_CMN_EXTENDED_SECURITY
,
307 attribute_set_t returned_attrs
;
309 char buf
[8192]; // current acls are up to 3116 bytes, but they may increase in the future
310 } __attribute__((aligned(4), packed
));
312 struct ACLBuf aclBuf
;
314 result
= getattrlist(path
, &list
, &aclBuf
, sizeof(aclBuf
), FSOPT_NOFOLLOW
);
318 RECORD_FAILURE(27445, error
);
319 errc(1, error
, "SHA256_Path_ACL: getattrlist");
322 // if the path does not have an acl, return none
323 if ( ( ! ( aclBuf
.returned_attrs
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) )
324 || ( aclBuf
.acl
.attr_length
== 0 ) ) {
328 data
= ((char*)&aclBuf
.acl
) + aclBuf
.acl
.attr_dataoffset
;
330 digest
= SHA256_Data(data
, aclBuf
.acl
.attr_length
, buf
);
332 err(1, "SHA256_Path_ACL: SHA256_Data");
337 /* Functions for Digest_Path_* */
339 Digest_Data(CCDigestAlg algorithm
, void *data
, size_t size
, char *buf
) {
341 uint8_t digest
[32]; // SHA256 is the biggest
345 (void)os_assumes_zero(CCDigestInit(algorithm
, &ctx
));
346 (void)os_assumes_zero(CCDigestUpdate(&ctx
, data
, size
));
348 /* Finalize and convert to hex. */
349 (void)os_assumes_zero(CCDigestFinal(&ctx
, digest
));
350 length
= CCDigestOutputSize(&ctx
);
351 os_assert(length
<= sizeof(digest
));
352 for (i
= 0; i
< length
; i
++) {
353 buf
[i
+i
] = hex
[digest
[i
] >> 4];
354 buf
[i
+i
+1] = hex
[digest
[i
] & 0x0f];
362 get_sibling_id(const char *path
)
364 struct attrlist attr_list
= {0};
365 struct attrbuf attr_buf
= {0};
368 attr_list
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
369 attr_list
.forkattr
= ATTR_CMNEXT_LINKID
;
371 error
= getattrlist(path
, &attr_list
, &attr_buf
, sizeof(attr_buf
), FSOPT_ATTR_CMN_EXTENDED
| FSOPT_NOFOLLOW
);
374 RECORD_FAILURE(27447, error
);
375 errc(1, error
, "get_sibling_id: getattrlist failed for %s\n", path
);
378 return attr_buf
.sibling_id
;