]> git.saurik.com Git - apple/hfs.git/blob - core/hfs_xattr.c
0d3dcd357bf86bf0c698e542df8f4d831a795626
[apple/hfs.git] / core / hfs_xattr.c
1 /*
2 * Copyright (c) 2004-2017 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 int blkcnt;
1084 int extentblks;
1085 u_int32_t *keystartblk;
1086 int i;
1087
1088 if (uio == NULL) {
1089 /*
1090 * setxattrs originating from in-kernel are not supported if they are bigger
1091 * than the inline max size. Just return ENOATTR and force them to do it with a
1092 * smaller EA.
1093 */
1094 result = EPERM;
1095 goto exit;
1096 }
1097
1098 /* Get some blocks. */
1099 blkcnt = howmany(attrsize, hfsmp->blockSize);
1100 extentbufsize = blkcnt * sizeof(HFSPlusExtentDescriptor);
1101 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
1102 extentptr = hfs_mallocz(extentbufsize);
1103 result = alloc_attr_blks(hfsmp, attrsize, extentbufsize, extentptr, &allocatedblks);
1104 if (result) {
1105 allocatedblks = 0;
1106 goto exit; /* no more space */
1107 }
1108 /* Copy data into the blocks. */
1109 result = write_attr_data(hfsmp, uio, attrsize, extentptr);
1110 if (result) {
1111 if (vp) {
1112 const char *name = vnode_getname(vp);
1113 printf("hfs_setxattr: write_attr_data vol=%s err (%d) %s:%s\n",
1114 hfsmp->vcbVN, result, name ? name : "", ap->a_name);
1115 if (name)
1116 vnode_putname(name);
1117 }
1118 goto exit;
1119 }
1120
1121 /* Now remove any previous attribute. */
1122 if (exists) {
1123 result = remove_attribute_records(hfsmp, iterator);
1124 if (result) {
1125 if (vp) {
1126 const char *name = vnode_getname(vp);
1127 printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n",
1128 hfsmp->vcbVN, result, name ? name : "", ap->a_name);
1129 if (name)
1130 vnode_putname(name);
1131 }
1132 goto exit;
1133 }
1134 }
1135 /* Create attribute fork data record. */
1136 recp = hfs_malloc(recp_size = sizeof(HFSPlusAttrRecord));
1137
1138 btdata.bufferAddress = recp;
1139 btdata.itemCount = 1;
1140 btdata.itemSize = sizeof(HFSPlusAttrForkData);
1141
1142 recp->recordType = kHFSPlusAttrForkData;
1143 recp->forkData.reserved = 0;
1144 recp->forkData.theFork.logicalSize = attrsize;
1145 recp->forkData.theFork.clumpSize = 0;
1146 recp->forkData.theFork.totalBlocks = blkcnt;
1147 bcopy(extentptr, recp->forkData.theFork.extents, sizeof(HFSPlusExtentRecord));
1148
1149 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1150
1151 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1152 if (result) {
1153 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
1154 hfsmp->vcbVN, target_id, ap->a_name, result);
1155 goto exit;
1156 }
1157 extentblks = count_extent_blocks(blkcnt, recp->forkData.theFork.extents);
1158 blkcnt -= extentblks;
1159 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1160 i = 0;
1161
1162 /* Create overflow extents as needed. */
1163 while (blkcnt > 0) {
1164 /* Initialize the key and record. */
1165 *keystartblk += (u_int32_t)extentblks;
1166 btdata.itemSize = sizeof(HFSPlusAttrExtents);
1167 recp->recordType = kHFSPlusAttrExtents;
1168 recp->overflowExtents.reserved = 0;
1169
1170 /* Copy the next set of extents. */
1171 i += kHFSPlusExtentDensity;
1172 bcopy(&extentptr[i], recp->overflowExtents.extents, sizeof(HFSPlusExtentRecord));
1173
1174 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1175 if (result) {
1176 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
1177 hfsmp->vcbVN, target_id, ap->a_name, result);
1178 goto exit;
1179 }
1180 extentblks = count_extent_blocks(blkcnt, recp->overflowExtents.extents);
1181 blkcnt -= extentblks;
1182 }
1183 } else { /* Inline data */
1184 if (exists) {
1185 result = remove_attribute_records(hfsmp, iterator);
1186 if (result) {
1187 goto exit;
1188 }
1189 }
1190
1191 /* Calculate size of record rounded up to multiple of 2 bytes. */
1192 btdata.itemSize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0);
1193 recp = hfs_malloc(recp_size = btdata.itemSize);
1194
1195 recp->recordType = kHFSPlusAttrInlineData;
1196 recp->attrData.reserved[0] = 0;
1197 recp->attrData.reserved[1] = 0;
1198 recp->attrData.attrSize = attrsize;
1199
1200 /* Copy in the attribute data (if any). */
1201 if (attrsize > 0) {
1202 if (data_ptr) {
1203 bcopy(data_ptr, &recp->attrData.attrData, attrsize);
1204 } else {
1205 /*
1206 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
1207 * then deny the copy operation.
1208 */
1209 if (uio == NULL) {
1210 result = EPERM;
1211 goto exit;
1212 }
1213 result = uiomove((caddr_t)&recp->attrData.attrData, attrsize, uio);
1214 }
1215
1216 if (result) {
1217 goto exit;
1218 }
1219 }
1220
1221 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1222
1223 btdata.bufferAddress = recp;
1224 btdata.itemCount = 1;
1225 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
1226 }
1227
1228 exit:
1229 if (btfile && started_transaction) {
1230 (void) BTFlushPath(btfile);
1231 }
1232 hfs_systemfile_unlock(hfsmp, lockflags);
1233 if (result == 0) {
1234 if (vp) {
1235 cp = VTOC(vp);
1236 /* Setting an attribute only updates change time and not
1237 * modified time of the file.
1238 */
1239 cp->c_touch_chgtime = TRUE;
1240 cp->c_flag |= C_MODIFIED;
1241 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
1242 if ((strcmp(ap->a_name, KAUTH_FILESEC_XATTR) == 0)) {
1243 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
1244 }
1245 (void) hfs_update(vp, 0);
1246 }
1247 }
1248 if (started_transaction) {
1249 if (result && allocatedblks) {
1250 free_attr_blks(hfsmp, allocatedblks, extentptr);
1251 }
1252 hfs_end_transaction(hfsmp);
1253 }
1254
1255 hfs_free(recp, recp_size);
1256 hfs_free(extentptr, extentbufsize);
1257 hfs_free(iterator, sizeof(*iterator));
1258
1259 return result;
1260 }
1261
1262
1263
1264
1265 /*
1266 * Remove an extended attribute.
1267 */
1268 int
1269 hfs_vnop_removexattr(struct vnop_removexattr_args *ap)
1270 /*
1271 struct vnop_removexattr_args {
1272 struct vnodeop_desc *a_desc;
1273 vnode_t a_vp;
1274 char * a_name;
1275 int a_options;
1276 vfs_context_t a_context;
1277 };
1278 */
1279 {
1280 struct vnode *vp = ap->a_vp;
1281 struct cnode *cp = VTOC(vp);
1282 struct hfsmount *hfsmp;
1283 struct BTreeIterator * iterator = NULL;
1284 int lockflags;
1285 int result;
1286 time_t orig_ctime=VTOC(vp)->c_ctime;
1287
1288 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
1289 return (EINVAL); /* invalid name */
1290 }
1291 hfsmp = VTOHFS(vp);
1292 if (VNODE_IS_RSRC(vp)) {
1293 return (EPERM);
1294 }
1295
1296 #if HFS_COMPRESSION
1297 if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) && !(ap->a_options & XATTR_SHOWCOMPRESSION)) {
1298 return ENOATTR;
1299 }
1300 #endif /* HFS_COMPRESSION */
1301
1302 nspace_snapshot_event(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_DELETE_OP, NSPACE_REARM_NO_ARG);
1303
1304 /* If Resource Fork is non-empty then truncate it. */
1305 if (strcmp(ap->a_name, XATTR_RESOURCEFORK_NAME) == 0) {
1306 struct vnode *rvp = NULL;
1307
1308 if ( !vnode_isreg(vp) ) {
1309 return (EPERM);
1310 }
1311 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1312 return (result);
1313 }
1314 if (!hfs_has_rsrc(cp)) {
1315 hfs_unlock(cp);
1316 return (ENOATTR);
1317 }
1318 result = hfs_vgetrsrc(hfsmp, vp, &rvp);
1319 hfs_unlock(cp);
1320 if (result) {
1321 return (result);
1322 }
1323
1324 hfs_lock_truncate(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
1325
1326 // Tell UBC now before we take the cnode lock and start the transaction
1327 hfs_ubc_setsize(rvp, 0, false);
1328
1329 if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1330 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1331 vnode_put(rvp);
1332 return (result);
1333 }
1334
1335 /* Start a transaction for encapsulating changes in
1336 * hfs_truncate() and hfs_update()
1337 */
1338 if ((result = hfs_start_transaction(hfsmp))) {
1339 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1340 hfs_unlock(cp);
1341 vnode_put(rvp);
1342 return (result);
1343 }
1344
1345 result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context);
1346 if (result == 0) {
1347 cp->c_touch_chgtime = TRUE;
1348 cp->c_flag |= C_MODIFIED;
1349 result = hfs_update(vp, 0);
1350 }
1351
1352 hfs_end_transaction(hfsmp);
1353 hfs_unlock_truncate(VTOC(rvp), HFS_LOCK_DEFAULT);
1354 hfs_unlock(VTOC(rvp));
1355
1356 vnode_put(rvp);
1357 return (result);
1358 }
1359 /* Clear out the Finder Info. */
1360 if (strcmp(ap->a_name, XATTR_FINDERINFO_NAME) == 0) {
1361 void * finderinfo_start;
1362 int finderinfo_size;
1363 u_int8_t finderinfo[32];
1364 u_int32_t date_added = 0, write_gen_counter = 0, document_id = 0;
1365 u_int8_t *finfo = NULL;
1366
1367 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1368 return (result);
1369 }
1370
1371 /* Use the local copy to store our temporary changes. */
1372 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
1373
1374
1375 /* Zero out the date added field in the local copy */
1376 hfs_zero_hidden_fields (cp, finderinfo);
1377
1378 /* Don't expose a symlink's private type/creator. */
1379 if (vnode_islnk(vp)) {
1380 struct FndrFileInfo *fip;
1381
1382 fip = (struct FndrFileInfo *)&finderinfo;
1383 fip->fdType = 0;
1384 fip->fdCreator = 0;
1385 }
1386
1387 /* Do the byte compare against the local copy */
1388 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
1389 hfs_unlock(cp);
1390 return (ENOATTR);
1391 }
1392
1393 /*
1394 * If there was other content, zero out everything except
1395 * type/creator and date added. First, save the date added.
1396 */
1397 finfo = cp->c_finderinfo;
1398 finfo = finfo + 16;
1399 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
1400 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
1401 date_added = extinfo->date_added;
1402 write_gen_counter = extinfo->write_gen_counter;
1403 document_id = extinfo->document_id;
1404 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
1405 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
1406 date_added = extinfo->date_added;
1407 write_gen_counter = extinfo->write_gen_counter;
1408 document_id = extinfo->document_id;
1409 }
1410
1411 if (vnode_islnk(vp)) {
1412 /* Ignore type/creator */
1413 finderinfo_start = &cp->c_finderinfo[8];
1414 finderinfo_size = sizeof(cp->c_finderinfo) - 8;
1415 } else {
1416 finderinfo_start = &cp->c_finderinfo[0];
1417 finderinfo_size = sizeof(cp->c_finderinfo);
1418 }
1419 bzero(finderinfo_start, finderinfo_size);
1420
1421
1422 /* Now restore the date added */
1423 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
1424 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
1425 extinfo->date_added = date_added;
1426 extinfo->write_gen_counter = write_gen_counter;
1427 extinfo->document_id = document_id;
1428 } else if (S_ISDIR(cp->c_attr.ca_mode)) {
1429 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
1430 extinfo->date_added = date_added;
1431 extinfo->write_gen_counter = write_gen_counter;
1432 extinfo->document_id = document_id;
1433 }
1434
1435 /* Updating finderInfo updates change time and modified time */
1436 cp->c_touch_chgtime = TRUE;
1437 cp->c_flag |= C_MODIFIED;
1438 hfs_update(vp, 0);
1439
1440 hfs_unlock(cp);
1441
1442 return (0);
1443 }
1444 #if CONFIG_HFS_STD
1445 /*
1446 * Standard HFS only supports native FinderInfo and Resource Forks.
1447 */
1448 if (hfsmp->hfs_flags & HFS_STANDARD) {
1449 return (EPERM);
1450 }
1451 #endif
1452 if (hfsmp->hfs_attribute_vp == NULL) {
1453 return (ENOATTR);
1454 }
1455
1456 iterator = hfs_mallocz(sizeof(*iterator));
1457
1458 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1459 goto exit_nolock;
1460 }
1461
1462 result = hfs_buildattrkey(cp->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1463 if (result) {
1464 goto exit;
1465 }
1466
1467 if (hfs_start_transaction(hfsmp) != 0) {
1468 result = EINVAL;
1469 goto exit;
1470 }
1471 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1472
1473 result = remove_attribute_records(hfsmp, iterator);
1474
1475 hfs_systemfile_unlock(hfsmp, lockflags);
1476
1477 if (result == 0) {
1478 cp->c_touch_chgtime = TRUE;
1479
1480 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1481
1482 /* If no more attributes exist, clear attribute bit */
1483 result = file_attribute_exist(hfsmp, cp->c_fileid);
1484 if (result == 0) {
1485 cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask;
1486 cp->c_flag |= C_MODIFIED;
1487 }
1488 if (result == EEXIST) {
1489 result = 0;
1490 }
1491
1492 hfs_systemfile_unlock(hfsmp, lockflags);
1493
1494 /* If ACL was removed, clear security bit */
1495 if (strcmp(ap->a_name, KAUTH_FILESEC_XATTR) == 0) {
1496 cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
1497 cp->c_flag |= C_MODIFIED;
1498 }
1499 (void) hfs_update(vp, 0);
1500 }
1501
1502 hfs_end_transaction(hfsmp);
1503 exit:
1504 hfs_unlock(cp);
1505 exit_nolock:
1506 hfs_free(iterator, sizeof(*iterator));
1507 return MacToVFSError(result);
1508 }
1509
1510 /*
1511 * Removes a non rsrc-fork, non-finderinfo EA from the specified file ID.
1512 * Note that this results in a bit of code duplication for the xattr removal
1513 * path. This is done because it's a bit messy to deal with things without the
1514 * cnode. This function is used by exchangedata to port XATTRS to alternate
1515 * fileIDs while everything is locked, and the cnodes are in a transitional state.
1516 *
1517 * Assumes that the cnode backing the fileid specified is LOCKED.
1518 */
1519
1520 int
1521 hfs_removexattr_by_id (struct hfsmount *hfsmp, uint32_t fileid, const char *xattr_name ) {
1522 struct BTreeIterator iter; // allocated on the stack to avoid heap allocation mid-txn
1523 int ret = 0;
1524 int started_txn = 0;
1525 int lockflags;
1526
1527 memset (&iter, 0, sizeof(iter));
1528
1529 //position the B-Tree iter key before grabbing locks and starting a txn
1530 ret = hfs_buildattrkey (fileid, xattr_name, (HFSPlusAttrKey*)&iter.key);
1531 if (ret) {
1532 goto xattr_out;
1533 }
1534
1535 //note: this is likely a nested transaction since there is a global transaction cover
1536 if (hfs_start_transaction (hfsmp) != 0) {
1537 ret = EINVAL;
1538 goto xattr_out;
1539 }
1540 started_txn = 1;
1541
1542
1543 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1544
1545 //actually remove the EA from the tree
1546 ret = remove_attribute_records(hfsmp, &iter);
1547
1548 hfs_systemfile_unlock(hfsmp, lockflags);
1549
1550 /*
1551 * NOTE: Responsibility of the caller to remove the "has XATTRs" bit in the catalog record
1552 * if this was the last EA.
1553 */
1554
1555
1556 xattr_out:
1557 if (started_txn) {
1558 hfs_end_transaction(hfsmp);
1559 }
1560
1561 return MacToVFSError(ret);
1562
1563 }
1564
1565
1566 /* Check if any attribute record exist for given fileID. This function
1567 * is called by hfs_vnop_removexattr to determine if it should clear the
1568 * attribute bit in the catalog record or not.
1569 *
1570 * Note - you must acquire a shared lock on the attribute btree before
1571 * calling this function.
1572 *
1573 * Output:
1574 * EEXIST - If attribute record was found
1575 * 0 - Attribute was not found
1576 * (other) - Other error (such as EIO)
1577 */
1578 int
1579 file_attribute_exist(struct hfsmount *hfsmp, uint32_t fileID)
1580 {
1581 HFSPlusAttrKey *key;
1582 struct BTreeIterator * iterator = NULL;
1583 struct filefork *btfile;
1584 int result = 0;
1585
1586 // if there's no attribute b-tree we sure as heck
1587 // can't have any attributes!
1588 if (hfsmp->hfs_attribute_vp == NULL) {
1589 return false;
1590 }
1591
1592 iterator = hfs_mallocz(sizeof(*iterator));
1593
1594 key = (HFSPlusAttrKey *)&iterator->key;
1595
1596 result = hfs_buildattrkey(fileID, NULL, key);
1597 if (result) {
1598 goto out;
1599 }
1600
1601 btfile = VTOF(hfsmp->hfs_attribute_vp);
1602 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1603 if (result && (result != btNotFound)) {
1604 goto out;
1605 }
1606
1607 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1608 /* If no next record was found or fileID for next record did not match,
1609 * no more attributes exist for this fileID
1610 */
1611 if ((result && (result == btNotFound)) || (key->fileID != fileID)) {
1612 result = 0;
1613 } else {
1614 result = EEXIST;
1615 }
1616
1617 out:
1618 hfs_free(iterator, sizeof(*iterator));
1619 return result;
1620 }
1621
1622
1623 /*
1624 * Remove all the records for a given attribute.
1625 *
1626 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1627 * - A transaction must have been started.
1628 * - The Attribute b-tree file must be locked exclusive.
1629 * - The Allocation Bitmap file must be locked exclusive.
1630 * - The iterator key must be initialized.
1631 */
1632 int
1633 remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator)
1634 {
1635 struct filefork *btfile;
1636 FSBufferDescriptor btdata;
1637 HFSPlusAttrRecord attrdata; /* 90 bytes */
1638 u_int16_t datasize;
1639 int result;
1640
1641 btfile = VTOF(hfsmp->hfs_attribute_vp);
1642
1643 btdata.bufferAddress = &attrdata;
1644 btdata.itemSize = sizeof(attrdata);
1645 btdata.itemCount = 1;
1646 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1647 if (result) {
1648 goto exit; /* no records. */
1649 }
1650 /*
1651 * Free the blocks from extent based attributes.
1652 *
1653 * Note that the block references (btree records) are removed
1654 * before releasing the blocks in the allocation bitmap.
1655 */
1656 if (attrdata.recordType == kHFSPlusAttrForkData) {
1657 int totalblks;
1658 int extentblks;
1659 u_int32_t *keystartblk;
1660
1661 if (datasize < sizeof(HFSPlusAttrForkData)) {
1662 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize, sizeof(HFSPlusAttrForkData));
1663 }
1664 totalblks = attrdata.forkData.theFork.totalBlocks;
1665
1666 /* Process the first 8 extents. */
1667 extentblks = count_extent_blocks(totalblks, attrdata.forkData.theFork.extents);
1668 if (extentblks > totalblks)
1669 panic("hfs: remove_attribute_records: corruption...");
1670 if (BTDeleteRecord(btfile, iterator) == 0) {
1671 free_attr_blks(hfsmp, extentblks, attrdata.forkData.theFork.extents);
1672 }
1673 totalblks -= extentblks;
1674 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1675
1676 /* Process any overflow extents. */
1677 while (totalblks) {
1678 *keystartblk += (u_int32_t)extentblks;
1679
1680 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1681 if (result ||
1682 (attrdata.recordType != kHFSPlusAttrExtents) ||
1683 (datasize < sizeof(HFSPlusAttrExtents))) {
1684 printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n",
1685 hfsmp->vcbVN, MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks);
1686 result = ENOATTR;
1687 break; /* break from while */
1688 }
1689 /* Process the next 8 extents. */
1690 extentblks = count_extent_blocks(totalblks, attrdata.overflowExtents.extents);
1691 if (extentblks > totalblks)
1692 panic("hfs: remove_attribute_records: corruption...");
1693 if (BTDeleteRecord(btfile, iterator) == 0) {
1694 free_attr_blks(hfsmp, extentblks, attrdata.overflowExtents.extents);
1695 }
1696 totalblks -= extentblks;
1697 }
1698 } else {
1699 result = BTDeleteRecord(btfile, iterator);
1700 }
1701 (void) BTFlushPath(btfile);
1702 exit:
1703 return (result == btNotFound ? ENOATTR : MacToVFSError(result));
1704 }
1705
1706
1707 /*
1708 * Retrieve the list of extended attribute names.
1709 */
1710 int
1711 hfs_vnop_listxattr(struct vnop_listxattr_args *ap)
1712 /*
1713 struct vnop_listxattr_args {
1714 struct vnodeop_desc *a_desc;
1715 vnode_t a_vp;
1716 uio_t a_uio;
1717 size_t *a_size;
1718 int a_options;
1719 vfs_context_t a_context;
1720 */
1721 {
1722 struct vnode *vp = ap->a_vp;
1723 struct cnode *cp = VTOC(vp);
1724 struct hfsmount *hfsmp;
1725 uio_t uio = ap->a_uio;
1726 struct BTreeIterator * iterator = NULL;
1727 struct filefork *btfile;
1728 struct listattr_callback_state state;
1729 user_addr_t user_start = 0;
1730 user_size_t user_len = 0;
1731 int lockflags;
1732 int result;
1733 u_int8_t finderinfo[32];
1734
1735
1736 if (VNODE_IS_RSRC(vp)) {
1737 return (EPERM);
1738 }
1739
1740 #if HFS_COMPRESSION
1741 int compressed = hfs_file_is_compressed(cp, 1); /* 1 == don't take the cnode lock */
1742 #endif /* HFS_COMPRESSION */
1743
1744 hfsmp = VTOHFS(vp);
1745 *ap->a_size = 0;
1746
1747 /*
1748 * Take the truncate lock; this serializes us against the ioctl
1749 * to truncate data & reset the decmpfs state
1750 * in the compressed file handler.
1751 */
1752 hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
1753
1754 /* Now the regular cnode lock (shared) */
1755 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
1756 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1757 return (result);
1758 }
1759
1760 /*
1761 * Make a copy of the cnode's finderinfo to a local so we can
1762 * zero out the date added field. Also zero out the private type/creator
1763 * for symlinks.
1764 */
1765 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
1766 hfs_zero_hidden_fields (cp, finderinfo);
1767
1768 /* Don't expose a symlink's private type/creator. */
1769 if (vnode_islnk(vp)) {
1770 struct FndrFileInfo *fip;
1771
1772 fip = (struct FndrFileInfo *)&finderinfo;
1773 fip->fdType = 0;
1774 fip->fdCreator = 0;
1775 }
1776
1777
1778 /* If Finder Info is non-empty then export it's name. */
1779 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) {
1780 if (uio == NULL) {
1781 *ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
1782 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
1783 result = ERANGE;
1784 goto exit;
1785 } else {
1786 result = uiomove(XATTR_FINDERINFO_NAME,
1787 sizeof(XATTR_FINDERINFO_NAME), uio);
1788 if (result)
1789 goto exit;
1790 }
1791 }
1792 /* If Resource Fork is non-empty then export it's name. */
1793 if (S_ISREG(cp->c_mode) && hfs_has_rsrc(cp)) {
1794 #if HFS_COMPRESSION
1795 if ((ap->a_options & XATTR_SHOWCOMPRESSION) ||
1796 !compressed ||
1797 !decmpfs_hides_rsrc(ap->a_context, VTOCMP(vp))
1798 )
1799 #endif /* HFS_COMPRESSION */
1800 {
1801 if (uio == NULL) {
1802 *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
1803 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
1804 result = ERANGE;
1805 goto exit;
1806 } else {
1807 result = uiomove(XATTR_RESOURCEFORK_NAME,
1808 sizeof(XATTR_RESOURCEFORK_NAME), uio);
1809 if (result)
1810 goto exit;
1811 }
1812 }
1813 }
1814 #if CONFIG_HFS_STD
1815 /*
1816 * Standard HFS only supports native FinderInfo and Resource Forks.
1817 * Return at this point.
1818 */
1819 if (hfsmp->hfs_flags & HFS_STANDARD) {
1820 result = 0;
1821 goto exit;
1822 }
1823 #endif
1824 /* Bail if we don't have any extended attributes. */
1825 if ((hfsmp->hfs_attribute_vp == NULL) ||
1826 (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
1827 result = 0;
1828 goto exit;
1829 }
1830 btfile = VTOF(hfsmp->hfs_attribute_vp);
1831
1832 iterator = hfs_mallocz(sizeof(*iterator));
1833
1834 result = hfs_buildattrkey(cp->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key);
1835 if (result)
1836 goto exit;
1837
1838 /*
1839 * Lock the user's buffer here so that we won't fault on
1840 * it in uiomove while holding the attributes b-tree lock.
1841 */
1842 if (uio && uio_isuserspace(uio)) {
1843 user_start = uio_curriovbase(uio);
1844 user_len = uio_curriovlen(uio);
1845
1846 if ((result = vslock(user_start, user_len)) != 0) {
1847 user_start = 0;
1848 goto exit;
1849 }
1850 }
1851 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1852
1853 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1854 if (result && result != btNotFound) {
1855 hfs_systemfile_unlock(hfsmp, lockflags);
1856 goto exit;
1857 }
1858
1859 state.fileID = cp->c_fileid;
1860 state.result = 0;
1861 state.uio = uio;
1862 state.size = 0;
1863 #if HFS_COMPRESSION
1864 state.showcompressed = !compressed || ap->a_options & XATTR_SHOWCOMPRESSION;
1865 state.ctx = ap->a_context;
1866 state.vp = vp;
1867 #endif /* HFS_COMPRESSION */
1868
1869 /*
1870 * Process entries starting just after iterator->key.
1871 */
1872 result = BTIterateRecords(btfile, kBTreeNextRecord, iterator,
1873 (IterateCallBackProcPtr)listattr_callback, &state);
1874 hfs_systemfile_unlock(hfsmp, lockflags);
1875 if (uio == NULL) {
1876 *ap->a_size += state.size;
1877 }
1878
1879 if (state.result || result == btNotFound)
1880 result = state.result;
1881
1882 exit:
1883 if (user_start) {
1884 vsunlock(user_start, user_len, TRUE);
1885 }
1886 hfs_free(iterator, sizeof(*iterator));
1887 hfs_unlock(cp);
1888 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1889
1890 return MacToVFSError(result);
1891 }
1892
1893
1894 /*
1895 * Callback - called for each attribute record
1896 */
1897 static int
1898 listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state)
1899 {
1900 char attrname[XATTR_MAXNAMELEN + 1];
1901 ssize_t bytecount;
1902 int result;
1903
1904 if (state->fileID != key->fileID) {
1905 state->result = 0;
1906 return (0); /* stop */
1907 }
1908 /*
1909 * Skip over non-primary keys
1910 */
1911 if (key->startBlock != 0) {
1912 return (1); /* continue */
1913 }
1914
1915 /* Convert the attribute name into UTF-8. */
1916 result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar),
1917 (u_int8_t *)attrname, (size_t *)&bytecount, sizeof(attrname), '/', 0);
1918 if (result) {
1919 state->result = result;
1920 return (0); /* stop */
1921 }
1922 bytecount++; /* account for null termination char */
1923
1924 if (xattr_protected(attrname))
1925 return (1); /* continue */
1926
1927 #if HFS_COMPRESSION
1928 if (!state->showcompressed && decmpfs_hides_xattr(state->ctx, VTOCMP(state->vp), attrname) )
1929 return 1; /* continue */
1930 #endif /* HFS_COMPRESSION */
1931
1932 if (state->uio == NULL) {
1933 state->size += bytecount;
1934 } else {
1935 if (bytecount > uio_resid(state->uio)) {
1936 state->result = ERANGE;
1937 return (0); /* stop */
1938 }
1939 result = uiomove((caddr_t) attrname, bytecount, state->uio);
1940 if (result) {
1941 state->result = result;
1942 return (0); /* stop */
1943 }
1944 }
1945 return (1); /* continue */
1946 }
1947
1948 /*
1949 * Remove all the attributes from a cnode.
1950 *
1951 * This function creates/ends its own transaction so that each
1952 * attribute is deleted in its own transaction (to avoid having
1953 * a transaction grow too large).
1954 *
1955 * This function takes the necessary locks on the attribute
1956 * b-tree file and the allocation (bitmap) file.
1957 *
1958 * NOTE: Upon sucecss, this function will return with an open
1959 * transaction. The reason we do it this way is because when we
1960 * delete the last attribute, we must make sure the flag in the
1961 * catalog record that indicates there are no more records is cleared.
1962 * The caller is responsible for doing this and *must* do it before
1963 * ending the transaction.
1964 */
1965 int
1966 hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid,
1967 bool *open_transaction)
1968 {
1969 BTreeIterator *iterator = NULL;
1970 HFSPlusAttrKey *key;
1971 struct filefork *btfile;
1972 int result, lockflags = 0;
1973
1974 *open_transaction = false;
1975
1976 if (hfsmp->hfs_attribute_vp == NULL)
1977 return 0;
1978
1979 btfile = VTOF(hfsmp->hfs_attribute_vp);
1980
1981 iterator = hfs_mallocz(sizeof(BTreeIterator));
1982
1983 key = (HFSPlusAttrKey *)&iterator->key;
1984
1985 /* Loop until there are no more attributes for this file id */
1986 do {
1987 if (!*open_transaction)
1988 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1989
1990 (void) hfs_buildattrkey(fileid, NULL, key);
1991 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1992 if (result || key->fileID != fileid)
1993 goto exit;
1994
1995 hfs_systemfile_unlock(hfsmp, lockflags);
1996 lockflags = 0;
1997
1998 if (*open_transaction) {
1999 hfs_end_transaction(hfsmp);
2000 *open_transaction = false;
2001 }
2002
2003 if (hfs_start_transaction(hfsmp) != 0) {
2004 result = EINVAL;
2005 goto exit;
2006 }
2007
2008 *open_transaction = true;
2009
2010 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2011
2012 result = remove_attribute_records(hfsmp, iterator);
2013
2014 #if HFS_XATTR_VERBOSE
2015 if (result) {
2016 printf("hfs_removeallattr: unexpected err %d\n", result);
2017 }
2018 #endif
2019 } while (!result);
2020
2021 exit:
2022 hfs_free(iterator, sizeof(*iterator));
2023
2024 if (lockflags)
2025 hfs_systemfile_unlock(hfsmp, lockflags);
2026
2027 result = result == btNotFound ? 0 : MacToVFSError(result);
2028
2029 if (result && *open_transaction) {
2030 hfs_end_transaction(hfsmp);
2031 *open_transaction = false;
2032 }
2033
2034 return result;
2035 }
2036
2037 void
2038 hfs_xattr_init(struct hfsmount * hfsmp)
2039 {
2040 #if CONFIG_HFS_STD
2041 if (ISSET(hfsmp->hfs_flags, HFS_STANDARD))
2042 return;
2043 #endif
2044
2045 /*
2046 * If there isn't an attributes b-tree then create one.
2047 */
2048 if ((hfsmp->hfs_attribute_vp == NULL) &&
2049 !(hfsmp->hfs_flags & HFS_READ_ONLY)) {
2050 (void) hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
2051 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
2052 }
2053 if (hfsmp->hfs_attribute_vp)
2054 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
2055 }
2056
2057 /*
2058 * Enable/Disable volume attributes stored as EA for root file system.
2059 * Supported attributes are -
2060 * 1. Extent-based Extended Attributes
2061 */
2062 int
2063 hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state)
2064 {
2065 struct BTreeIterator * iterator = NULL;
2066 struct filefork *btfile;
2067 int lockflags;
2068 int result;
2069
2070 #if CONFIG_HFS_STD
2071 if (hfsmp->hfs_flags & HFS_STANDARD) {
2072 return (ENOTSUP);
2073 }
2074 #endif
2075 if (xattrtype != HFSIOC_SET_XATTREXTENTS_STATE) {
2076 return EINVAL;
2077 }
2078
2079 /*
2080 * If there isn't an attributes b-tree then create one.
2081 */
2082 if (hfsmp->hfs_attribute_vp == NULL) {
2083 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
2084 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
2085 if (result) {
2086 return (result);
2087 }
2088 }
2089
2090 iterator = hfs_mallocz(sizeof(*iterator));
2091
2092 /*
2093 * Build a b-tree key.
2094 * We use the root's parent id (1) to hold this volume attribute.
2095 */
2096 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
2097 (HFSPlusAttrKey *)&iterator->key);
2098
2099 /* Start a transaction for our changes. */
2100 if (hfs_start_transaction(hfsmp) != 0) {
2101 result = EINVAL;
2102 goto exit;
2103 }
2104 btfile = VTOF(hfsmp->hfs_attribute_vp);
2105
2106 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2107
2108 if (state == 0) {
2109 /* Remove the attribute. */
2110 result = BTDeleteRecord(btfile, iterator);
2111 if (result == btNotFound)
2112 result = 0;
2113 } else {
2114 FSBufferDescriptor btdata;
2115 HFSPlusAttrData attrdata;
2116 u_int16_t datasize;
2117
2118 datasize = sizeof(attrdata);
2119 btdata.bufferAddress = &attrdata;
2120 btdata.itemSize = datasize;
2121 btdata.itemCount = 1;
2122 attrdata.recordType = kHFSPlusAttrInlineData;
2123 attrdata.reserved[0] = 0;
2124 attrdata.reserved[1] = 0;
2125 attrdata.attrSize = 2;
2126 attrdata.attrData[0] = 0;
2127 attrdata.attrData[1] = 0;
2128
2129 /* Insert the attribute. */
2130 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
2131 if (result == btExists)
2132 result = 0;
2133 }
2134 (void) BTFlushPath(btfile);
2135
2136 hfs_systemfile_unlock(hfsmp, lockflags);
2137
2138 /* Finish the transaction of our changes. */
2139 hfs_end_transaction(hfsmp);
2140
2141 /* Update the state in the mount point */
2142 hfs_lock_mount (hfsmp);
2143 if (state == 0) {
2144 hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS;
2145 } else {
2146 hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
2147 }
2148 hfs_unlock_mount (hfsmp);
2149
2150 exit:
2151 hfs_free(iterator, sizeof(*iterator));
2152 return MacToVFSError(result);
2153 }
2154
2155
2156 /*
2157 * hfs_attrkeycompare - compare two attribute b-tree keys.
2158 *
2159 * The name portion of the key is compared using a 16-bit binary comparison.
2160 * This is called from the b-tree code.
2161 */
2162 int
2163 hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey)
2164 {
2165 u_int32_t searchFileID, trialFileID;
2166 int result;
2167
2168 searchFileID = searchKey->fileID;
2169 trialFileID = trialKey->fileID;
2170 result = 0;
2171
2172 if (searchFileID > trialFileID) {
2173 ++result;
2174 } else if (searchFileID < trialFileID) {
2175 --result;
2176 } else {
2177 u_int16_t * str1 = &searchKey->attrName[0];
2178 u_int16_t * str2 = &trialKey->attrName[0];
2179 int length1 = searchKey->attrNameLen;
2180 int length2 = trialKey->attrNameLen;
2181 u_int16_t c1, c2;
2182 int length;
2183
2184 if (length1 < length2) {
2185 length = length1;
2186 --result;
2187 } else if (length1 > length2) {
2188 length = length2;
2189 ++result;
2190 } else {
2191 length = length1;
2192 }
2193
2194 while (length--) {
2195 c1 = *(str1++);
2196 c2 = *(str2++);
2197
2198 if (c1 > c2) {
2199 result = 1;
2200 break;
2201 }
2202 if (c1 < c2) {
2203 result = -1;
2204 break;
2205 }
2206 }
2207 if (result)
2208 return (result);
2209 /*
2210 * Names are equal; compare startBlock
2211 */
2212 if (searchKey->startBlock == trialKey->startBlock) {
2213 return (0);
2214 } else {
2215 return (searchKey->startBlock < trialKey->startBlock ? -1 : 1);
2216 }
2217 }
2218
2219 return result;
2220 }
2221
2222
2223 /*
2224 * hfs_buildattrkey - build an Attribute b-tree key
2225 */
2226 int
2227 hfs_buildattrkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key)
2228 {
2229 int result = 0;
2230 size_t unicodeBytes = 0;
2231
2232 if (attrname != NULL) {
2233 /*
2234 * Convert filename from UTF-8 into Unicode
2235 */
2236 result = utf8_decodestr((const u_int8_t *)attrname, strlen(attrname), key->attrName,
2237 &unicodeBytes, sizeof(key->attrName), 0, 0);
2238 if (result) {
2239 if (result != ENAMETOOLONG)
2240 result = EINVAL; /* name has invalid characters */
2241 return (result);
2242 }
2243 key->attrNameLen = unicodeBytes / sizeof(UniChar);
2244 key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes;
2245 } else {
2246 key->attrNameLen = 0;
2247 key->keyLength = kHFSPlusAttrKeyMinimumLength;
2248 }
2249 key->pad = 0;
2250 key->fileID = fileID;
2251 key->startBlock = 0;
2252
2253 return (0);
2254 }
2255
2256 /*
2257 * getnodecount - calculate starting node count for attributes b-tree.
2258 */
2259 static int
2260 getnodecount(struct hfsmount *hfsmp, size_t nodesize)
2261 {
2262 u_int64_t freebytes;
2263 u_int64_t calcbytes;
2264
2265 /*
2266 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
2267 * 10.5: Attempt to be as big as the catalog clump size.
2268 *
2269 * Use no more than 10 % of the remaining free space.
2270 */
2271 freebytes = (u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize;
2272
2273 calcbytes = MIN(hfsmp->hfs_catalog_cp->c_datafork->ff_size / 5, 20 * 1024 * 1024);
2274
2275 calcbytes = MAX(calcbytes, hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize);
2276
2277 calcbytes = MIN(calcbytes, freebytes / 10);
2278
2279 return (MAX(2, (int)(calcbytes / nodesize)));
2280 }
2281
2282
2283 /*
2284 * getmaxinlineattrsize - calculate maximum inline attribute size.
2285 *
2286 * This yields 3,802 bytes for an 8K node size.
2287 */
2288 static size_t
2289 getmaxinlineattrsize(struct vnode * attrvp)
2290 {
2291 struct BTreeInfoRec btinfo;
2292 size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE;
2293 size_t maxsize;
2294
2295 if (attrvp != NULL) {
2296 (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2297 if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0)
2298 nodesize = btinfo.nodeSize;
2299 hfs_unlock(VTOC(attrvp));
2300 }
2301 maxsize = nodesize;
2302 maxsize -= sizeof(BTNodeDescriptor); /* minus node descriptor */
2303 maxsize -= 3 * sizeof(u_int16_t); /* minus 3 index slots */
2304 maxsize /= 2; /* 2 key/rec pairs minumum */
2305 maxsize -= sizeof(HFSPlusAttrKey); /* minus maximum key size */
2306 maxsize -= sizeof(HFSPlusAttrData) - 2; /* minus data header */
2307 maxsize &= 0xFFFFFFFE; /* multiple of 2 bytes */
2308
2309 return (maxsize);
2310 }
2311
2312 /*
2313 * Initialize vnode for attribute data I/O.
2314 *
2315 * On success,
2316 * - returns zero
2317 * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp
2318 * - an iocount is taken on the attrdata vnode which exists
2319 * for the entire duration of the mount. It is only dropped
2320 * during unmount
2321 * - the attrdata cnode is not locked
2322 *
2323 * On failure,
2324 * - returns non-zero value
2325 * - the caller does not have to worry about any locks or references
2326 */
2327 int init_attrdata_vnode(struct hfsmount *hfsmp)
2328 {
2329 vnode_t vp;
2330 int result = 0;
2331 struct cat_desc cat_desc;
2332 struct cat_attr cat_attr;
2333 struct cat_fork cat_fork;
2334 int newvnode_flags = 0;
2335
2336 bzero(&cat_desc, sizeof(cat_desc));
2337 cat_desc.cd_parentcnid = kHFSRootParentID;
2338 cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename;
2339 cat_desc.cd_namelen = strlen(hfs_attrdatafilename);
2340 cat_desc.cd_cnid = kHFSAttributeDataFileID;
2341 /* Tag vnode as system file, note that we can still use cluster I/O */
2342 cat_desc.cd_flags |= CD_ISMETA;
2343
2344 bzero(&cat_attr, sizeof(cat_attr));
2345 cat_attr.ca_linkcount = 1;
2346 cat_attr.ca_mode = S_IFREG;
2347 cat_attr.ca_fileid = cat_desc.cd_cnid;
2348 cat_attr.ca_blocks = hfsmp->totalBlocks;
2349
2350 /*
2351 * The attribute data file is a virtual file that spans the
2352 * entire file system space.
2353 *
2354 * Each extent-based attribute occupies a unique portion of
2355 * in this virtual file. The cluster I/O is done using actual
2356 * allocation block offsets so no additional mapping is needed
2357 * for the VNOP_BLOCKMAP call.
2358 *
2359 * This approach allows the attribute data to be cached without
2360 * incurring the high cost of using a separate vnode per attribute.
2361 *
2362 * Since we need to acquire the attribute b-tree file lock anyways,
2363 * the virtual file doesn't introduce any additional serialization.
2364 */
2365 bzero(&cat_fork, sizeof(cat_fork));
2366 cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
2367 cat_fork.cf_blocks = hfsmp->totalBlocks;
2368 cat_fork.cf_extents[0].startBlock = 0;
2369 cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks;
2370
2371 result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr,
2372 &cat_fork, &vp, &newvnode_flags);
2373 if (result == 0) {
2374 hfsmp->hfs_attrdata_vp = vp;
2375 hfs_unlock(VTOC(vp));
2376 }
2377 return (result);
2378 }
2379
2380 /*
2381 * Read an extent based attribute.
2382 */
2383 static int
2384 read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2385 {
2386 vnode_t evp = hfsmp->hfs_attrdata_vp;
2387 int bufsize;
2388 int64_t iosize;
2389 int attrsize;
2390 int blksize;
2391 int i;
2392 int result = 0;
2393
2394 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2395
2396 bufsize = (int)uio_resid(uio);
2397 attrsize = (int)datasize;
2398 blksize = (int)hfsmp->blockSize;
2399
2400 /*
2401 * Read the attribute data one extent at a time.
2402 * For the typical case there is only one extent.
2403 */
2404 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2405 iosize = extents[i].blockCount * blksize;
2406 iosize = MIN(iosize, attrsize);
2407 iosize = MIN(iosize, bufsize);
2408 uio_setresid(uio, iosize);
2409 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2410
2411 result = cluster_read(evp, uio, VTOF(evp)->ff_size, IO_SYNC | IO_UNIT);
2412
2413 #if HFS_XATTR_VERBOSE
2414 printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n",
2415 iosize, extents[i].startBlock, extents[i].blockCount, result);
2416 #endif
2417 if (result)
2418 break;
2419 attrsize -= iosize;
2420 bufsize -= iosize;
2421 }
2422 uio_setresid(uio, bufsize);
2423 uio_setoffset(uio, datasize);
2424
2425 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
2426 return (result);
2427 }
2428
2429 /*
2430 * Write an extent based attribute.
2431 */
2432 static int
2433 write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2434 {
2435 vnode_t evp = hfsmp->hfs_attrdata_vp;
2436 off_t filesize;
2437 int bufsize;
2438 int attrsize;
2439 int64_t iosize;
2440 int blksize;
2441 int i;
2442 int result = 0;
2443
2444 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2445
2446 bufsize = uio_resid(uio);
2447 attrsize = (int) datasize;
2448 blksize = (int) hfsmp->blockSize;
2449 filesize = VTOF(evp)->ff_size;
2450
2451 /*
2452 * Write the attribute data one extent at a time.
2453 */
2454 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2455 iosize = extents[i].blockCount * blksize;
2456 iosize = MIN(iosize, attrsize);
2457 iosize = MIN(iosize, bufsize);
2458 uio_setresid(uio, iosize);
2459 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2460
2461 result = cluster_write(evp, uio, filesize, filesize, filesize,
2462 (off_t) 0, IO_SYNC | IO_UNIT);
2463 #if HFS_XATTR_VERBOSE
2464 printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n",
2465 iosize, extents[i].startBlock, extents[i].blockCount, result);
2466 #endif
2467 if (result)
2468 break;
2469 attrsize -= iosize;
2470 bufsize -= iosize;
2471 }
2472 uio_setresid(uio, bufsize);
2473 uio_setoffset(uio, datasize);
2474
2475 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
2476 return (result);
2477 }
2478
2479 /*
2480 * Allocate blocks for an extent based attribute.
2481 */
2482 static int
2483 alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks)
2484 {
2485 int blkcnt;
2486 int startblk;
2487 int lockflags;
2488 int i;
2489 int maxextents;
2490 int result = 0;
2491
2492 startblk = hfsmp->hfs_metazone_end;
2493 blkcnt = howmany(attrsize, hfsmp->blockSize);
2494 if (blkcnt > (int)hfs_freeblks(hfsmp, 0)) {
2495 return (ENOSPC);
2496 }
2497 *blocks = blkcnt;
2498 maxextents = extentbufsize / sizeof(HFSPlusExtentDescriptor);
2499
2500 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2501
2502 for (i = 0; (blkcnt > 0) && (i < maxextents); i++) {
2503 /* Try allocating and see if we find something decent */
2504 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0,
2505 &extents[i].startBlock, &extents[i].blockCount);
2506 /*
2507 * If we couldn't find anything, then re-try the allocation but allow
2508 * journal flushes.
2509 */
2510 if (result == dskFulErr) {
2511 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, HFS_ALLOC_FLUSHTXN,
2512 &extents[i].startBlock, &extents[i].blockCount);
2513 }
2514
2515
2516 #if HFS_XATTR_VERBOSE
2517 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2518 blkcnt, extents[i].startBlock, extents[i].blockCount, result);
2519 #endif
2520 if (result) {
2521 extents[i].startBlock = 0;
2522 extents[i].blockCount = 0;
2523 break;
2524 }
2525 blkcnt -= extents[i].blockCount;
2526 startblk = extents[i].startBlock + extents[i].blockCount;
2527 }
2528 /*
2529 * If it didn't fit in the extents buffer then bail.
2530 */
2531 if (blkcnt) {
2532 result = ENOSPC;
2533
2534 #if HFS_XATTR_VERBOSE
2535 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt);
2536 #endif
2537 for (; i >= 0; i--) {
2538 if ((blkcnt = extents[i].blockCount) != 0) {
2539 (void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt, 0);
2540 extents[i].startBlock = 0;
2541 extents[i].blockCount = 0;
2542 }
2543 }
2544 }
2545
2546 hfs_systemfile_unlock(hfsmp, lockflags);
2547 return MacToVFSError(result);
2548 }
2549
2550 /*
2551 * Release blocks from an extent based attribute.
2552 */
2553 static void
2554 free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents)
2555 {
2556 vnode_t evp = hfsmp->hfs_attrdata_vp;
2557 int remblks = blkcnt;
2558 int lockflags;
2559 int i;
2560
2561 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2562
2563 for (i = 0; (remblks > 0) && (extents[i].blockCount != 0); i++) {
2564 if (extents[i].blockCount > (u_int32_t)blkcnt) {
2565 #if HFS_XATTR_VERBOSE
2566 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
2567 extents[i].startBlock, extents[i].blockCount);
2568 #endif
2569 extents[i].blockCount = 0;
2570 continue;
2571 }
2572 if (extents[i].startBlock == 0) {
2573 break;
2574 }
2575 (void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount, 0);
2576 remblks -= extents[i].blockCount;
2577 extents[i].startBlock = 0;
2578 extents[i].blockCount = 0;
2579
2580 #if HFS_XATTR_VERBOSE
2581 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2582 extents[i].startBlock, extents[i].blockCount);
2583 #endif
2584 /* Discard any resident pages for this block range. */
2585 if (evp) {
2586 off_t start, end;
2587
2588 start = (u_int64_t)extents[i].startBlock * (u_int64_t)hfsmp->blockSize;
2589 end = start + (u_int64_t)extents[i].blockCount * (u_int64_t)hfsmp->blockSize;
2590 (void) ubc_msync(hfsmp->hfs_attrdata_vp, start, end, &start, UBC_INVALIDATE);
2591 }
2592 }
2593
2594 hfs_systemfile_unlock(hfsmp, lockflags);
2595 }
2596
2597 static int
2598 has_overflow_extents(HFSPlusForkData *forkdata)
2599 {
2600 u_int32_t blocks;
2601
2602 if (forkdata->extents[7].blockCount == 0)
2603 return (0);
2604
2605 blocks = forkdata->extents[0].blockCount +
2606 forkdata->extents[1].blockCount +
2607 forkdata->extents[2].blockCount +
2608 forkdata->extents[3].blockCount +
2609 forkdata->extents[4].blockCount +
2610 forkdata->extents[5].blockCount +
2611 forkdata->extents[6].blockCount +
2612 forkdata->extents[7].blockCount;
2613
2614 return (forkdata->totalBlocks > blocks);
2615 }
2616
2617 static int
2618 count_extent_blocks(int maxblks, HFSPlusExtentRecord extents)
2619 {
2620 int blocks;
2621 int i;
2622
2623 for (i = 0, blocks = 0; i < kHFSPlusExtentDensity; ++i) {
2624 /* Ignore obvious bogus extents. */
2625 if (extents[i].blockCount > (u_int32_t)maxblks)
2626 continue;
2627 if (extents[i].startBlock == 0 || extents[i].blockCount == 0)
2628 break;
2629 blocks += extents[i].blockCount;
2630 }
2631 return (blocks);
2632 }
2633