]> git.saurik.com Git - apple/hfs.git/blob - core/hfs_xattr.c
hfs-556.41.1.tar.gz
[apple/hfs.git] / core / hfs_xattr.c
1 /*
2 * Copyright (c) 2004-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/ubc.h>
34 #include <sys/utfconv.h>
35 #include <sys/vnode.h>
36 #include <sys/xattr.h>
37 #include <sys/fcntl.h>
38 #include <sys/fsctl.h>
39 #include <sys/kauth.h>
40
41 #include "hfs.h"
42 #include "hfs_cnode.h"
43 #include "hfs_mount.h"
44 #include "hfs_format.h"
45 #include "hfs_endian.h"
46 #include "hfs_btreeio.h"
47 #include "hfs_fsctl.h"
48 #include "hfs_cprotect.h"
49
50 #include "BTreesInternal.h"
51
52 #define HFS_XATTR_VERBOSE 0
53
54 #define ATTRIBUTE_FILE_NODE_SIZE 8192
55
56
57 /* State information for the listattr_callback callback function. */
58 struct listattr_callback_state {
59 u_int32_t fileID;
60 int result;
61 uio_t uio;
62 size_t size;
63 #if HFS_COMPRESSION
64 int showcompressed;
65 vfs_context_t ctx;
66 vnode_t vp;
67 #endif /* HFS_COMPRESSION */
68 };
69
70
71 /* HFS Internal Names */
72 #define XATTR_XATTREXTENTS_NAME "system.xattrextents"
73
74 static u_int32_t emptyfinfo[8] = {0};
75
76 static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo);
77
78 const char hfs_attrdatafilename[] = "Attribute Data";
79
80 static int listattr_callback(const HFSPlusAttrKey *key, const HFSPlusAttrData *data,
81 struct listattr_callback_state *state);
82
83 static int remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator);
84
85 static int getnodecount(struct hfsmount *hfsmp, size_t nodesize);
86
87 static size_t getmaxinlineattrsize(struct vnode * attrvp);
88
89 static int read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents);
90
91 static int write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents);
92
93 static int alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks);
94
95 static void free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents);
96
97 static int has_overflow_extents(HFSPlusForkData *forkdata);
98
99 static int count_extent_blocks(int maxblks, HFSPlusExtentRecord extents);
100
101 #if NAMEDSTREAMS
102 /*
103 * Obtain the vnode for a stream.
104 */
105 int
106 hfs_vnop_getnamedstream(struct vnop_getnamedstream_args* ap)
107 {
108 vnode_t vp = ap->a_vp;
109 vnode_t *svpp = ap->a_svpp;
110 struct cnode *cp;
111 int error = 0;
112
113 *svpp = NULL;
114
115 /*
116 * We only support the "com.apple.ResourceFork" stream.
117 */
118 if (strcmp(ap->a_name, XATTR_RESOURCEFORK_NAME) != 0) {
119 return (ENOATTR);
120 }
121 cp = VTOC(vp);
122 if ( !S_ISREG(cp->c_mode) ) {
123 return (EPERM);
124 }
125 #if HFS_COMPRESSION
126 int hide_rsrc = hfs_hides_rsrc(ap->a_context, VTOC(vp), 1);
127 #endif /* HFS_COMPRESSION */
128 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
129 return (error);
130 }
131 if ((!hfs_has_rsrc(cp)
132 #if HFS_COMPRESSION
133 || hide_rsrc
134 #endif /* HFS_COMPRESSION */
135 ) && (ap->a_operation != NS_OPEN)) {
136 hfs_unlock(cp);
137 return (ENOATTR);
138 }
139 error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp);
140 hfs_unlock(cp);
141
142 return (error);
143 }
144
145 /*
146 * Create a stream.
147 */
148 int
149 hfs_vnop_makenamedstream(struct vnop_makenamedstream_args* ap)
150 {
151 vnode_t vp = ap->a_vp;
152 vnode_t *svpp = ap->a_svpp;
153 struct cnode *cp;
154 int error = 0;
155
156 *svpp = NULL;
157
158 /*
159 * We only support the "com.apple.ResourceFork" stream.
160 */
161 if (strcmp(ap->a_name, XATTR_RESOURCEFORK_NAME) != 0) {
162 return (ENOATTR);
163 }
164 cp = VTOC(vp);
165 if ( !S_ISREG(cp->c_mode) ) {
166 return (EPERM);
167 }
168 #if HFS_COMPRESSION
169 if (hfs_hides_rsrc(ap->a_context, VTOC(vp), 1)) {
170 if (VNODE_IS_RSRC(vp)) {
171 return EINVAL;
172 } else {
173 error = decmpfs_decompress_file(vp, VTOCMP(vp), -1, 1, 0);
174 if (error != 0)
175 return error;
176 }
177 }
178 #endif /* HFS_COMPRESSION */
179 if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
180 return (error);
181 }
182 error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp);
183 hfs_unlock(cp);
184
185 return (error);
186 }
187
188 /*
189 * Remove a stream.
190 */
191 int
192 hfs_vnop_removenamedstream(struct vnop_removenamedstream_args* ap)
193 {
194 vnode_t svp = ap->a_svp;
195 cnode_t *scp = VTOC(svp);
196 int error = 0;
197
198 /*
199 * We only support the "com.apple.ResourceFork" stream.
200 */
201 if (strcmp(ap->a_name, XATTR_RESOURCEFORK_NAME) != 0) {
202 return (ENOATTR);
203 }
204 #if HFS_COMPRESSION
205 if (hfs_hides_rsrc(ap->a_context, scp, 1)) {
206 /* do nothing */
207 return 0;
208 }
209 #endif /* HFS_COMPRESSION */
210
211 hfs_lock_truncate(scp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
212 if (VTOF(svp)->ff_size) {
213 // hfs_truncate will deal with the cnode lock
214 error = hfs_truncate(svp, 0, IO_NDELAY, 0, ap->a_context);
215 }
216 hfs_unlock_truncate(scp, HFS_LOCK_DEFAULT);
217
218 return error;
219 }
220 #endif
221
222
223 /* Zero out the date added field for the specified cnode */
224 static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo)
225 {
226 u_int8_t *finfo = finderinfo;
227
228 /* Advance finfo by 16 bytes to the 2nd half of the finderinfo */
229 finfo = finfo + 16;
230
231 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
232 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
233 extinfo->document_id = 0;
234 extinfo->date_added = 0;
235 extinfo->write_gen_counter = 0;
236 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
237 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
238 extinfo->document_id = 0;
239 extinfo->date_added = 0;
240 extinfo->write_gen_counter = 0;
241 } else {
242 /* Return an error */
243 return -1;
244 }
245 return 0;
246
247 }
248
249 /*
250 * Retrieve the data of an extended attribute.
251 */
252 int
253 hfs_vnop_getxattr(struct vnop_getxattr_args *ap)
254 /*
255 struct vnop_getxattr_args {
256 struct vnodeop_desc *a_desc;
257 vnode_t a_vp;
258 char * a_name;
259 uio_t a_uio;
260 size_t *a_size;
261 int a_options;
262 vfs_context_t a_context;
263 };
264 */
265 {
266 struct vnode *vp = ap->a_vp;
267 struct cnode *cp;
268 struct hfsmount *hfsmp;
269 uio_t uio = ap->a_uio;
270 size_t bufsize;
271 int result;
272
273 cp = VTOC(vp);
274 if (vp == cp->c_vp) {
275 #if HFS_COMPRESSION
276 int decmpfs_hide = hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1); /* 1 == don't take the cnode lock */
277 if (decmpfs_hide && !(ap->a_options & XATTR_SHOWCOMPRESSION))
278 return ENOATTR;
279 #endif /* HFS_COMPRESSION */
280
281 /* Get the Finder Info. */
282 if (strcmp(ap->a_name, XATTR_FINDERINFO_NAME) == 0) {
283 u_int8_t finderinfo[32];
284 bufsize = 32;
285
286 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
287 return (result);
288 }
289 /* Make a copy since we may not export all of it. */
290 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
291 hfs_unlock(cp);
292
293 /* Zero out the date added field in the local copy */
294 hfs_zero_hidden_fields (cp, finderinfo);
295
296 /* Don't expose a symlink's private type/creator. */
297 if (vnode_islnk(vp)) {
298 struct FndrFileInfo *fip;
299
300 fip = (struct FndrFileInfo *)&finderinfo;
301 fip->fdType = 0;
302 fip->fdCreator = 0;
303 }
304 /* If Finder Info is empty then it doesn't exist. */
305 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
306 return (ENOATTR);
307 }
308 if (uio == NULL) {
309 *ap->a_size = bufsize;
310 return (0);
311 }
312 if ((user_size_t)uio_resid(uio) < bufsize)
313 return (ERANGE);
314
315 result = uiomove((caddr_t)&finderinfo , bufsize, uio);
316
317 return (result);
318 }
319 /* Read the Resource Fork. */
320 if (strcmp(ap->a_name, XATTR_RESOURCEFORK_NAME) == 0) {
321 struct vnode *rvp = NULL;
322 int openunlinked = 0;
323 int namelen = 0;
324
325 if ( !S_ISREG(cp->c_mode) ) {
326 return (EPERM);
327 }
328 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
329 return (result);
330 }
331 namelen = cp->c_desc.cd_namelen;
332
333 if (!hfs_has_rsrc(cp)) {
334 hfs_unlock(cp);
335 return (ENOATTR);
336 }
337 hfsmp = VTOHFS(vp);
338 if ((cp->c_flag & C_DELETED) && (namelen == 0)) {
339 openunlinked = 1;
340 }
341
342 result = hfs_vgetrsrc(hfsmp, vp, &rvp);
343 hfs_unlock(cp);
344 if (result) {
345 return (result);
346 }
347 if (uio == NULL) {
348 *ap->a_size = (size_t)VTOF(rvp)->ff_size;
349 } else {
350 #if HFS_COMPRESSION
351 user_ssize_t uio_size = 0;
352 if (decmpfs_hide)
353 uio_size = uio_resid(uio);
354 #endif /* HFS_COMPRESSION */
355 result = VNOP_READ(rvp, uio, 0, ap->a_context);
356 #if HFS_COMPRESSION
357 if (decmpfs_hide &&
358 (result == 0) &&
359 (uio_resid(uio) == uio_size)) {
360 /*
361 * We intentionally make the above call to VNOP_READ so that
362 * it can return an authorization/permission/etc. Error
363 * based on ap->a_context and thus deny this operation;
364 * in that case, result != 0 and we won't proceed.
365 *
366 * However, if result == 0, it will have returned no data
367 * because hfs_vnop_read hid the resource fork
368 * (hence uio_resid(uio) == uio_size, i.e. the uio is untouched)
369 *
370 * In that case, we try again with the decmpfs_ctx context
371 * to get the actual data
372 */
373 result = VNOP_READ(rvp, uio, 0, decmpfs_ctx);
374 }
375 #endif /* HFS_COMPRESSION */
376 }
377 /* force the rsrc fork vnode to recycle right away */
378 if (openunlinked) {
379 int vref;
380 vref = vnode_ref (rvp);
381 if (vref == 0) {
382 vnode_rele (rvp);
383 }
384 vnode_recycle(rvp);
385 }
386 vnode_put(rvp);
387 return (result);
388 }
389 }
390 hfsmp = VTOHFS(vp);
391 #if CONFIG_HFS_STD
392 /*
393 * Standard HFS only supports native FinderInfo and Resource Forks.
394 */
395 if (hfsmp->hfs_flags & HFS_STANDARD) {
396 return (EPERM);
397 }
398 #endif
399
400 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
401 return (result);
402 }
403
404 /* Check for non-rsrc, non-finderinfo EAs */
405 result = hfs_getxattr_internal (cp, ap, VTOHFS(cp->c_vp), 0);
406
407 hfs_unlock(cp);
408
409 return MacToVFSError(result);
410 }
411
412 // Has same limitations as hfs_getxattr_internal below
413 int hfs_xattr_read(vnode_t vp, const char *name, void *data, size_t *size)
414 {
415 uio_t uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
416
417 uio_addiov(uio, CAST_USER_ADDR_T(data), *size);
418
419 struct vnop_getxattr_args args = {
420 .a_uio = uio,
421 .a_name = name,
422 .a_size = size
423 };
424
425 int ret = hfs_getxattr_internal(VTOC(vp), &args, VTOHFS(vp), 0);
426
427 uio_free(uio);
428
429 return ret;
430 }
431
432 /*
433 * getxattr_internal
434 *
435 * We break out this internal function which searches the attributes B-Tree and the
436 * overflow extents file to find non-resource, non-finderinfo EAs. There may be cases
437 * where we need to get EAs in contexts where we are already holding the cnode lock,
438 * and to re-enter hfs_vnop_getxattr would cause us to double-lock the cnode. Instead,
439 * we can just directly call this function.
440 *
441 * We pass the hfsmp argument directly here because we may not necessarily have a cnode to
442 * operate on. Under normal conditions, we have a file or directory to query, but if we
443 * are operating on the root directory (id 1), then we may not have a cnode. In this case, if hte
444 * 'cp' argument is NULL, then we need to use the 'fileid' argument as the entry to manipulate
445 *
446 * NOTE: This function assumes the cnode lock for 'cp' is held exclusive or shared.
447 */
448 int hfs_getxattr_internal (struct cnode *cp, struct vnop_getxattr_args *ap,
449 struct hfsmount *hfsmp, u_int32_t fileid)
450 {
451
452 struct filefork *btfile;
453 struct BTreeIterator * iterator = NULL;
454 size_t bufsize = 0;
455 HFSPlusAttrRecord *recp = NULL;
456 size_t recp_size = 0;
457 FSBufferDescriptor btdata;
458 int lockflags = 0;
459 int result = 0;
460 u_int16_t datasize = 0;
461 uio_t uio = ap->a_uio;
462 u_int32_t target_id = 0;
463
464 if (cp) {
465 target_id = cp->c_fileid;
466 } else {
467 target_id = fileid;
468 }
469
470
471 /* Bail if we don't have an EA B-Tree. */
472 if ((hfsmp->hfs_attribute_vp == NULL) ||
473 ((cp) && (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0)) {
474 result = ENOATTR;
475 goto exit;
476 }
477
478 /* Initialize the B-Tree iterator for searching for the proper EA */
479 btfile = VTOF(hfsmp->hfs_attribute_vp);
480
481 iterator = hfs_mallocz(sizeof(*iterator));
482
483 /* Allocate memory for reading in the attribute record. This buffer is
484 * big enough to read in all types of attribute records. It is not big
485 * enough to read inline attribute data which is read in later.
486 */
487 recp = hfs_malloc(recp_size = sizeof(HFSPlusAttrRecord));
488 btdata.bufferAddress = recp;
489 btdata.itemSize = sizeof(HFSPlusAttrRecord);
490 btdata.itemCount = 1;
491
492 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
493 if (result) {
494 goto exit;
495 }
496
497 /* Lookup the attribute in the Attribute B-Tree */
498 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
499 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
500 hfs_systemfile_unlock(hfsmp, lockflags);
501
502 if (result) {
503 if (result == btNotFound) {
504 result = ENOATTR;
505 }
506 goto exit;
507 }
508
509 /*
510 * Operate differently if we have inline EAs that can fit in the attribute B-Tree or if
511 * we have extent based EAs.
512 */
513 switch (recp->recordType) {
514
515 /* Attribute fits in the Attribute B-Tree */
516 case kHFSPlusAttrInlineData: {
517 /*
518 * Sanity check record size. It's not required to have any
519 * user data, so the minimum size is 2 bytes less that the
520 * size of HFSPlusAttrData (since HFSPlusAttrData struct
521 * has 2 bytes set aside for attribute data).
522 */
523 if (datasize < (sizeof(HFSPlusAttrData) - 2)) {
524 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
525 hfsmp->vcbVN, target_id, ap->a_name, datasize, sizeof(HFSPlusAttrData));
526 result = ENOATTR;
527 break;
528 }
529 *ap->a_size = recp->attrData.attrSize;
530 if (uio && recp->attrData.attrSize != 0) {
531 if (*ap->a_size > (user_size_t)uio_resid(uio)) {
532 /* User provided buffer is not large enough for the xattr data */
533 result = ERANGE;
534 } else {
535 /* Previous BTreeSearchRecord() read in only the attribute record,
536 * and not the attribute data. Now allocate enough memory for
537 * both attribute record and data, and read the attribute record again.
538 */
539 bufsize = sizeof(HFSPlusAttrData) - 2 + recp->attrData.attrSize;
540 hfs_free(recp, recp_size);
541 recp = hfs_malloc(recp_size = bufsize);
542
543 btdata.bufferAddress = recp;
544 btdata.itemSize = bufsize;
545 btdata.itemCount = 1;
546
547 bzero(iterator, sizeof(*iterator));
548 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
549 if (result) {
550 goto exit;
551 }
552
553 /* Lookup the attribute record and inline data */
554 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
555 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
556 hfs_systemfile_unlock(hfsmp, lockflags);
557 if (result) {
558 if (result == btNotFound) {
559 result = ENOATTR;
560 }
561 goto exit;
562 }
563
564 /* Copy-out the attribute data to the user buffer */
565 *ap->a_size = recp->attrData.attrSize;
566 result = uiomove((caddr_t) &recp->attrData.attrData , recp->attrData.attrSize, uio);
567 }
568 }
569 break;
570 }
571
572 /* Extent-Based EAs */
573 case kHFSPlusAttrForkData: {
574 if (datasize < sizeof(HFSPlusAttrForkData)) {
575 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
576 hfsmp->vcbVN, target_id, ap->a_name, datasize, sizeof(HFSPlusAttrForkData));
577 result = ENOATTR;
578 break;
579 }
580 *ap->a_size = recp->forkData.theFork.logicalSize;
581 if (uio == NULL) {
582 break;
583 }
584 if (*ap->a_size > (user_size_t)uio_resid(uio)) {
585 result = ERANGE;
586 break;
587 }
588 /* Process overflow extents if necessary. */
589 if (has_overflow_extents(&recp->forkData.theFork)) {
590 HFSPlusExtentDescriptor *extentbuf;
591 HFSPlusExtentDescriptor *extentptr;
592 size_t extentbufsize;
593 u_int32_t totalblocks;
594 u_int32_t blkcnt;
595 u_int32_t attrlen;
596
597 totalblocks = recp->forkData.theFork.totalBlocks;
598 /* Ignore bogus block counts. */
599 if (totalblocks > howmany(HFS_XATTR_MAXSIZE, hfsmp->blockSize)) {
600 result = ERANGE;
601 break;
602 }
603 attrlen = recp->forkData.theFork.logicalSize;
604
605 /* Get a buffer to hold the worst case amount of extents. */
606 extentbufsize = totalblocks * sizeof(HFSPlusExtentDescriptor);
607 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
608 extentbuf = hfs_mallocz(extentbufsize);
609 extentptr = extentbuf;
610
611 /* Grab the first 8 extents. */
612 bcopy(&recp->forkData.theFork.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
613 extentptr += kHFSPlusExtentDensity;
614 blkcnt = count_extent_blocks(totalblocks, recp->forkData.theFork.extents);
615
616 /* Now lookup the overflow extents. */
617 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
618 while (blkcnt < totalblocks) {
619 ((HFSPlusAttrKey *)&iterator->key)->startBlock = blkcnt;
620 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
621 if (result ||
622 (recp->recordType != kHFSPlusAttrExtents) ||
623 (datasize < sizeof(HFSPlusAttrExtents))) {
624 printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
625 ap->a_name, blkcnt, totalblocks);
626 result = ENOATTR;
627 break; /* break from while */
628 }
629 /* Grab the next 8 extents. */
630 bcopy(&recp->overflowExtents.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
631 extentptr += kHFSPlusExtentDensity;
632 blkcnt += count_extent_blocks(totalblocks, recp->overflowExtents.extents);
633 }
634
635 /* Release Attr B-Tree lock */
636 hfs_systemfile_unlock(hfsmp, lockflags);
637
638 if (blkcnt < totalblocks) {
639 result = ENOATTR;
640 } else {
641 result = read_attr_data(hfsmp, uio, attrlen, extentbuf);
642 }
643 hfs_free(extentbuf, extentbufsize);
644
645 } else { /* No overflow extents. */
646 result = read_attr_data(hfsmp, uio, recp->forkData.theFork.logicalSize, recp->forkData.theFork.extents);
647 }
648 break;
649 }
650
651 default:
652 /* We only support Extent or inline EAs. Default to ENOATTR for anything else */
653 result = ENOATTR;
654 break;
655 }
656
657 exit:
658 hfs_free(iterator, sizeof(*iterator));
659 hfs_free(recp, recp_size);
660
661 return result;
662
663 }
664
665
666 /*
667 * Set the data of an extended attribute.
668 */
669 int
670 hfs_vnop_setxattr(struct vnop_setxattr_args *ap)
671 /*
672 struct vnop_setxattr_args {
673 struct vnodeop_desc *a_desc;
674 vnode_t a_vp;
675 char * a_name;
676 uio_t a_uio;
677 int a_options;
678 vfs_context_t a_context;
679 };
680 */
681 {
682 struct vnode *vp = ap->a_vp;
683 struct cnode *cp = NULL;
684 struct hfsmount *hfsmp;
685 uio_t uio = ap->a_uio;
686 size_t attrsize;
687 void * user_data_ptr = NULL;
688 int result;
689 time_t orig_ctime=VTOC(vp)->c_ctime;
690
691 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
692 return (EINVAL); /* invalid name */
693 }
694 hfsmp = VTOHFS(vp);
695 if (VNODE_IS_RSRC(vp)) {
696 return (EPERM);
697 }
698
699 #if HFS_COMPRESSION
700 if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) ) { /* 1 == don't take the cnode lock */
701 result = decmpfs_decompress_file(vp, VTOCMP(vp), -1, 1, 0);
702 if (result != 0)
703 return result;
704 }
705 #endif /* HFS_COMPRESSION */
706
707 nspace_snapshot_event(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_WRITE_OP, NSPACE_REARM_NO_ARG);
708
709 /* Set the Finder Info. */
710 if (strcmp(ap->a_name, XATTR_FINDERINFO_NAME) == 0) {
711 union {
712 uint8_t data[32];
713 char cdata[32];
714 struct FndrFileInfo info;
715 } fi;
716 void * finderinfo_start;
717 u_int8_t *finfo = NULL;
718 u_int16_t fdFlags;
719 u_int32_t dateadded = 0;
720 u_int32_t write_gen_counter = 0;
721 u_int32_t document_id = 0;
722
723 attrsize = sizeof(VTOC(vp)->c_finderinfo);
724
725 if ((user_size_t)uio_resid(uio) != attrsize) {
726 return (ERANGE);
727 }
728 /* Grab the new Finder Info data. */
729 if ((result = uiomove(fi.cdata, attrsize, uio))) {
730 return (result);
731 }
732
733 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
734 return (result);
735 }
736 cp = VTOC(vp);
737
738 /* Symlink's don't have an external type/creator. */
739 if (vnode_islnk(vp)) {
740 /* Skip over type/creator fields. */
741 finderinfo_start = &cp->c_finderinfo[8];
742 attrsize -= 8;
743 } else {
744 finderinfo_start = &cp->c_finderinfo[0];
745 /*
746 * Don't allow the external setting of
747 * file type to kHardLinkFileType.
748 */
749 if (fi.info.fdType == SWAP_BE32(kHardLinkFileType)) {
750 hfs_unlock(cp);
751 return (EPERM);
752 }
753 }
754
755 /* Grab the current date added from the cnode */
756 dateadded = hfs_get_dateadded (cp);
757 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
758 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)((u_int8_t*)cp->c_finderinfo + 16);
759 /*
760 * Grab generation counter directly from the cnode
761 * instead of calling hfs_get_gencount(), because
762 * for zero generation count values hfs_get_gencount()
763 * lies and bumps it up to one.
764 */
765 write_gen_counter = extinfo->write_gen_counter;
766 document_id = extinfo->document_id;
767 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
768 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)cp->c_finderinfo + 16);
769 write_gen_counter = extinfo->write_gen_counter;
770 document_id = extinfo->document_id;
771 }
772
773 /*
774 * Zero out the finder info's reserved fields like date added,
775 * generation counter, and document id to ignore user's attempts
776 * to set it
777 */
778 hfs_zero_hidden_fields(cp, fi.data);
779
780 if (bcmp(finderinfo_start, emptyfinfo, attrsize)) {
781 /* attr exists and "create" was specified. */
782 if (ap->a_options & XATTR_CREATE) {
783 hfs_unlock(cp);
784 return (EEXIST);
785 }
786 } else { /* empty */
787 /* attr doesn't exists and "replace" was specified. */
788 if (ap->a_options & XATTR_REPLACE) {
789 hfs_unlock(cp);
790 return (ENOATTR);
791 }
792 }
793
794 /*
795 * Now restore the date added and other reserved fields to the finderinfo to
796 * be written out. Advance to the 2nd half of the finderinfo to write them
797 * out into the buffer.
798 *
799 * Make sure to endian swap the date added back into big endian. When we used
800 * hfs_get_dateadded above to retrieve it, it swapped into local endianness
801 * for us. But now that we're writing it out, put it back into big endian.
802 */
803 finfo = &fi.data[16];
804 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
805 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
806 extinfo->date_added = OSSwapHostToBigInt32(dateadded);
807 extinfo->write_gen_counter = write_gen_counter;
808 extinfo->document_id = document_id;
809 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
810 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
811 extinfo->date_added = OSSwapHostToBigInt32(dateadded);
812 extinfo->write_gen_counter = write_gen_counter;
813 extinfo->document_id = document_id;
814 }
815
816 /* Set the cnode's Finder Info. */
817 if (attrsize == sizeof(cp->c_finderinfo)) {
818 bcopy(&fi.data[0], finderinfo_start, attrsize);
819 } else {
820 bcopy(&fi.data[8], finderinfo_start, attrsize);
821 }
822
823 /* Updating finderInfo updates change time and modified time */
824 cp->c_touch_chgtime = TRUE;
825 cp->c_flag |= C_MODIFIED;
826
827 /*
828 * Mirror the invisible bit to the UF_HIDDEN flag.
829 *
830 * The fdFlags for files and frFlags for folders are both 8 bytes
831 * into the userInfo (the first 16 bytes of the Finder Info). They
832 * are both 16-bit fields.
833 */
834 fdFlags = *((u_int16_t *) &cp->c_finderinfo[8]);
835 if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) {
836 cp->c_bsdflags |= UF_HIDDEN;
837 } else {
838 cp->c_bsdflags &= ~UF_HIDDEN;
839 }
840
841 result = hfs_update(vp, 0);
842
843 hfs_unlock(cp);
844 return (result);
845 }
846 /* Write the Resource Fork. */
847 if (strcmp(ap->a_name, XATTR_RESOURCEFORK_NAME) == 0) {
848 struct vnode *rvp = NULL;
849 int namelen = 0;
850 int openunlinked = 0;
851
852 if (!vnode_isreg(vp)) {
853 return (EPERM);
854 }
855 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
856 return (result);
857 }
858 cp = VTOC(vp);
859 namelen = cp->c_desc.cd_namelen;
860
861 if (hfs_has_rsrc(cp)) {
862 /* attr exists and "create" was specified. */
863 if (ap->a_options & XATTR_CREATE) {
864 hfs_unlock(cp);
865 return (EEXIST);
866 }
867 } else {
868 /* attr doesn't exists and "replace" was specified. */
869 if (ap->a_options & XATTR_REPLACE) {
870 hfs_unlock(cp);
871 return (ENOATTR);
872 }
873 }
874
875 /*
876 * Note that we could be called on to grab the rsrc fork vnode
877 * for a file that has become open-unlinked.
878 */
879 if ((cp->c_flag & C_DELETED) && (namelen == 0)) {
880 openunlinked = 1;
881 }
882
883 result = hfs_vgetrsrc(hfsmp, vp, &rvp);
884 hfs_unlock(cp);
885 if (result) {
886 return (result);
887 }
888 /* VNOP_WRITE marks cnode as needing a modtime update */
889 result = VNOP_WRITE(rvp, uio, 0, ap->a_context);
890
891 /* if open unlinked, force it inactive */
892 if (openunlinked) {
893 int vref;
894 vref = vnode_ref (rvp);
895 if (vref == 0) {
896 vnode_rele(rvp);
897 }
898 vnode_recycle (rvp);
899 } else {
900 /* cnode is not open-unlinked, so re-lock cnode to sync */
901 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
902 vnode_recycle (rvp);
903 vnode_put(rvp);
904 return result;
905 }
906
907 /* hfs fsync rsrc fork to force to disk and update modtime */
908 result = hfs_fsync (rvp, MNT_NOWAIT, 0, vfs_context_proc (ap->a_context));
909 hfs_unlock (cp);
910 }
911
912 vnode_put(rvp);
913 return (result);
914 }
915 #if CONFIG_HFS_STD
916 /*
917 * Standard HFS only supports native FinderInfo and Resource Forks.
918 */
919 if (hfsmp->hfs_flags & HFS_STANDARD) {
920 return (EPERM);
921 }
922 #endif
923 attrsize = uio_resid(uio);
924
925 /* Enforce an upper limit. */
926 if (attrsize > HFS_XATTR_MAXSIZE) {
927 result = E2BIG;
928 goto exit;
929 }
930
931 /*
932 * Attempt to copy the users attr data before taking any locks,
933 * only if it will be an inline attribute. For larger attributes,
934 * the data will be directly read from the uio.
935 */
936 if (attrsize > 0 &&
937 hfsmp->hfs_max_inline_attrsize != 0 &&
938 attrsize < hfsmp->hfs_max_inline_attrsize) {
939 user_data_ptr = hfs_malloc(attrsize);
940
941 result = uiomove((caddr_t)user_data_ptr, attrsize, uio);
942 if (result) {
943 goto exit;
944 }
945 }
946
947 result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
948 if (result) {
949 goto exit;
950 }
951 cp = VTOC(vp);
952
953 /*
954 * If we're trying to set a non-finderinfo, non-resourcefork EA, then
955 * call the breakout function.
956 */
957 result = hfs_setxattr_internal (cp, user_data_ptr, attrsize, ap, VTOHFS(vp), 0);
958
959 exit:
960 if (cp) {
961 hfs_unlock(cp);
962 }
963 if (user_data_ptr) {
964 hfs_free(user_data_ptr, attrsize);
965 }
966
967 return (result == btNotFound ? ENOATTR : MacToVFSError(result));
968 }
969
970 // Has same limitations as hfs_setxattr_internal below
971 int hfs_xattr_write(vnode_t vp, const char *name, const void *data, size_t size)
972 {
973 struct vnop_setxattr_args args = {
974 .a_vp = vp,
975 .a_name = name,
976 };
977
978 return hfs_setxattr_internal(VTOC(vp), data, size, &args, VTOHFS(vp), 0);
979 }
980
981 /*
982 * hfs_setxattr_internal
983 *
984 * Internal function to set non-rsrc, non-finderinfo EAs to either the attribute B-Tree or
985 * extent-based EAs.
986 *
987 * See comments from hfs_getxattr_internal on why we need to pass 'hfsmp' and fileid here.
988 * The gist is that we could end up writing to the root folder which may not have a cnode.
989 *
990 * Assumptions:
991 * 1. cnode 'cp' is locked EXCLUSIVE before calling this function.
992 * 2. data_ptr contains data to be written. If gathering data from userland, this must be
993 * done before calling this function.
994 * 3. If data originates entirely in-kernel, use a null UIO, and ensure the size is less than
995 * hfsmp->hfs_max_inline_attrsize bytes long.
996 */
997 int hfs_setxattr_internal (struct cnode *cp, const void *data_ptr, size_t attrsize,
998 struct vnop_setxattr_args *ap, struct hfsmount *hfsmp,
999 u_int32_t fileid)
1000 {
1001 uio_t uio = ap->a_uio;
1002 struct vnode *vp = ap->a_vp;
1003 int started_transaction = 0;
1004 struct BTreeIterator * iterator = NULL;
1005 struct filefork *btfile = NULL;
1006 FSBufferDescriptor btdata;
1007 HFSPlusAttrRecord attrdata; /* 90 bytes */
1008 HFSPlusAttrRecord *recp = NULL;
1009 size_t recp_size = 0;
1010 HFSPlusExtentDescriptor *extentptr = NULL;
1011 size_t extentbufsize = 0;
1012 int result = 0;
1013 int lockflags = 0;
1014 int exists = 0;
1015 int allocatedblks = 0;
1016 u_int32_t target_id;
1017
1018 if (cp) {
1019 target_id = cp->c_fileid;
1020 } else {
1021 target_id = fileid;
1022 }
1023
1024 /* Start a transaction for our changes. */
1025 if (hfs_start_transaction(hfsmp) != 0) {
1026 result = EINVAL;
1027 goto exit;
1028 }
1029 started_transaction = 1;
1030
1031 /*
1032 * Once we started the transaction, nobody can compete
1033 * with us, so make sure this file is still there.
1034 */
1035 if ((cp) && (cp->c_flag & C_NOEXISTS)) {
1036 result = ENOENT;
1037 goto exit;
1038 }
1039
1040 /*
1041 * If there isn't an attributes b-tree then create one.
1042 */
1043 if (hfsmp->hfs_attribute_vp == NULL) {
1044 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
1045 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
1046 if (result) {
1047 goto exit;
1048 }
1049 }
1050 if (hfsmp->hfs_max_inline_attrsize == 0) {
1051 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
1052 }
1053
1054 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1055
1056 /* Build the b-tree key. */
1057 iterator = hfs_mallocz(sizeof(*iterator));
1058 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1059 if (result) {
1060 goto exit;
1061 }
1062
1063 /* Preflight for replace/create semantics. */
1064 btfile = VTOF(hfsmp->hfs_attribute_vp);
1065 btdata.bufferAddress = &attrdata;
1066 btdata.itemSize = sizeof(attrdata);
1067 btdata.itemCount = 1;
1068 exists = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL) == 0;
1069
1070 /* Replace requires that the attribute already exists. */
1071 if ((ap->a_options & XATTR_REPLACE) && !exists) {
1072 result = ENOATTR;
1073 goto exit;
1074 }
1075 /* Create requires that the attribute doesn't exist. */
1076 if ((ap->a_options & XATTR_CREATE) && exists) {
1077 result = EEXIST;
1078 goto exit;
1079 }
1080
1081 /* If it won't fit inline then use extent-based attributes. */
1082 if (attrsize > hfsmp->hfs_max_inline_attrsize) {
1083 #if (TARGET_OS_OSX && TARGET_CPU_ARM64)
1084 printf("hfs_setxattr: non-inline attributes are not supported\n");
1085 //NOTE: ENOTSUP will fool XNU into thinking we need AppleDouble files...
1086 result = EPERM;
1087 goto exit;
1088 #else
1089 int blkcnt;
1090 int extentblks;
1091 u_int32_t *keystartblk;
1092 int i;
1093
1094 if (uio == NULL) {
1095 /*
1096 * setxattrs originating from in-kernel are not supported if they are bigger
1097 * than the inline max size. Just return ENOATTR and force them to do it with a
1098 * smaller EA.
1099 */
1100 result = EPERM;
1101 goto exit;
1102 }
1103
1104 /* Get some blocks. */
1105 blkcnt = howmany(attrsize, hfsmp->blockSize);
1106 extentbufsize = blkcnt * sizeof(HFSPlusExtentDescriptor);
1107 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
1108 extentptr = hfs_mallocz(extentbufsize);
1109 result = alloc_attr_blks(hfsmp, attrsize, extentbufsize, extentptr, &allocatedblks);
1110 if (result) {
1111 allocatedblks = 0;
1112 goto exit; /* no more space */
1113 }
1114 /* Copy data into the blocks. */
1115 result = write_attr_data(hfsmp, uio, attrsize, extentptr);
1116 if (result) {
1117 if (vp) {
1118 const char *name = vnode_getname(vp);
1119 printf("hfs_setxattr: write_attr_data vol=%s err (%d) %s:%s\n",
1120 hfsmp->vcbVN, result, name ? name : "", ap->a_name);
1121 if (name)
1122 vnode_putname(name);
1123 }
1124 goto exit;
1125 }
1126
1127 /* Now remove any previous attribute. */
1128 if (exists) {
1129 result = remove_attribute_records(hfsmp, iterator);
1130 if (result) {
1131 if (vp) {
1132 const char *name = vnode_getname(vp);
1133 printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n",
1134 hfsmp->vcbVN, result, name ? name : "", ap->a_name);
1135 if (name)
1136 vnode_putname(name);
1137 }
1138 goto exit;
1139 }
1140 }
1141 /* Create attribute fork data record. */
1142 recp = hfs_malloc(recp_size = sizeof(HFSPlusAttrRecord));
1143
1144 btdata.bufferAddress = recp;
1145 btdata.itemCount = 1;
1146 btdata.itemSize = sizeof(HFSPlusAttrForkData);
1147
1148 recp->recordType = kHFSPlusAttrForkData;
1149 recp->forkData.reserved = 0;
1150 recp->forkData.theFork.logicalSize = attrsize;
1151 recp->forkData.theFork.clumpSize = 0;
1152 recp->forkData.theFork.totalBlocks = blkcnt;
1153 bcopy(extentptr, recp->forkData.theFork.extents, sizeof(HFSPlusExtentRecord));
1154
1155 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1156
1157 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1158 if (result) {
1159 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
1160 hfsmp->vcbVN, target_id, ap->a_name, result);
1161 goto exit;
1162 }
1163 extentblks = count_extent_blocks(blkcnt, recp->forkData.theFork.extents);
1164 blkcnt -= extentblks;
1165 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1166 i = 0;
1167
1168 /* Create overflow extents as needed. */
1169 while (blkcnt > 0) {
1170 /* Initialize the key and record. */
1171 *keystartblk += (u_int32_t)extentblks;
1172 btdata.itemSize = sizeof(HFSPlusAttrExtents);
1173 recp->recordType = kHFSPlusAttrExtents;
1174 recp->overflowExtents.reserved = 0;
1175
1176 /* Copy the next set of extents. */
1177 i += kHFSPlusExtentDensity;
1178 bcopy(&extentptr[i], recp->overflowExtents.extents, sizeof(HFSPlusExtentRecord));
1179
1180 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1181 if (result) {
1182 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
1183 hfsmp->vcbVN, target_id, ap->a_name, result);
1184 goto exit;
1185 }
1186 extentblks = count_extent_blocks(blkcnt, recp->overflowExtents.extents);
1187 blkcnt -= extentblks;
1188 }
1189 #endif //(TARGET_OS_OSX && TARGET_CPU_ARM64)
1190 } else { /* Inline data */
1191 if (exists) {
1192 result = remove_attribute_records(hfsmp, iterator);
1193 if (result) {
1194 goto exit;
1195 }
1196 }
1197
1198 /* Calculate size of record rounded up to multiple of 2 bytes. */
1199 btdata.itemSize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0);
1200 recp = hfs_malloc(recp_size = btdata.itemSize);
1201
1202 recp->recordType = kHFSPlusAttrInlineData;
1203 recp->attrData.reserved[0] = 0;
1204 recp->attrData.reserved[1] = 0;
1205 recp->attrData.attrSize = attrsize;
1206
1207 /* Copy in the attribute data (if any). */
1208 if (attrsize > 0) {
1209 if (data_ptr) {
1210 bcopy(data_ptr, &recp->attrData.attrData, attrsize);
1211 } else {
1212 /*
1213 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
1214 * then deny the copy operation.
1215 */
1216 if (uio == NULL) {
1217 result = EPERM;
1218 goto exit;
1219 }
1220 result = uiomove((caddr_t)&recp->attrData.attrData, attrsize, uio);
1221 }
1222
1223 if (result) {
1224 goto exit;
1225 }
1226 }
1227
1228 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1229
1230 btdata.bufferAddress = recp;
1231 btdata.itemCount = 1;
1232 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1233 }
1234
1235 exit:
1236 if (btfile && started_transaction) {
1237 (void) BTFlushPath(btfile);
1238 }
1239 hfs_systemfile_unlock(hfsmp, lockflags);
1240 if (result == 0) {
1241 if (vp) {
1242 cp = VTOC(vp);
1243 /* Setting an attribute only updates change time and not
1244 * modified time of the file.
1245 */
1246 cp->c_touch_chgtime = TRUE;
1247 cp->c_flag |= C_MODIFIED;
1248 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
1249 if ((strcmp(ap->a_name, KAUTH_FILESEC_XATTR) == 0)) {
1250 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
1251 }
1252 (void) hfs_update(vp, 0);
1253 }
1254 }
1255 if (started_transaction) {
1256 if (result && allocatedblks) {
1257 free_attr_blks(hfsmp, allocatedblks, extentptr);
1258 }
1259 hfs_end_transaction(hfsmp);
1260 }
1261
1262 hfs_free(recp, recp_size);
1263 hfs_free(extentptr, extentbufsize);
1264 hfs_free(iterator, sizeof(*iterator));
1265
1266 return result;
1267 }
1268
1269
1270
1271
1272 /*
1273 * Remove an extended attribute.
1274 */
1275 int
1276 hfs_vnop_removexattr(struct vnop_removexattr_args *ap)
1277 /*
1278 struct vnop_removexattr_args {
1279 struct vnodeop_desc *a_desc;
1280 vnode_t a_vp;
1281 char * a_name;
1282 int a_options;
1283 vfs_context_t a_context;
1284 };
1285 */
1286 {
1287 struct vnode *vp = ap->a_vp;
1288 struct cnode *cp = VTOC(vp);
1289 struct hfsmount *hfsmp;
1290 struct BTreeIterator * iterator = NULL;
1291 int lockflags;
1292 int result;
1293 time_t orig_ctime=VTOC(vp)->c_ctime;
1294
1295 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
1296 return (EINVAL); /* invalid name */
1297 }
1298 hfsmp = VTOHFS(vp);
1299 if (VNODE_IS_RSRC(vp)) {
1300 return (EPERM);
1301 }
1302
1303 #if HFS_COMPRESSION
1304 if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) && !(ap->a_options & XATTR_SHOWCOMPRESSION)) {
1305 return ENOATTR;
1306 }
1307 #endif /* HFS_COMPRESSION */
1308
1309 nspace_snapshot_event(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_DELETE_OP, NSPACE_REARM_NO_ARG);
1310
1311 /* If Resource Fork is non-empty then truncate it. */
1312 if (strcmp(ap->a_name, XATTR_RESOURCEFORK_NAME) == 0) {
1313 struct vnode *rvp = NULL;
1314
1315 if ( !vnode_isreg(vp) ) {
1316 return (EPERM);
1317 }
1318 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1319 return (result);
1320 }
1321 if (!hfs_has_rsrc(cp)) {
1322 hfs_unlock(cp);
1323 return (ENOATTR);
1324 }
1325 result = hfs_vgetrsrc(hfsmp, vp, &rvp);
1326 hfs_unlock(cp);
1327 if (result) {
1328 return (result);
1329 }
1330
1331 hfs_lock_truncate(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
1332
1333 // Tell UBC now before we take the cnode lock and start the transaction
1334 hfs_ubc_setsize(rvp, 0, false);
1335
1336 if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1337 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1338 vnode_put(rvp);
1339 return (result);
1340 }
1341
1342 /* Start a transaction for encapsulating changes in
1343 * hfs_truncate() and hfs_update()
1344 */
1345 if ((result = hfs_start_transaction(hfsmp))) {
1346 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1347 hfs_unlock(cp);
1348 vnode_put(rvp);
1349 return (result);
1350 }
1351
1352 result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context);
1353 if (result == 0) {
1354 cp->c_touch_chgtime = TRUE;
1355 cp->c_flag |= C_MODIFIED;
1356 result = hfs_update(vp, 0);
1357 }
1358
1359 hfs_end_transaction(hfsmp);
1360 hfs_unlock_truncate(VTOC(rvp), HFS_LOCK_DEFAULT);
1361 hfs_unlock(VTOC(rvp));
1362
1363 vnode_put(rvp);
1364 return (result);
1365 }
1366 /* Clear out the Finder Info. */
1367 if (strcmp(ap->a_name, XATTR_FINDERINFO_NAME) == 0) {
1368 void * finderinfo_start;
1369 int finderinfo_size;
1370 u_int8_t finderinfo[32];
1371 u_int32_t date_added = 0, write_gen_counter = 0, document_id = 0;
1372 u_int8_t *finfo = NULL;
1373
1374 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1375 return (result);
1376 }
1377
1378 /* Use the local copy to store our temporary changes. */
1379 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
1380
1381
1382 /* Zero out the date added field in the local copy */
1383 hfs_zero_hidden_fields (cp, finderinfo);
1384
1385 /* Don't expose a symlink's private type/creator. */
1386 if (vnode_islnk(vp)) {
1387 struct FndrFileInfo *fip;
1388
1389 fip = (struct FndrFileInfo *)&finderinfo;
1390 fip->fdType = 0;
1391 fip->fdCreator = 0;
1392 }
1393
1394 /* Do the byte compare against the local copy */
1395 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
1396 hfs_unlock(cp);
1397 return (ENOATTR);
1398 }
1399
1400 /*
1401 * If there was other content, zero out everything except
1402 * type/creator and date added. First, save the date added.
1403 */
1404 finfo = cp->c_finderinfo;
1405 finfo = finfo + 16;
1406 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
1407 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
1408 date_added = extinfo->date_added;
1409 write_gen_counter = extinfo->write_gen_counter;
1410 document_id = extinfo->document_id;
1411 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
1412 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
1413 date_added = extinfo->date_added;
1414 write_gen_counter = extinfo->write_gen_counter;
1415 document_id = extinfo->document_id;
1416 }
1417
1418 if (vnode_islnk(vp)) {
1419 /* Ignore type/creator */
1420 finderinfo_start = &cp->c_finderinfo[8];
1421 finderinfo_size = sizeof(cp->c_finderinfo) - 8;
1422 } else {
1423 finderinfo_start = &cp->c_finderinfo[0];
1424 finderinfo_size = sizeof(cp->c_finderinfo);
1425 }
1426 bzero(finderinfo_start, finderinfo_size);
1427
1428
1429 /* Now restore the date added */
1430 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
1431 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
1432 extinfo->date_added = date_added;
1433 extinfo->write_gen_counter = write_gen_counter;
1434 extinfo->document_id = document_id;
1435 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
1436 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
1437 extinfo->date_added = date_added;
1438 extinfo->write_gen_counter = write_gen_counter;
1439 extinfo->document_id = document_id;
1440 }
1441
1442 /* Updating finderInfo updates change time and modified time */
1443 cp->c_touch_chgtime = TRUE;
1444 cp->c_flag |= C_MODIFIED;
1445 hfs_update(vp, 0);
1446
1447 hfs_unlock(cp);
1448
1449 return (0);
1450 }
1451 #if CONFIG_HFS_STD
1452 /*
1453 * Standard HFS only supports native FinderInfo and Resource Forks.
1454 */
1455 if (hfsmp->hfs_flags & HFS_STANDARD) {
1456 return (EPERM);
1457 }
1458 #endif
1459 if (hfsmp->hfs_attribute_vp == NULL) {
1460 return (ENOATTR);
1461 }
1462
1463 iterator = hfs_mallocz(sizeof(*iterator));
1464
1465 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1466 goto exit_nolock;
1467 }
1468
1469 result = hfs_buildattrkey(cp->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1470 if (result) {
1471 goto exit;
1472 }
1473
1474 if (hfs_start_transaction(hfsmp) != 0) {
1475 result = EINVAL;
1476 goto exit;
1477 }
1478 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1479
1480 result = remove_attribute_records(hfsmp, iterator);
1481
1482 hfs_systemfile_unlock(hfsmp, lockflags);
1483
1484 if (result == 0) {
1485 cp->c_touch_chgtime = TRUE;
1486
1487 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1488
1489 /* If no more attributes exist, clear attribute bit */
1490 result = file_attribute_exist(hfsmp, cp->c_fileid);
1491 if (result == 0) {
1492 cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask;
1493 cp->c_flag |= C_MODIFIED;
1494 }
1495 if (result == EEXIST) {
1496 result = 0;
1497 }
1498
1499 hfs_systemfile_unlock(hfsmp, lockflags);
1500
1501 /* If ACL was removed, clear security bit */
1502 if (strcmp(ap->a_name, KAUTH_FILESEC_XATTR) == 0) {
1503 cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
1504 cp->c_flag |= C_MODIFIED;
1505 }
1506 (void) hfs_update(vp, 0);
1507 }
1508
1509 hfs_end_transaction(hfsmp);
1510 exit:
1511 hfs_unlock(cp);
1512 exit_nolock:
1513 hfs_free(iterator, sizeof(*iterator));
1514 return MacToVFSError(result);
1515 }
1516
1517 /*
1518 * Removes a non rsrc-fork, non-finderinfo EA from the specified file ID.
1519 * Note that this results in a bit of code duplication for the xattr removal
1520 * path. This is done because it's a bit messy to deal with things without the
1521 * cnode. This function is used by exchangedata to port XATTRS to alternate
1522 * fileIDs while everything is locked, and the cnodes are in a transitional state.
1523 *
1524 * Assumes that the cnode backing the fileid specified is LOCKED.
1525 */
1526
1527 int
1528 hfs_removexattr_by_id (struct hfsmount *hfsmp, uint32_t fileid, const char *xattr_name ) {
1529 struct BTreeIterator iter; // allocated on the stack to avoid heap allocation mid-txn
1530 int ret = 0;
1531 int started_txn = 0;
1532 int lockflags;
1533
1534 memset (&iter, 0, sizeof(iter));
1535
1536 //position the B-Tree iter key before grabbing locks and starting a txn
1537 ret = hfs_buildattrkey (fileid, xattr_name, (HFSPlusAttrKey*)&iter.key);
1538 if (ret) {
1539 goto xattr_out;
1540 }
1541
1542 //note: this is likely a nested transaction since there is a global transaction cover
1543 if (hfs_start_transaction (hfsmp) != 0) {
1544 ret = EINVAL;
1545 goto xattr_out;
1546 }
1547 started_txn = 1;
1548
1549
1550 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1551
1552 //actually remove the EA from the tree
1553 ret = remove_attribute_records(hfsmp, &iter);
1554
1555 hfs_systemfile_unlock(hfsmp, lockflags);
1556
1557 /*
1558 * NOTE: Responsibility of the caller to remove the "has XATTRs" bit in the catalog record
1559 * if this was the last EA.
1560 */
1561
1562
1563 xattr_out:
1564 if (started_txn) {
1565 hfs_end_transaction(hfsmp);
1566 }
1567
1568 return MacToVFSError(ret);
1569
1570 }
1571
1572
1573 /* Check if any attribute record exist for given fileID. This function
1574 * is called by hfs_vnop_removexattr to determine if it should clear the
1575 * attribute bit in the catalog record or not.
1576 *
1577 * Note - you must acquire a shared lock on the attribute btree before
1578 * calling this function.
1579 *
1580 * Output:
1581 * EEXIST - If attribute record was found
1582 * 0 - Attribute was not found
1583 * (other) - Other error (such as EIO)
1584 */
1585 int
1586 file_attribute_exist(struct hfsmount *hfsmp, uint32_t fileID)
1587 {
1588 HFSPlusAttrKey *key;
1589 struct BTreeIterator * iterator = NULL;
1590 struct filefork *btfile;
1591 int result = 0;
1592
1593 // if there's no attribute b-tree we sure as heck
1594 // can't have any attributes!
1595 if (hfsmp->hfs_attribute_vp == NULL) {
1596 return false;
1597 }
1598
1599 iterator = hfs_mallocz(sizeof(*iterator));
1600
1601 key = (HFSPlusAttrKey *)&iterator->key;
1602
1603 result = hfs_buildattrkey(fileID, NULL, key);
1604 if (result) {
1605 goto out;
1606 }
1607
1608 btfile = VTOF(hfsmp->hfs_attribute_vp);
1609 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1610 if (result && (result != btNotFound)) {
1611 goto out;
1612 }
1613
1614 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1615 /* If no next record was found or fileID for next record did not match,
1616 * no more attributes exist for this fileID
1617 */
1618 if ((result && (result == btNotFound)) || (key->fileID != fileID)) {
1619 result = 0;
1620 } else {
1621 result = EEXIST;
1622 }
1623
1624 out:
1625 hfs_free(iterator, sizeof(*iterator));
1626 return result;
1627 }
1628
1629
1630 /*
1631 * Remove all the records for a given attribute.
1632 *
1633 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1634 * - A transaction must have been started.
1635 * - The Attribute b-tree file must be locked exclusive.
1636 * - The Allocation Bitmap file must be locked exclusive.
1637 * - The iterator key must be initialized.
1638 */
1639 int
1640 remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator)
1641 {
1642 struct filefork *btfile;
1643 FSBufferDescriptor btdata;
1644 HFSPlusAttrRecord attrdata; /* 90 bytes */
1645 u_int16_t datasize;
1646 int result;
1647
1648 btfile = VTOF(hfsmp->hfs_attribute_vp);
1649
1650 btdata.bufferAddress = &attrdata;
1651 btdata.itemSize = sizeof(attrdata);
1652 btdata.itemCount = 1;
1653 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1654 if (result) {
1655 goto exit; /* no records. */
1656 }
1657 /*
1658 * Free the blocks from extent based attributes.
1659 *
1660 * Note that the block references (btree records) are removed
1661 * before releasing the blocks in the allocation bitmap.
1662 */
1663 if (attrdata.recordType == kHFSPlusAttrForkData) {
1664 int totalblks;
1665 int extentblks;
1666 u_int32_t *keystartblk;
1667
1668 if (datasize < sizeof(HFSPlusAttrForkData)) {
1669 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize, sizeof(HFSPlusAttrForkData));
1670 }
1671 totalblks = attrdata.forkData.theFork.totalBlocks;
1672
1673 /* Process the first 8 extents. */
1674 extentblks = count_extent_blocks(totalblks, attrdata.forkData.theFork.extents);
1675 if (extentblks > totalblks)
1676 panic("hfs: remove_attribute_records: corruption...");
1677 if (BTDeleteRecord(btfile, iterator) == 0) {
1678 free_attr_blks(hfsmp, extentblks, attrdata.forkData.theFork.extents);
1679 }
1680 totalblks -= extentblks;
1681 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1682
1683 /* Process any overflow extents. */
1684 while (totalblks) {
1685 *keystartblk += (u_int32_t)extentblks;
1686
1687 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1688 if (result ||
1689 (attrdata.recordType != kHFSPlusAttrExtents) ||
1690 (datasize < sizeof(HFSPlusAttrExtents))) {
1691 printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n",
1692 hfsmp->vcbVN, MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks);
1693 result = ENOATTR;
1694 break; /* break from while */
1695 }
1696 /* Process the next 8 extents. */
1697 extentblks = count_extent_blocks(totalblks, attrdata.overflowExtents.extents);
1698 if (extentblks > totalblks)
1699 panic("hfs: remove_attribute_records: corruption...");
1700 if (BTDeleteRecord(btfile, iterator) == 0) {
1701 free_attr_blks(hfsmp, extentblks, attrdata.overflowExtents.extents);
1702 }
1703 totalblks -= extentblks;
1704 }
1705 } else {
1706 result = BTDeleteRecord(btfile, iterator);
1707 }
1708 (void) BTFlushPath(btfile);
1709 exit:
1710 return (result == btNotFound ? ENOATTR : MacToVFSError(result));
1711 }
1712
1713
1714 /*
1715 * Retrieve the list of extended attribute names.
1716 */
1717 int
1718 hfs_vnop_listxattr(struct vnop_listxattr_args *ap)
1719 /*
1720 struct vnop_listxattr_args {
1721 struct vnodeop_desc *a_desc;
1722 vnode_t a_vp;
1723 uio_t a_uio;
1724 size_t *a_size;
1725 int a_options;
1726 vfs_context_t a_context;
1727 */
1728 {
1729 struct vnode *vp = ap->a_vp;
1730 struct cnode *cp = VTOC(vp);
1731 struct hfsmount *hfsmp;
1732 uio_t uio = ap->a_uio;
1733 struct BTreeIterator * iterator = NULL;
1734 struct filefork *btfile;
1735 struct listattr_callback_state state;
1736 user_addr_t user_start = 0;
1737 user_size_t user_len = 0;
1738 int lockflags;
1739 int result;
1740 u_int8_t finderinfo[32];
1741
1742
1743 if (VNODE_IS_RSRC(vp)) {
1744 return (EPERM);
1745 }
1746
1747 #if HFS_COMPRESSION
1748 int compressed = hfs_file_is_compressed(cp, 1); /* 1 == don't take the cnode lock */
1749 #endif /* HFS_COMPRESSION */
1750
1751 hfsmp = VTOHFS(vp);
1752 *ap->a_size = 0;
1753
1754 /*
1755 * Take the truncate lock; this serializes us against the ioctl
1756 * to truncate data & reset the decmpfs state
1757 * in the compressed file handler.
1758 */
1759 hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
1760
1761 /* Now the regular cnode lock (shared) */
1762 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
1763 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1764 return (result);
1765 }
1766
1767 /*
1768 * Make a copy of the cnode's finderinfo to a local so we can
1769 * zero out the date added field. Also zero out the private type/creator
1770 * for symlinks.
1771 */
1772 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
1773 hfs_zero_hidden_fields (cp, finderinfo);
1774
1775 /* Don't expose a symlink's private type/creator. */
1776 if (vnode_islnk(vp)) {
1777 struct FndrFileInfo *fip;
1778
1779 fip = (struct FndrFileInfo *)&finderinfo;
1780 fip->fdType = 0;
1781 fip->fdCreator = 0;
1782 }
1783
1784
1785 /* If Finder Info is non-empty then export it's name. */
1786 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) {
1787 if (uio == NULL) {
1788 *ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
1789 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
1790 result = ERANGE;
1791 goto exit;
1792 } else {
1793 result = uiomove(XATTR_FINDERINFO_NAME,
1794 sizeof(XATTR_FINDERINFO_NAME), uio);
1795 if (result)
1796 goto exit;
1797 }
1798 }
1799 /* If Resource Fork is non-empty then export it's name. */
1800 if (S_ISREG(cp->c_mode) && hfs_has_rsrc(cp)) {
1801 #if HFS_COMPRESSION
1802 if ((ap->a_options & XATTR_SHOWCOMPRESSION) ||
1803 !compressed ||
1804 !decmpfs_hides_rsrc(ap->a_context, VTOCMP(vp))
1805 )
1806 #endif /* HFS_COMPRESSION */
1807 {
1808 if (uio == NULL) {
1809 *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
1810 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
1811 result = ERANGE;
1812 goto exit;
1813 } else {
1814 result = uiomove(XATTR_RESOURCEFORK_NAME,
1815 sizeof(XATTR_RESOURCEFORK_NAME), uio);
1816 if (result)
1817 goto exit;
1818 }
1819 }
1820 }
1821 #if CONFIG_HFS_STD
1822 /*
1823 * Standard HFS only supports native FinderInfo and Resource Forks.
1824 * Return at this point.
1825 */
1826 if (hfsmp->hfs_flags & HFS_STANDARD) {
1827 result = 0;
1828 goto exit;
1829 }
1830 #endif
1831 /* Bail if we don't have any extended attributes. */
1832 if ((hfsmp->hfs_attribute_vp == NULL) ||
1833 (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
1834 result = 0;
1835 goto exit;
1836 }
1837 btfile = VTOF(hfsmp->hfs_attribute_vp);
1838
1839 iterator = hfs_mallocz(sizeof(*iterator));
1840
1841 result = hfs_buildattrkey(cp->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key);
1842 if (result)
1843 goto exit;
1844
1845 /*
1846 * Lock the user's buffer here so that we won't fault on
1847 * it in uiomove while holding the attributes b-tree lock.
1848 */
1849 if (uio && uio_isuserspace(uio)) {
1850 user_start = uio_curriovbase(uio);
1851 user_len = uio_curriovlen(uio);
1852
1853 if ((result = vslock(user_start, user_len)) != 0) {
1854 user_start = 0;
1855 goto exit;
1856 }
1857 }
1858 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1859
1860 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1861 if (result && result != btNotFound) {
1862 hfs_systemfile_unlock(hfsmp, lockflags);
1863 goto exit;
1864 }
1865
1866 state.fileID = cp->c_fileid;
1867 state.result = 0;
1868 state.uio = uio;
1869 state.size = 0;
1870 #if HFS_COMPRESSION
1871 state.showcompressed = !compressed || ap->a_options & XATTR_SHOWCOMPRESSION;
1872 state.ctx = ap->a_context;
1873 state.vp = vp;
1874 #endif /* HFS_COMPRESSION */
1875
1876 /*
1877 * Process entries starting just after iterator->key.
1878 */
1879 result = BTIterateRecords(btfile, kBTreeNextRecord, iterator,
1880 (IterateCallBackProcPtr)listattr_callback, &state);
1881 hfs_systemfile_unlock(hfsmp, lockflags);
1882 if (uio == NULL) {
1883 *ap->a_size += state.size;
1884 }
1885
1886 if (state.result || result == btNotFound)
1887 result = state.result;
1888
1889 exit:
1890 if (user_start) {
1891 vsunlock(user_start, user_len, TRUE);
1892 }
1893 hfs_free(iterator, sizeof(*iterator));
1894 hfs_unlock(cp);
1895 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1896
1897 return MacToVFSError(result);
1898 }
1899
1900
1901 /*
1902 * Callback - called for each attribute record
1903 */
1904 static int
1905 listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state)
1906 {
1907 char attrname[XATTR_MAXNAMELEN + 1];
1908 ssize_t bytecount;
1909 int result;
1910
1911 if (state->fileID != key->fileID) {
1912 state->result = 0;
1913 return (0); /* stop */
1914 }
1915 /*
1916 * Skip over non-primary keys
1917 */
1918 if (key->startBlock != 0) {
1919 return (1); /* continue */
1920 }
1921
1922 /* Convert the attribute name into UTF-8. */
1923 result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar),
1924 (u_int8_t *)attrname, (size_t *)&bytecount, sizeof(attrname), '/', 0);
1925 if (result) {
1926 state->result = result;
1927 return (0); /* stop */
1928 }
1929 bytecount++; /* account for null termination char */
1930
1931 if (xattr_protected(attrname))
1932 return (1); /* continue */
1933
1934 #if HFS_COMPRESSION
1935 if (!state->showcompressed && decmpfs_hides_xattr(state->ctx, VTOCMP(state->vp), attrname) )
1936 return 1; /* continue */
1937 #endif /* HFS_COMPRESSION */
1938
1939 if (state->uio == NULL) {
1940 state->size += bytecount;
1941 } else {
1942 if (bytecount > uio_resid(state->uio)) {
1943 state->result = ERANGE;
1944 return (0); /* stop */
1945 }
1946 result = uiomove((caddr_t) attrname, bytecount, state->uio);
1947 if (result) {
1948 state->result = result;
1949 return (0); /* stop */
1950 }
1951 }
1952 return (1); /* continue */
1953 }
1954
1955 /*
1956 * Remove all the attributes from a cnode.
1957 *
1958 * This function creates/ends its own transaction so that each
1959 * attribute is deleted in its own transaction (to avoid having
1960 * a transaction grow too large).
1961 *
1962 * This function takes the necessary locks on the attribute
1963 * b-tree file and the allocation (bitmap) file.
1964 *
1965 * NOTE: Upon sucecss, this function will return with an open
1966 * transaction. The reason we do it this way is because when we
1967 * delete the last attribute, we must make sure the flag in the
1968 * catalog record that indicates there are no more records is cleared.
1969 * The caller is responsible for doing this and *must* do it before
1970 * ending the transaction.
1971 */
1972 int
1973 hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid,
1974 bool *open_transaction)
1975 {
1976 BTreeIterator *iterator = NULL;
1977 HFSPlusAttrKey *key;
1978 struct filefork *btfile;
1979 int result, lockflags = 0;
1980
1981 *open_transaction = false;
1982
1983 if (hfsmp->hfs_attribute_vp == NULL)
1984 return 0;
1985
1986 btfile = VTOF(hfsmp->hfs_attribute_vp);
1987
1988 iterator = hfs_mallocz(sizeof(BTreeIterator));
1989
1990 key = (HFSPlusAttrKey *)&iterator->key;
1991
1992 /* Loop until there are no more attributes for this file id */
1993 do {
1994 if (!*open_transaction)
1995 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1996
1997 (void) hfs_buildattrkey(fileid, NULL, key);
1998 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1999 if (result || key->fileID != fileid)
2000 goto exit;
2001
2002 hfs_systemfile_unlock(hfsmp, lockflags);
2003 lockflags = 0;
2004
2005 if (*open_transaction) {
2006 hfs_end_transaction(hfsmp);
2007 *open_transaction = false;
2008 }
2009
2010 if (hfs_start_transaction(hfsmp) != 0) {
2011 result = EINVAL;
2012 goto exit;
2013 }
2014
2015 *open_transaction = true;
2016
2017 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2018
2019 result = remove_attribute_records(hfsmp, iterator);
2020
2021 #if HFS_XATTR_VERBOSE
2022 if (result) {
2023 printf("hfs_removeallattr: unexpected err %d\n", result);
2024 }
2025 #endif
2026 } while (!result);
2027
2028 exit:
2029 hfs_free(iterator, sizeof(*iterator));
2030
2031 if (lockflags)
2032 hfs_systemfile_unlock(hfsmp, lockflags);
2033
2034 result = result == btNotFound ? 0 : MacToVFSError(result);
2035
2036 if (result && *open_transaction) {
2037 hfs_end_transaction(hfsmp);
2038 *open_transaction = false;
2039 }
2040
2041 return result;
2042 }
2043
2044 void
2045 hfs_xattr_init(struct hfsmount * hfsmp)
2046 {
2047 #if CONFIG_HFS_STD
2048 if (ISSET(hfsmp->hfs_flags, HFS_STANDARD))
2049 return;
2050 #endif
2051
2052 /*
2053 * If there isn't an attributes b-tree then create one.
2054 */
2055 if ((hfsmp->hfs_attribute_vp == NULL) &&
2056 !(hfsmp->hfs_flags & HFS_READ_ONLY)) {
2057 (void) hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
2058 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
2059 }
2060 if (hfsmp->hfs_attribute_vp)
2061 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
2062 }
2063
2064 /*
2065 * Enable/Disable volume attributes stored as EA for root file system.
2066 * Supported attributes are -
2067 * 1. Extent-based Extended Attributes
2068 */
2069 int
2070 hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state)
2071 {
2072 struct BTreeIterator * iterator = NULL;
2073 struct filefork *btfile;
2074 int lockflags;
2075 int result;
2076
2077 #if CONFIG_HFS_STD
2078 if (hfsmp->hfs_flags & HFS_STANDARD) {
2079 return (ENOTSUP);
2080 }
2081 #endif
2082 if (xattrtype != HFSIOC_SET_XATTREXTENTS_STATE) {
2083 return EINVAL;
2084 }
2085
2086 /*
2087 * If there isn't an attributes b-tree then create one.
2088 */
2089 if (hfsmp->hfs_attribute_vp == NULL) {
2090 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
2091 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
2092 if (result) {
2093 return (result);
2094 }
2095 }
2096
2097 iterator = hfs_mallocz(sizeof(*iterator));
2098
2099 /*
2100 * Build a b-tree key.
2101 * We use the root's parent id (1) to hold this volume attribute.
2102 */
2103 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
2104 (HFSPlusAttrKey *)&iterator->key);
2105
2106 /* Start a transaction for our changes. */
2107 if (hfs_start_transaction(hfsmp) != 0) {
2108 result = EINVAL;
2109 goto exit;
2110 }
2111 btfile = VTOF(hfsmp->hfs_attribute_vp);
2112
2113 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2114
2115 if (state == 0) {
2116 /* Remove the attribute. */
2117 result = BTDeleteRecord(btfile, iterator);
2118 if (result == btNotFound)
2119 result = 0;
2120 } else {
2121 FSBufferDescriptor btdata;
2122 HFSPlusAttrData attrdata;
2123 u_int16_t datasize;
2124
2125 datasize = sizeof(attrdata);
2126 btdata.bufferAddress = &attrdata;
2127 btdata.itemSize = datasize;
2128 btdata.itemCount = 1;
2129 attrdata.recordType = kHFSPlusAttrInlineData;
2130 attrdata.reserved[0] = 0;
2131 attrdata.reserved[1] = 0;
2132 attrdata.attrSize = 2;
2133 attrdata.attrData[0] = 0;
2134 attrdata.attrData[1] = 0;
2135
2136 /* Insert the attribute. */
2137 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
2138 if (result == btExists)
2139 result = 0;
2140 }
2141 (void) BTFlushPath(btfile);
2142
2143 hfs_systemfile_unlock(hfsmp, lockflags);
2144
2145 /* Finish the transaction of our changes. */
2146 hfs_end_transaction(hfsmp);
2147
2148 /* Update the state in the mount point */
2149 hfs_lock_mount (hfsmp);
2150 if (state == 0) {
2151 hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS;
2152 } else {
2153 hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
2154 }
2155 hfs_unlock_mount (hfsmp);
2156
2157 exit:
2158 hfs_free(iterator, sizeof(*iterator));
2159 return MacToVFSError(result);
2160 }
2161
2162
2163 /*
2164 * hfs_attrkeycompare - compare two attribute b-tree keys.
2165 *
2166 * The name portion of the key is compared using a 16-bit binary comparison.
2167 * This is called from the b-tree code.
2168 */
2169 int
2170 hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey)
2171 {
2172 u_int32_t searchFileID, trialFileID;
2173 int result;
2174
2175 searchFileID = searchKey->fileID;
2176 trialFileID = trialKey->fileID;
2177 result = 0;
2178
2179 if (searchFileID > trialFileID) {
2180 ++result;
2181 } else if (searchFileID < trialFileID) {
2182 --result;
2183 } else {
2184 u_int16_t * str1 = &searchKey->attrName[0];
2185 u_int16_t * str2 = &trialKey->attrName[0];
2186 int length1 = searchKey->attrNameLen;
2187 int length2 = trialKey->attrNameLen;
2188 u_int16_t c1, c2;
2189 int length;
2190
2191 if (length1 < length2) {
2192 length = length1;
2193 --result;
2194 } else if (length1 > length2) {
2195 length = length2;
2196 ++result;
2197 } else {
2198 length = length1;
2199 }
2200
2201 while (length--) {
2202 c1 = *(str1++);
2203 c2 = *(str2++);
2204
2205 if (c1 > c2) {
2206 result = 1;
2207 break;
2208 }
2209 if (c1 < c2) {
2210 result = -1;
2211 break;
2212 }
2213 }
2214 if (result)
2215 return (result);
2216 /*
2217 * Names are equal; compare startBlock
2218 */
2219 if (searchKey->startBlock == trialKey->startBlock) {
2220 return (0);
2221 } else {
2222 return (searchKey->startBlock < trialKey->startBlock ? -1 : 1);
2223 }
2224 }
2225
2226 return result;
2227 }
2228
2229
2230 /*
2231 * hfs_buildattrkey - build an Attribute b-tree key
2232 */
2233 int
2234 hfs_buildattrkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key)
2235 {
2236 int result = 0;
2237 size_t unicodeBytes = 0;
2238
2239 if (attrname != NULL) {
2240 /*
2241 * Convert filename from UTF-8 into Unicode
2242 */
2243 result = utf8_decodestr((const u_int8_t *)attrname, strlen(attrname), key->attrName,
2244 &unicodeBytes, sizeof(key->attrName), 0, 0);
2245 if (result) {
2246 if (result != ENAMETOOLONG)
2247 result = EINVAL; /* name has invalid characters */
2248 return (result);
2249 }
2250 key->attrNameLen = unicodeBytes / sizeof(UniChar);
2251 key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes;
2252 } else {
2253 key->attrNameLen = 0;
2254 key->keyLength = kHFSPlusAttrKeyMinimumLength;
2255 }
2256 key->pad = 0;
2257 key->fileID = fileID;
2258 key->startBlock = 0;
2259
2260 return (0);
2261 }
2262
2263 /*
2264 * getnodecount - calculate starting node count for attributes b-tree.
2265 */
2266 static int
2267 getnodecount(struct hfsmount *hfsmp, size_t nodesize)
2268 {
2269 u_int64_t freebytes;
2270 u_int64_t calcbytes;
2271
2272 /*
2273 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
2274 * 10.5: Attempt to be as big as the catalog clump size.
2275 *
2276 * Use no more than 10 % of the remaining free space.
2277 */
2278 freebytes = (u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize;
2279
2280 calcbytes = MIN(hfsmp->hfs_catalog_cp->c_datafork->ff_size / 5, 20 * 1024 * 1024);
2281
2282 calcbytes = MAX(calcbytes, hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize);
2283
2284 calcbytes = MIN(calcbytes, freebytes / 10);
2285
2286 return (MAX(2, (int)(calcbytes / nodesize)));
2287 }
2288
2289
2290 /*
2291 * getmaxinlineattrsize - calculate maximum inline attribute size.
2292 *
2293 * This yields 3,802 bytes for an 8K node size.
2294 */
2295 static size_t
2296 getmaxinlineattrsize(struct vnode * attrvp)
2297 {
2298 struct BTreeInfoRec btinfo;
2299 size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE;
2300 size_t maxsize;
2301
2302 if (attrvp != NULL) {
2303 (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2304 if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0)
2305 nodesize = btinfo.nodeSize;
2306 hfs_unlock(VTOC(attrvp));
2307 }
2308 maxsize = nodesize;
2309 maxsize -= sizeof(BTNodeDescriptor); /* minus node descriptor */
2310 maxsize -= 3 * sizeof(u_int16_t); /* minus 3 index slots */
2311 maxsize /= 2; /* 2 key/rec pairs minumum */
2312 maxsize -= sizeof(HFSPlusAttrKey); /* minus maximum key size */
2313 maxsize -= sizeof(HFSPlusAttrData) - 2; /* minus data header */
2314 maxsize &= 0xFFFFFFFE; /* multiple of 2 bytes */
2315
2316 return (maxsize);
2317 }
2318
2319 /*
2320 * Initialize vnode for attribute data I/O.
2321 *
2322 * On success,
2323 * - returns zero
2324 * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp
2325 * - an iocount is taken on the attrdata vnode which exists
2326 * for the entire duration of the mount. It is only dropped
2327 * during unmount
2328 * - the attrdata cnode is not locked
2329 *
2330 * On failure,
2331 * - returns non-zero value
2332 * - the caller does not have to worry about any locks or references
2333 */
2334 int init_attrdata_vnode(struct hfsmount *hfsmp)
2335 {
2336 vnode_t vp;
2337 int result = 0;
2338 struct cat_desc cat_desc;
2339 struct cat_attr cat_attr;
2340 struct cat_fork cat_fork;
2341 int newvnode_flags = 0;
2342
2343 bzero(&cat_desc, sizeof(cat_desc));
2344 cat_desc.cd_parentcnid = kHFSRootParentID;
2345 cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename;
2346 cat_desc.cd_namelen = strlen(hfs_attrdatafilename);
2347 cat_desc.cd_cnid = kHFSAttributeDataFileID;
2348 /* Tag vnode as system file, note that we can still use cluster I/O */
2349 cat_desc.cd_flags |= CD_ISMETA;
2350
2351 bzero(&cat_attr, sizeof(cat_attr));
2352 cat_attr.ca_linkcount = 1;
2353 cat_attr.ca_mode = S_IFREG;
2354 cat_attr.ca_fileid = cat_desc.cd_cnid;
2355 cat_attr.ca_blocks = hfsmp->totalBlocks;
2356
2357 /*
2358 * The attribute data file is a virtual file that spans the
2359 * entire file system space.
2360 *
2361 * Each extent-based attribute occupies a unique portion of
2362 * in this virtual file. The cluster I/O is done using actual
2363 * allocation block offsets so no additional mapping is needed
2364 * for the VNOP_BLOCKMAP call.
2365 *
2366 * This approach allows the attribute data to be cached without
2367 * incurring the high cost of using a separate vnode per attribute.
2368 *
2369 * Since we need to acquire the attribute b-tree file lock anyways,
2370 * the virtual file doesn't introduce any additional serialization.
2371 */
2372 bzero(&cat_fork, sizeof(cat_fork));
2373 cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
2374 cat_fork.cf_blocks = hfsmp->totalBlocks;
2375 cat_fork.cf_extents[0].startBlock = 0;
2376 cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks;
2377
2378 result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr,
2379 &cat_fork, &vp, &newvnode_flags);
2380 if (result == 0) {
2381 hfsmp->hfs_attrdata_vp = vp;
2382 hfs_unlock(VTOC(vp));
2383 }
2384 return (result);
2385 }
2386
2387 /*
2388 * Read an extent based attribute.
2389 */
2390 static int
2391 read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2392 {
2393 vnode_t evp = hfsmp->hfs_attrdata_vp;
2394 int bufsize;
2395 int64_t iosize;
2396 int attrsize;
2397 int blksize;
2398 int i;
2399 int result = 0;
2400
2401 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2402
2403 bufsize = (int)uio_resid(uio);
2404 attrsize = (int)datasize;
2405 blksize = (int)hfsmp->blockSize;
2406
2407 /*
2408 * Read the attribute data one extent at a time.
2409 * For the typical case there is only one extent.
2410 */
2411 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2412 iosize = extents[i].blockCount * blksize;
2413 iosize = MIN(iosize, attrsize);
2414 iosize = MIN(iosize, bufsize);
2415 uio_setresid(uio, iosize);
2416 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2417
2418 result = cluster_read(evp, uio, VTOF(evp)->ff_size, IO_SYNC | IO_UNIT);
2419
2420 #if HFS_XATTR_VERBOSE
2421 printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n",
2422 iosize, extents[i].startBlock, extents[i].blockCount, result);
2423 #endif
2424 if (result)
2425 break;
2426 attrsize -= iosize;
2427 bufsize -= iosize;
2428 }
2429 uio_setresid(uio, bufsize);
2430 uio_setoffset(uio, datasize);
2431
2432 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
2433 return (result);
2434 }
2435
2436 /*
2437 * Write an extent based attribute.
2438 */
2439 __unused static int
2440 write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2441 {
2442 vnode_t evp = hfsmp->hfs_attrdata_vp;
2443 off_t filesize;
2444 int bufsize;
2445 int attrsize;
2446 int64_t iosize;
2447 int blksize;
2448 int i;
2449 int result = 0;
2450
2451 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2452
2453 bufsize = uio_resid(uio);
2454 attrsize = (int) datasize;
2455 blksize = (int) hfsmp->blockSize;
2456 filesize = VTOF(evp)->ff_size;
2457
2458 /*
2459 * Write the attribute data one extent at a time.
2460 */
2461 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2462 iosize = extents[i].blockCount * blksize;
2463 iosize = MIN(iosize, attrsize);
2464 iosize = MIN(iosize, bufsize);
2465 uio_setresid(uio, iosize);
2466 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2467
2468 result = cluster_write(evp, uio, filesize, filesize, filesize,
2469 (off_t) 0, IO_SYNC | IO_UNIT);
2470 #if HFS_XATTR_VERBOSE
2471 printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n",
2472 iosize, extents[i].startBlock, extents[i].blockCount, result);
2473 #endif
2474 if (result)
2475 break;
2476 attrsize -= iosize;
2477 bufsize -= iosize;
2478 }
2479 uio_setresid(uio, bufsize);
2480 uio_setoffset(uio, datasize);
2481
2482 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
2483 return (result);
2484 }
2485
2486 /*
2487 * Allocate blocks for an extent based attribute.
2488 */
2489 __unused static int
2490 alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks)
2491 {
2492 int blkcnt;
2493 int startblk;
2494 int lockflags;
2495 int i;
2496 int maxextents;
2497 int result = 0;
2498
2499 startblk = hfsmp->hfs_metazone_end;
2500 blkcnt = howmany(attrsize, hfsmp->blockSize);
2501 if (blkcnt > (int)hfs_freeblks(hfsmp, 0)) {
2502 return (ENOSPC);
2503 }
2504 *blocks = blkcnt;
2505 maxextents = extentbufsize / sizeof(HFSPlusExtentDescriptor);
2506
2507 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2508
2509 for (i = 0; (blkcnt > 0) && (i < maxextents); i++) {
2510 /* Try allocating and see if we find something decent */
2511 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0,
2512 &extents[i].startBlock, &extents[i].blockCount);
2513 /*
2514 * If we couldn't find anything, then re-try the allocation but allow
2515 * journal flushes.
2516 */
2517 if (result == dskFulErr) {
2518 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, HFS_ALLOC_FLUSHTXN,
2519 &extents[i].startBlock, &extents[i].blockCount);
2520 }
2521
2522
2523 #if HFS_XATTR_VERBOSE
2524 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2525 blkcnt, extents[i].startBlock, extents[i].blockCount, result);
2526 #endif
2527 if (result) {
2528 extents[i].startBlock = 0;
2529 extents[i].blockCount = 0;
2530 break;
2531 }
2532 blkcnt -= extents[i].blockCount;
2533 startblk = extents[i].startBlock + extents[i].blockCount;
2534 }
2535 /*
2536 * If it didn't fit in the extents buffer then bail.
2537 */
2538 if (blkcnt) {
2539 result = ENOSPC;
2540
2541 #if HFS_XATTR_VERBOSE
2542 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt);
2543 #endif
2544 for (; i >= 0; i--) {
2545 if ((blkcnt = extents[i].blockCount) != 0) {
2546 (void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt, 0);
2547 extents[i].startBlock = 0;
2548 extents[i].blockCount = 0;
2549 }
2550 }
2551 }
2552
2553 hfs_systemfile_unlock(hfsmp, lockflags);
2554 return MacToVFSError(result);
2555 }
2556
2557 /*
2558 * Release blocks from an extent based attribute.
2559 */
2560 static void
2561 free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents)
2562 {
2563 vnode_t evp = hfsmp->hfs_attrdata_vp;
2564 int remblks = blkcnt;
2565 int lockflags;
2566 int i;
2567
2568 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2569
2570 for (i = 0; (remblks > 0) && (extents[i].blockCount != 0); i++) {
2571 if (extents[i].blockCount > (u_int32_t)blkcnt) {
2572 #if HFS_XATTR_VERBOSE
2573 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
2574 extents[i].startBlock, extents[i].blockCount);
2575 #endif
2576 extents[i].blockCount = 0;
2577 continue;
2578 }
2579 if (extents[i].startBlock == 0) {
2580 break;
2581 }
2582 (void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount, 0);
2583 remblks -= extents[i].blockCount;
2584 extents[i].startBlock = 0;
2585 extents[i].blockCount = 0;
2586
2587 #if HFS_XATTR_VERBOSE
2588 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2589 extents[i].startBlock, extents[i].blockCount);
2590 #endif
2591 /* Discard any resident pages for this block range. */
2592 if (evp) {
2593 off_t start, end;
2594
2595 start = (u_int64_t)extents[i].startBlock * (u_int64_t)hfsmp->blockSize;
2596 end = start + (u_int64_t)extents[i].blockCount * (u_int64_t)hfsmp->blockSize;
2597 (void) ubc_msync(hfsmp->hfs_attrdata_vp, start, end, &start, UBC_INVALIDATE);
2598 }
2599 }
2600
2601 hfs_systemfile_unlock(hfsmp, lockflags);
2602 }
2603
2604 static int
2605 has_overflow_extents(HFSPlusForkData *forkdata)
2606 {
2607 u_int32_t blocks;
2608
2609 if (forkdata->extents[7].blockCount == 0)
2610 return (0);
2611
2612 blocks = forkdata->extents[0].blockCount +
2613 forkdata->extents[1].blockCount +
2614 forkdata->extents[2].blockCount +
2615 forkdata->extents[3].blockCount +
2616 forkdata->extents[4].blockCount +
2617 forkdata->extents[5].blockCount +
2618 forkdata->extents[6].blockCount +
2619 forkdata->extents[7].blockCount;
2620
2621 return (forkdata->totalBlocks > blocks);
2622 }
2623
2624 static int
2625 count_extent_blocks(int maxblks, HFSPlusExtentRecord extents)
2626 {
2627 int blocks;
2628 int i;
2629
2630 for (i = 0, blocks = 0; i < kHFSPlusExtentDensity; ++i) {
2631 /* Ignore obvious bogus extents. */
2632 if (extents[i].blockCount > (u_int32_t)maxblks)
2633 continue;
2634 if (extents[i].startBlock == 0 || extents[i].blockCount == 0)
2635 break;
2636 blocks += extents[i].blockCount;
2637 }
2638 return (blocks);
2639 }
2640