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
;
109 ai
= get_xdstream_privateid(path
, buf
);
111 ai
= calculate_SHA256_XATTRs(path
, buf
);
119 calculate_SHA256_XATTRs(char *path
, char *buf
)
122 char *xattrsSummary
= NULL
;
123 int options
= XATTR_SHOWCOMPRESSION
| XATTR_NOFOLLOW
;
124 ssize_t nameBufSize
= listxattr(path
, NULL
, 0, options
);
125 uint64_t xd_obj_id
= 0;
126 if (nameBufSize
> 0) {
127 char *nameBuf
= malloc(nameBufSize
);
129 listxattr(path
, nameBuf
, nameBufSize
, options
);
131 size_t xattrsLen
= 1;
132 size_t xattrIndex
= 0;
133 char **xattrs
= malloc(xattrsLen
* sizeof(char *));
134 char *nextName
= nameBuf
;
135 while (nextName
< nameBuf
+ nameBufSize
)
137 char *name
= nextName
;
138 if (xattrIndex
== xattrsLen
) {
140 xattrs
= realloc(xattrs
, xattrsLen
* sizeof(char *));
142 xattrs
[xattrIndex
++] = name
;
143 nextName
+= strlen(name
) + 1;
146 // sort the xattr array as they're not guaranteed to come in the same order
147 qsort_b(xattrs
, xattrIndex
, sizeof(char *), ^(const void *l
, const void *r
) {
148 char *left
= *(char **)l
;
149 char *right
= *(char **)r
;
150 return strcmp(left
, right
);
153 // gather the data for the xattrs
154 bool didAddXATTR
= false;
155 int xattrBufLen
= kSHA256NullTerminatedBuffLen
;
156 void *xattrBuf
= malloc(xattrBufLen
); // resized if necessary
159 char *oldSummary
= NULL
;
160 //XXX Make xattr_info an array of structs if necessary
161 xattr_info
*ai
= (xattr_info
*) malloc(sizeof(xattr_info
));
162 for (int i
= 0; i
< xattrIndex
; i
++) {
163 char *name
= xattrs
[i
];
164 ssize_t xlen
= getxattr(path
, name
, NULL
, 0, 0, options
);
165 if (xlen
> xattrBufLen
) {
167 xattrBuf
= realloc(xattrBuf
, xattrBufLen
);
169 bzero(xattrBuf
, xattrBufLen
);
170 result
= getxattr(path
, name
, xattrBuf
, xattrBufLen
, 0, options
);
173 RECORD_FAILURE(27442, error
);
174 errc(1, error
, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name
, path
);
177 digest
= SHA256_Data(xattrBuf
, xattrBufLen
, buf
);
179 err(1, "%s", xattrsSummary
);
183 asprintf(&xattrsSummary
, "%s:%s", name
, digest
);
185 oldSummary
= xattrsSummary
;
186 asprintf(&xattrsSummary
, "%s, %s:%s", oldSummary
, name
, digest
);
189 #ifdef APFSIOC_XDSTREAM_OBJ_ID
190 // System volume has stream based xattrs only in form of resource forks
191 if (!strncmp(name
, XATTR_RESOURCEFORK_NAME
, XATTR_MAXNAMELEN
)) {
192 struct xdstream_obj_id x_obj
;
193 x_obj
.xdi_name
= name
;
194 x_obj
.xdi_xdtream_obj_id
= 0;
196 result
= fsctl(path
, APFSIOC_XDSTREAM_OBJ_ID
, &x_obj
, 0);
198 xd_obj_id
= x_obj
.xdi_xdtream_obj_id
;
199 } else if (errno
== ENOTTY
) {
200 // Not an apfs filesystem, return zero.
204 RECORD_FAILURE(27444, error
);
205 errc(1, error
, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path
, error
);
209 ai
->xdstream_priv_id
= xd_obj_id
;
216 digest
= SHA256_Data(xattrsSummary
, strlen(xattrsSummary
) * sizeof(char), buf
);
218 err(1, "%s", xattrsSummary
);
229 get_xdstream_privateid(char *path
, char *buf
) {
231 int options
= XATTR_SHOWCOMPRESSION
| XATTR_NOFOLLOW
;
232 ssize_t nameBufSize
= listxattr(path
, NULL
, 0, options
);
233 uint64_t xd_obj_id
= 0;
235 if (nameBufSize
> 0) {
236 //XXX Make xattr_info an array of structs if necessary
237 xattr_info
*ai
= (xattr_info
*) malloc(sizeof(xattr_info
));
238 char *nameBuf
= malloc(nameBufSize
);
241 listxattr(path
, nameBuf
, nameBufSize
, options
);
243 size_t xattrsLen
= 1;
244 size_t xattrIndex
= 0;
245 char **xattrs
= malloc(xattrsLen
* sizeof(char *));
246 char *nextName
= nameBuf
;
247 while (nextName
< nameBuf
+ nameBufSize
)
249 char *name
= nextName
;
250 if (xattrIndex
== xattrsLen
) {
252 xattrs
= realloc(xattrs
, xattrsLen
* sizeof(char *));
254 xattrs
[xattrIndex
++] = name
;
255 nextName
+= strlen(name
) + 1;
258 for (int i
= 0; i
< xattrIndex
; i
++) {
259 char *name
= xattrs
[i
];
260 // System volume has stream based xattrs only in form of resource forks
261 if (!strncmp(name
, XATTR_RESOURCEFORK_NAME
, XATTR_MAXNAMELEN
)) {
262 struct xdstream_obj_id x_obj
;
263 x_obj
.xdi_name
= name
;
264 x_obj
.xdi_xdtream_obj_id
= 0;
266 result
= fsctl(path
, APFSIOC_XDSTREAM_OBJ_ID
, &x_obj
, 0);
267 if (!result
&& x_obj
.xdi_xdtream_obj_id
!= 0) {
268 xd_obj_id
= x_obj
.xdi_xdtream_obj_id
;
269 } else if (errno
== ENOTTY
) {
270 // Not an apfs filesystem, return zero.
274 RECORD_FAILURE(29983, error
);
275 errc(1, error
, "%s - SHA256_Path_XATTRs APFSIOC_XDSTREAM_OBJ_ID failed with %d", path
, error
);
280 ai
->xdstream_priv_id
= xd_obj_id
;
281 // insert a dummy value as digest is not used in presence of mflag
282 ai
->digest
= "authapfs";
292 char *SHA256_Path_ACL(char *path
, char *buf
)
299 struct attrlist list
= {
300 .bitmapcount
= ATTR_BIT_MAP_COUNT
,
301 .commonattr
= ATTR_CMN_RETURNED_ATTRS
| ATTR_CMN_EXTENDED_SECURITY
,
306 attribute_set_t returned_attrs
;
308 char buf
[8192]; // current acls are up to 3116 bytes, but they may increase in the future
309 } __attribute__((aligned(4), packed
));
311 struct ACLBuf aclBuf
;
313 result
= getattrlist(path
, &list
, &aclBuf
, sizeof(aclBuf
), FSOPT_NOFOLLOW
);
317 RECORD_FAILURE(27445, error
);
318 errc(1, error
, "SHA256_Path_ACL: getattrlist");
321 // if the path does not have an acl, return none
322 if ( ( ! ( aclBuf
.returned_attrs
.commonattr
& ATTR_CMN_EXTENDED_SECURITY
) )
323 || ( aclBuf
.acl
.attr_length
== 0 ) ) {
327 data
= ((char*)&aclBuf
.acl
) + aclBuf
.acl
.attr_dataoffset
;
329 digest
= SHA256_Data(data
, aclBuf
.acl
.attr_length
, buf
);
331 err(1, "SHA256_Path_ACL: SHA256_Data");
336 /* Functions for Digest_Path_* */
338 Digest_Data(CCDigestAlg algorithm
, void *data
, size_t size
, char *buf
) {
340 uint8_t digest
[32]; // SHA256 is the biggest
344 (void)os_assumes_zero(CCDigestInit(algorithm
, &ctx
));
345 (void)os_assumes_zero(CCDigestUpdate(&ctx
, data
, size
));
347 /* Finalize and convert to hex. */
348 (void)os_assumes_zero(CCDigestFinal(&ctx
, digest
));
349 length
= CCDigestOutputSize(&ctx
);
350 os_assert(length
<= sizeof(digest
));
351 for (i
= 0; i
< length
; i
++) {
352 buf
[i
+i
] = hex
[digest
[i
] >> 4];
353 buf
[i
+i
+1] = hex
[digest
[i
] & 0x0f];
361 get_sibling_id(const char *path
)
363 struct attrlist attr_list
= {0};
364 struct attrbuf attr_buf
= {0};
367 attr_list
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
368 attr_list
.forkattr
= ATTR_CMNEXT_LINKID
;
370 error
= getattrlist(path
, &attr_list
, &attr_buf
, sizeof(attr_buf
), FSOPT_ATTR_CMN_EXTENDED
| FSOPT_NOFOLLOW
);
373 RECORD_FAILURE(27447, error
);
374 errc(1, error
, "get_sibling_id: getattrlist failed for %s\n", path
);
377 return attr_buf
.sibling_id
;