2 * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.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>
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"
50 #include "BTreesInternal.h"
52 #define HFS_XATTR_VERBOSE 0
54 #define ATTRIBUTE_FILE_NODE_SIZE 8192
57 /* State information for the listattr_callback callback function. */
58 struct listattr_callback_state
{
67 #endif /* HFS_COMPRESSION */
71 /* HFS Internal Names */
72 #define XATTR_XATTREXTENTS_NAME "system.xattrextents"
74 static u_int32_t emptyfinfo
[8] = {0};
76 static int hfs_zero_hidden_fields (struct cnode
*cp
, u_int8_t
*finderinfo
);
78 const char hfs_attrdatafilename
[] = "Attribute Data";
80 static int listattr_callback(const HFSPlusAttrKey
*key
, const HFSPlusAttrData
*data
,
81 struct listattr_callback_state
*state
);
83 static int remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
);
85 static int getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
);
87 static size_t getmaxinlineattrsize(struct vnode
* attrvp
);
89 static int read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
);
91 static int write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
);
93 static int alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
);
95 static void free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
);
97 static int has_overflow_extents(HFSPlusForkData
*forkdata
);
99 static int count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
);
103 * Obtain the vnode for a stream.
106 hfs_vnop_getnamedstream(struct vnop_getnamedstream_args
* ap
)
108 vnode_t vp
= ap
->a_vp
;
109 vnode_t
*svpp
= ap
->a_svpp
;
116 * We only support the "com.apple.ResourceFork" stream.
118 if (strcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
) != 0) {
122 if ( !S_ISREG(cp
->c_mode
) ) {
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
))) {
131 if ((!hfs_has_rsrc(cp
)
134 #endif /* HFS_COMPRESSION */
135 ) && (ap
->a_operation
!= NS_OPEN
)) {
139 error
= hfs_vgetrsrc(VTOHFS(vp
), vp
, svpp
);
149 hfs_vnop_makenamedstream(struct vnop_makenamedstream_args
* ap
)
151 vnode_t vp
= ap
->a_vp
;
152 vnode_t
*svpp
= ap
->a_svpp
;
159 * We only support the "com.apple.ResourceFork" stream.
161 if (strcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
) != 0) {
165 if ( !S_ISREG(cp
->c_mode
) ) {
169 if (hfs_hides_rsrc(ap
->a_context
, VTOC(vp
), 1)) {
170 if (VNODE_IS_RSRC(vp
)) {
173 error
= decmpfs_decompress_file(vp
, VTOCMP(vp
), -1, 1, 0);
178 #endif /* HFS_COMPRESSION */
179 if ((error
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
182 error
= hfs_vgetrsrc(VTOHFS(vp
), vp
, svpp
);
192 hfs_vnop_removenamedstream(struct vnop_removenamedstream_args
* ap
)
194 vnode_t svp
= ap
->a_svp
;
195 cnode_t
*scp
= VTOC(svp
);
199 * We only support the "com.apple.ResourceFork" stream.
201 if (strcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
) != 0) {
205 if (hfs_hides_rsrc(ap
->a_context
, scp
, 1)) {
209 #endif /* HFS_COMPRESSION */
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
);
216 hfs_unlock_truncate(scp
, HFS_LOCK_DEFAULT
);
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
)
226 u_int8_t
*finfo
= finderinfo
;
228 /* Advance finfo by 16 bytes to the 2nd half of the finderinfo */
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;
242 /* Return an error */
250 * Retrieve the data of an extended attribute.
253 hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
)
255 struct vnop_getxattr_args {
256 struct vnodeop_desc *a_desc;
262 vfs_context_t a_context;
266 struct vnode
*vp
= ap
->a_vp
;
268 struct hfsmount
*hfsmp
;
269 uio_t uio
= ap
->a_uio
;
274 if (vp
== cp
->c_vp
) {
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
))
279 #endif /* HFS_COMPRESSION */
281 /* Get the Finder Info. */
282 if (strcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
) == 0) {
283 u_int8_t finderinfo
[32];
286 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
289 /* Make a copy since we may not export all of it. */
290 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
293 /* Zero out the date added field in the local copy */
294 hfs_zero_hidden_fields (cp
, finderinfo
);
296 /* Don't expose a symlink's private type/creator. */
297 if (vnode_islnk(vp
)) {
298 struct FndrFileInfo
*fip
;
300 fip
= (struct FndrFileInfo
*)&finderinfo
;
304 /* If Finder Info is empty then it doesn't exist. */
305 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
309 *ap
->a_size
= bufsize
;
312 if ((user_size_t
)uio_resid(uio
) < bufsize
)
315 result
= uiomove((caddr_t
)&finderinfo
, bufsize
, uio
);
319 /* Read the Resource Fork. */
320 if (strcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
) == 0) {
321 struct vnode
*rvp
= NULL
;
322 int openunlinked
= 0;
325 if ( !S_ISREG(cp
->c_mode
) ) {
328 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
331 namelen
= cp
->c_desc
.cd_namelen
;
333 if (!hfs_has_rsrc(cp
)) {
338 if ((cp
->c_flag
& C_DELETED
) && (namelen
== 0)) {
342 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
);
348 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
351 user_ssize_t uio_size
= 0;
353 uio_size
= uio_resid(uio
);
354 #endif /* HFS_COMPRESSION */
355 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
359 (uio_resid(uio
) == uio_size
)) {
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.
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)
370 * In that case, we try again with the decmpfs_ctx context
371 * to get the actual data
373 result
= VNOP_READ(rvp
, uio
, 0, decmpfs_ctx
);
375 #endif /* HFS_COMPRESSION */
377 /* force the rsrc fork vnode to recycle right away */
380 vref
= vnode_ref (rvp
);
393 * Standard HFS only supports native FinderInfo and Resource Forks.
395 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
400 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
404 /* Check for non-rsrc, non-finderinfo EAs */
405 result
= hfs_getxattr_internal (cp
, ap
, VTOHFS(cp
->c_vp
), 0);
409 return MacToVFSError(result
);
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
)
415 uio_t uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
417 uio_addiov(uio
, CAST_USER_ADDR_T(data
), *size
);
419 struct vnop_getxattr_args args
= {
425 int ret
= hfs_getxattr_internal(VTOC(vp
), &args
, VTOHFS(vp
), 0);
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.
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
446 * NOTE: This function assumes the cnode lock for 'cp' is held exclusive or shared.
448 int hfs_getxattr_internal (struct cnode
*cp
, struct vnop_getxattr_args
*ap
,
449 struct hfsmount
*hfsmp
, u_int32_t fileid
)
452 struct filefork
*btfile
;
453 struct BTreeIterator
* iterator
= NULL
;
455 HFSPlusAttrRecord
*recp
= NULL
;
456 size_t recp_size
= 0;
457 FSBufferDescriptor btdata
;
460 u_int16_t datasize
= 0;
461 uio_t uio
= ap
->a_uio
;
462 u_int32_t target_id
= 0;
465 target_id
= cp
->c_fileid
;
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)) {
478 /* Initialize the B-Tree iterator for searching for the proper EA */
479 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
481 iterator
= hfs_mallocz(sizeof(*iterator
));
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.
487 recp
= hfs_malloc(recp_size
= sizeof(HFSPlusAttrRecord
));
488 btdata
.bufferAddress
= recp
;
489 btdata
.itemSize
= sizeof(HFSPlusAttrRecord
);
490 btdata
.itemCount
= 1;
492 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
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
);
503 if (result
== btNotFound
) {
510 * Operate differently if we have inline EAs that can fit in the attribute B-Tree or if
511 * we have extent based EAs.
513 switch (recp
->recordType
) {
515 /* Attribute fits in the Attribute B-Tree */
516 case kHFSPlusAttrInlineData
: {
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).
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
));
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 */
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.
539 bufsize
= sizeof(HFSPlusAttrData
) - 2 + recp
->attrData
.attrSize
;
540 hfs_free(recp
, recp_size
);
541 recp
= hfs_malloc(recp_size
= bufsize
);
543 btdata
.bufferAddress
= recp
;
544 btdata
.itemSize
= bufsize
;
545 btdata
.itemCount
= 1;
547 bzero(iterator
, sizeof(*iterator
));
548 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
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
);
558 if (result
== btNotFound
) {
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
);
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
));
580 *ap
->a_size
= recp
->forkData
.theFork
.logicalSize
;
584 if (*ap
->a_size
> (user_size_t
)uio_resid(uio
)) {
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
;
597 totalblocks
= recp
->forkData
.theFork
.totalBlocks
;
598 /* Ignore bogus block counts. */
599 if (totalblocks
> howmany(HFS_XATTR_MAXSIZE
, hfsmp
->blockSize
)) {
603 attrlen
= recp
->forkData
.theFork
.logicalSize
;
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
;
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
);
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
);
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
);
627 break; /* break from while */
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
);
635 /* Release Attr B-Tree lock */
636 hfs_systemfile_unlock(hfsmp
, lockflags
);
638 if (blkcnt
< totalblocks
) {
641 result
= read_attr_data(hfsmp
, uio
, attrlen
, extentbuf
);
643 hfs_free(extentbuf
, extentbufsize
);
645 } else { /* No overflow extents. */
646 result
= read_attr_data(hfsmp
, uio
, recp
->forkData
.theFork
.logicalSize
, recp
->forkData
.theFork
.extents
);
652 /* We only support Extent or inline EAs. Default to ENOATTR for anything else */
658 hfs_free(iterator
, sizeof(*iterator
));
659 hfs_free(recp
, recp_size
);
667 * Set the data of an extended attribute.
670 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
672 struct vnop_setxattr_args {
673 struct vnodeop_desc *a_desc;
678 vfs_context_t a_context;
682 struct vnode
*vp
= ap
->a_vp
;
683 struct cnode
*cp
= NULL
;
684 struct hfsmount
*hfsmp
;
685 uio_t uio
= ap
->a_uio
;
687 void * user_data_ptr
= NULL
;
689 time_t orig_ctime
=VTOC(vp
)->c_ctime
;
691 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
692 return (EINVAL
); /* invalid name */
695 if (VNODE_IS_RSRC(vp
)) {
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);
705 #endif /* HFS_COMPRESSION */
707 nspace_snapshot_event(vp
, orig_ctime
, NAMESPACE_HANDLER_METADATA_WRITE_OP
, NSPACE_REARM_NO_ARG
);
709 /* Set the Finder Info. */
710 if (strcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
) == 0) {
714 struct FndrFileInfo info
;
716 void * finderinfo_start
;
717 u_int8_t
*finfo
= NULL
;
719 u_int32_t dateadded
= 0;
720 u_int32_t write_gen_counter
= 0;
721 u_int32_t document_id
= 0;
723 attrsize
= sizeof(VTOC(vp
)->c_finderinfo
);
725 if ((user_size_t
)uio_resid(uio
) != attrsize
) {
728 /* Grab the new Finder Info data. */
729 if ((result
= uiomove(fi
.cdata
, attrsize
, uio
))) {
733 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
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];
744 finderinfo_start
= &cp
->c_finderinfo
[0];
746 * Don't allow the external setting of
747 * file type to kHardLinkFileType.
749 if (fi
.info
.fdType
== SWAP_BE32(kHardLinkFileType
)) {
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);
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.
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
;
774 * Zero out the finder info's reserved fields like date added,
775 * generation counter, and document id to ignore user's attempts
778 hfs_zero_hidden_fields(cp
, fi
.data
);
780 if (bcmp(finderinfo_start
, emptyfinfo
, attrsize
)) {
781 /* attr exists and "create" was specified. */
782 if (ap
->a_options
& XATTR_CREATE
) {
787 /* attr doesn't exists and "replace" was specified. */
788 if (ap
->a_options
& XATTR_REPLACE
) {
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.
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.
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
;
816 /* Set the cnode's Finder Info. */
817 if (attrsize
== sizeof(cp
->c_finderinfo
)) {
818 bcopy(&fi
.data
[0], finderinfo_start
, attrsize
);
820 bcopy(&fi
.data
[8], finderinfo_start
, attrsize
);
823 /* Updating finderInfo updates change time and modified time */
824 cp
->c_touch_chgtime
= TRUE
;
825 cp
->c_flag
|= C_MODIFIED
;
828 * Mirror the invisible bit to the UF_HIDDEN flag.
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.
834 fdFlags
= *((u_int16_t
*) &cp
->c_finderinfo
[8]);
835 if (fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
)) {
836 cp
->c_bsdflags
|= UF_HIDDEN
;
838 cp
->c_bsdflags
&= ~UF_HIDDEN
;
841 result
= hfs_update(vp
, 0);
846 /* Write the Resource Fork. */
847 if (strcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
) == 0) {
848 struct vnode
*rvp
= NULL
;
850 int openunlinked
= 0;
852 if (!vnode_isreg(vp
)) {
855 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
859 namelen
= cp
->c_desc
.cd_namelen
;
861 if (hfs_has_rsrc(cp
)) {
862 /* attr exists and "create" was specified. */
863 if (ap
->a_options
& XATTR_CREATE
) {
868 /* attr doesn't exists and "replace" was specified. */
869 if (ap
->a_options
& XATTR_REPLACE
) {
876 * Note that we could be called on to grab the rsrc fork vnode
877 * for a file that has become open-unlinked.
879 if ((cp
->c_flag
& C_DELETED
) && (namelen
== 0)) {
883 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
);
888 /* VNOP_WRITE marks cnode as needing a modtime update */
889 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
891 /* if open unlinked, force it inactive */
894 vref
= vnode_ref (rvp
);
900 /* cnode is not open-unlinked, so re-lock cnode to sync */
901 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
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
));
917 * Standard HFS only supports native FinderInfo and Resource Forks.
919 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
923 attrsize
= uio_resid(uio
);
925 /* Enforce an upper limit. */
926 if (attrsize
> HFS_XATTR_MAXSIZE
) {
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.
937 hfsmp
->hfs_max_inline_attrsize
!= 0 &&
938 attrsize
< hfsmp
->hfs_max_inline_attrsize
) {
939 user_data_ptr
= hfs_malloc(attrsize
);
941 result
= uiomove((caddr_t
)user_data_ptr
, attrsize
, uio
);
947 result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
954 * If we're trying to set a non-finderinfo, non-resourcefork EA, then
955 * call the breakout function.
957 result
= hfs_setxattr_internal (cp
, user_data_ptr
, attrsize
, ap
, VTOHFS(vp
), 0);
964 hfs_free(user_data_ptr
, attrsize
);
967 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
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
)
973 struct vnop_setxattr_args args
= {
978 return hfs_setxattr_internal(VTOC(vp
), data
, size
, &args
, VTOHFS(vp
), 0);
982 * hfs_setxattr_internal
984 * Internal function to set non-rsrc, non-finderinfo EAs to either the attribute B-Tree or
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.
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.
997 int hfs_setxattr_internal (struct cnode
*cp
, const void *data_ptr
, size_t attrsize
,
998 struct vnop_setxattr_args
*ap
, struct hfsmount
*hfsmp
,
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;
1015 int allocatedblks
= 0;
1016 u_int32_t target_id
;
1019 target_id
= cp
->c_fileid
;
1024 /* Start a transaction for our changes. */
1025 if (hfs_start_transaction(hfsmp
) != 0) {
1029 started_transaction
= 1;
1032 * Once we started the transaction, nobody can compete
1033 * with us, so make sure this file is still there.
1035 if ((cp
) && (cp
->c_flag
& C_NOEXISTS
)) {
1041 * If there isn't an attributes b-tree then create one.
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
));
1050 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
1051 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
1054 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1056 /* Build the b-tree key. */
1057 iterator
= hfs_mallocz(sizeof(*iterator
));
1058 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
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;
1070 /* Replace requires that the attribute already exists. */
1071 if ((ap
->a_options
& XATTR_REPLACE
) && !exists
) {
1075 /* Create requires that the attribute doesn't exist. */
1076 if ((ap
->a_options
& XATTR_CREATE
) && exists
) {
1081 /* If it won't fit inline then use extent-based attributes. */
1082 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
1085 u_int32_t
*keystartblk
;
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
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
);
1106 goto exit
; /* no more space */
1108 /* Copy data into the blocks. */
1109 result
= write_attr_data(hfsmp
, uio
, attrsize
, extentptr
);
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
);
1116 vnode_putname(name
);
1121 /* Now remove any previous attribute. */
1123 result
= remove_attribute_records(hfsmp
, iterator
);
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
);
1130 vnode_putname(name
);
1135 /* Create attribute fork data record. */
1136 recp
= hfs_malloc(recp_size
= sizeof(HFSPlusAttrRecord
));
1138 btdata
.bufferAddress
= recp
;
1139 btdata
.itemCount
= 1;
1140 btdata
.itemSize
= sizeof(HFSPlusAttrForkData
);
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
));
1149 (void) hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1151 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1153 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
1154 hfsmp
->vcbVN
, target_id
, ap
->a_name
, result
);
1157 extentblks
= count_extent_blocks(blkcnt
, recp
->forkData
.theFork
.extents
);
1158 blkcnt
-= extentblks
;
1159 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
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;
1170 /* Copy the next set of extents. */
1171 i
+= kHFSPlusExtentDensity
;
1172 bcopy(&extentptr
[i
], recp
->overflowExtents
.extents
, sizeof(HFSPlusExtentRecord
));
1174 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1176 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
1177 hfsmp
->vcbVN
, target_id
, ap
->a_name
, result
);
1180 extentblks
= count_extent_blocks(blkcnt
, recp
->overflowExtents
.extents
);
1181 blkcnt
-= extentblks
;
1183 } else { /* Inline data */
1185 result
= remove_attribute_records(hfsmp
, iterator
);
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
);
1195 recp
->recordType
= kHFSPlusAttrInlineData
;
1196 recp
->attrData
.reserved
[0] = 0;
1197 recp
->attrData
.reserved
[1] = 0;
1198 recp
->attrData
.attrSize
= attrsize
;
1200 /* Copy in the attribute data (if any). */
1203 bcopy(data_ptr
, &recp
->attrData
.attrData
, attrsize
);
1206 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
1207 * then deny the copy operation.
1213 result
= uiomove((caddr_t
)&recp
->attrData
.attrData
, attrsize
, uio
);
1221 (void) hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1223 btdata
.bufferAddress
= recp
;
1224 btdata
.itemCount
= 1;
1225 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1229 if (btfile
&& started_transaction
) {
1230 (void) BTFlushPath(btfile
);
1232 hfs_systemfile_unlock(hfsmp
, lockflags
);
1236 /* Setting an attribute only updates change time and not
1237 * modified time of the file.
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
;
1245 (void) hfs_update(vp
, 0);
1248 if (started_transaction
) {
1249 if (result
&& allocatedblks
) {
1250 free_attr_blks(hfsmp
, allocatedblks
, extentptr
);
1252 hfs_end_transaction(hfsmp
);
1255 hfs_free(recp
, recp_size
);
1256 hfs_free(extentptr
, extentbufsize
);
1257 hfs_free(iterator
, sizeof(*iterator
));
1266 * Remove an extended attribute.
1269 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
1271 struct vnop_removexattr_args {
1272 struct vnodeop_desc *a_desc;
1276 vfs_context_t a_context;
1280 struct vnode
*vp
= ap
->a_vp
;
1281 struct cnode
*cp
= VTOC(vp
);
1282 struct hfsmount
*hfsmp
;
1283 struct BTreeIterator
* iterator
= NULL
;
1286 time_t orig_ctime
=VTOC(vp
)->c_ctime
;
1288 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
1289 return (EINVAL
); /* invalid name */
1292 if (VNODE_IS_RSRC(vp
)) {
1297 if (hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1) && !(ap
->a_options
& XATTR_SHOWCOMPRESSION
)) {
1300 #endif /* HFS_COMPRESSION */
1302 nspace_snapshot_event(vp
, orig_ctime
, NAMESPACE_HANDLER_METADATA_DELETE_OP
, NSPACE_REARM_NO_ARG
);
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
;
1308 if ( !vnode_isreg(vp
) ) {
1311 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1314 if (!hfs_has_rsrc(cp
)) {
1318 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
);
1324 hfs_lock_truncate(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
1326 // Tell UBC now before we take the cnode lock and start the transaction
1327 hfs_ubc_setsize(rvp
, 0, false);
1329 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1330 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1335 /* Start a transaction for encapsulating changes in
1336 * hfs_truncate() and hfs_update()
1338 if ((result
= hfs_start_transaction(hfsmp
))) {
1339 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1345 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, ap
->a_context
);
1347 cp
->c_touch_chgtime
= TRUE
;
1348 cp
->c_flag
|= C_MODIFIED
;
1349 result
= hfs_update(vp
, 0);
1352 hfs_end_transaction(hfsmp
);
1353 hfs_unlock_truncate(VTOC(rvp
), HFS_LOCK_DEFAULT
);
1354 hfs_unlock(VTOC(rvp
));
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
;
1367 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1371 /* Use the local copy to store our temporary changes. */
1372 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
1375 /* Zero out the date added field in the local copy */
1376 hfs_zero_hidden_fields (cp
, finderinfo
);
1378 /* Don't expose a symlink's private type/creator. */
1379 if (vnode_islnk(vp
)) {
1380 struct FndrFileInfo
*fip
;
1382 fip
= (struct FndrFileInfo
*)&finderinfo
;
1387 /* Do the byte compare against the local copy */
1388 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
1394 * If there was other content, zero out everything except
1395 * type/creator and date added. First, save the date added.
1397 finfo
= cp
->c_finderinfo
;
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
;
1411 if (vnode_islnk(vp
)) {
1412 /* Ignore type/creator */
1413 finderinfo_start
= &cp
->c_finderinfo
[8];
1414 finderinfo_size
= sizeof(cp
->c_finderinfo
) - 8;
1416 finderinfo_start
= &cp
->c_finderinfo
[0];
1417 finderinfo_size
= sizeof(cp
->c_finderinfo
);
1419 bzero(finderinfo_start
, finderinfo_size
);
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
;
1435 /* Updating finderInfo updates change time and modified time */
1436 cp
->c_touch_chgtime
= TRUE
;
1437 cp
->c_flag
|= C_MODIFIED
;
1446 * Standard HFS only supports native FinderInfo and Resource Forks.
1448 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1452 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1456 iterator
= hfs_mallocz(sizeof(*iterator
));
1458 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1462 result
= hfs_buildattrkey(cp
->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1467 if (hfs_start_transaction(hfsmp
) != 0) {
1471 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1473 result
= remove_attribute_records(hfsmp
, iterator
);
1475 hfs_systemfile_unlock(hfsmp
, lockflags
);
1478 cp
->c_touch_chgtime
= TRUE
;
1480 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1482 /* If no more attributes exist, clear attribute bit */
1483 result
= file_attribute_exist(hfsmp
, cp
->c_fileid
);
1485 cp
->c_attr
.ca_recflags
&= ~kHFSHasAttributesMask
;
1486 cp
->c_flag
|= C_MODIFIED
;
1488 if (result
== EEXIST
) {
1492 hfs_systemfile_unlock(hfsmp
, lockflags
);
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
;
1499 (void) hfs_update(vp
, 0);
1502 hfs_end_transaction(hfsmp
);
1506 hfs_free(iterator
, sizeof(*iterator
));
1507 return MacToVFSError(result
);
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.
1517 * Assumes that the cnode backing the fileid specified is LOCKED.
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
1524 int started_txn
= 0;
1527 memset (&iter
, 0, sizeof(iter
));
1529 //position the B-Tree iter key before grabbing locks and starting a txn
1530 ret
= hfs_buildattrkey (fileid
, xattr_name
, (HFSPlusAttrKey
*)&iter
.key
);
1535 //note: this is likely a nested transaction since there is a global transaction cover
1536 if (hfs_start_transaction (hfsmp
) != 0) {
1543 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1545 //actually remove the EA from the tree
1546 ret
= remove_attribute_records(hfsmp
, &iter
);
1548 hfs_systemfile_unlock(hfsmp
, lockflags
);
1551 * NOTE: Responsibility of the caller to remove the "has XATTRs" bit in the catalog record
1552 * if this was the last EA.
1558 hfs_end_transaction(hfsmp
);
1561 return MacToVFSError(ret
);
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.
1570 * Note - you must acquire a shared lock on the attribute btree before
1571 * calling this function.
1574 * EEXIST - If attribute record was found
1575 * 0 - Attribute was not found
1576 * (other) - Other error (such as EIO)
1579 file_attribute_exist(struct hfsmount
*hfsmp
, uint32_t fileID
)
1581 HFSPlusAttrKey
*key
;
1582 struct BTreeIterator
* iterator
= NULL
;
1583 struct filefork
*btfile
;
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
) {
1592 iterator
= hfs_mallocz(sizeof(*iterator
));
1594 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1596 result
= hfs_buildattrkey(fileID
, NULL
, key
);
1601 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1602 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1603 if (result
&& (result
!= btNotFound
)) {
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
1611 if ((result
&& (result
== btNotFound
)) || (key
->fileID
!= fileID
)) {
1618 hfs_free(iterator
, sizeof(*iterator
));
1624 * Remove all the records for a given attribute.
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.
1633 remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
)
1635 struct filefork
*btfile
;
1636 FSBufferDescriptor btdata
;
1637 HFSPlusAttrRecord attrdata
; /* 90 bytes */
1641 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1643 btdata
.bufferAddress
= &attrdata
;
1644 btdata
.itemSize
= sizeof(attrdata
);
1645 btdata
.itemCount
= 1;
1646 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1648 goto exit
; /* no records. */
1651 * Free the blocks from extent based attributes.
1653 * Note that the block references (btree records) are removed
1654 * before releasing the blocks in the allocation bitmap.
1656 if (attrdata
.recordType
== kHFSPlusAttrForkData
) {
1659 u_int32_t
*keystartblk
;
1661 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
1662 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize
, sizeof(HFSPlusAttrForkData
));
1664 totalblks
= attrdata
.forkData
.theFork
.totalBlocks
;
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
);
1673 totalblks
-= extentblks
;
1674 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1676 /* Process any overflow extents. */
1678 *keystartblk
+= (u_int32_t
)extentblks
;
1680 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
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
);
1687 break; /* break from while */
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
);
1696 totalblks
-= extentblks
;
1699 result
= BTDeleteRecord(btfile
, iterator
);
1701 (void) BTFlushPath(btfile
);
1703 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
1708 * Retrieve the list of extended attribute names.
1711 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
1713 struct vnop_listxattr_args {
1714 struct vnodeop_desc *a_desc;
1719 vfs_context_t a_context;
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;
1733 u_int8_t finderinfo
[32];
1736 if (VNODE_IS_RSRC(vp
)) {
1741 int compressed
= hfs_file_is_compressed(cp
, 1); /* 1 == don't take the cnode lock */
1742 #endif /* HFS_COMPRESSION */
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.
1752 hfs_lock_truncate(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
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
);
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
1765 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
1766 hfs_zero_hidden_fields (cp
, finderinfo
);
1768 /* Don't expose a symlink's private type/creator. */
1769 if (vnode_islnk(vp
)) {
1770 struct FndrFileInfo
*fip
;
1772 fip
= (struct FndrFileInfo
*)&finderinfo
;
1778 /* If Finder Info is non-empty then export it's name. */
1779 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) != 0) {
1781 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
1782 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1786 result
= uiomove(XATTR_FINDERINFO_NAME
,
1787 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1792 /* If Resource Fork is non-empty then export it's name. */
1793 if (S_ISREG(cp
->c_mode
) && hfs_has_rsrc(cp
)) {
1795 if ((ap
->a_options
& XATTR_SHOWCOMPRESSION
) ||
1797 !decmpfs_hides_rsrc(ap
->a_context
, VTOCMP(vp
))
1799 #endif /* HFS_COMPRESSION */
1802 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1803 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1807 result
= uiomove(XATTR_RESOURCEFORK_NAME
,
1808 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
1816 * Standard HFS only supports native FinderInfo and Resource Forks.
1817 * Return at this point.
1819 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
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) {
1830 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1832 iterator
= hfs_mallocz(sizeof(*iterator
));
1834 result
= hfs_buildattrkey(cp
->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
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.
1842 if (uio
&& uio_isuserspace(uio
)) {
1843 user_start
= uio_curriovbase(uio
);
1844 user_len
= uio_curriovlen(uio
);
1846 if ((result
= vslock(user_start
, user_len
)) != 0) {
1851 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1853 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1854 if (result
&& result
!= btNotFound
) {
1855 hfs_systemfile_unlock(hfsmp
, lockflags
);
1859 state
.fileID
= cp
->c_fileid
;
1864 state
.showcompressed
= !compressed
|| ap
->a_options
& XATTR_SHOWCOMPRESSION
;
1865 state
.ctx
= ap
->a_context
;
1867 #endif /* HFS_COMPRESSION */
1870 * Process entries starting just after iterator->key.
1872 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
1873 (IterateCallBackProcPtr
)listattr_callback
, &state
);
1874 hfs_systemfile_unlock(hfsmp
, lockflags
);
1876 *ap
->a_size
+= state
.size
;
1879 if (state
.result
|| result
== btNotFound
)
1880 result
= state
.result
;
1884 vsunlock(user_start
, user_len
, TRUE
);
1886 hfs_free(iterator
, sizeof(*iterator
));
1888 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1890 return MacToVFSError(result
);
1895 * Callback - called for each attribute record
1898 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
1900 char attrname
[XATTR_MAXNAMELEN
+ 1];
1904 if (state
->fileID
!= key
->fileID
) {
1906 return (0); /* stop */
1909 * Skip over non-primary keys
1911 if (key
->startBlock
!= 0) {
1912 return (1); /* continue */
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);
1919 state
->result
= result
;
1920 return (0); /* stop */
1922 bytecount
++; /* account for null termination char */
1924 if (xattr_protected(attrname
))
1925 return (1); /* continue */
1928 if (!state
->showcompressed
&& decmpfs_hides_xattr(state
->ctx
, VTOCMP(state
->vp
), attrname
) )
1929 return 1; /* continue */
1930 #endif /* HFS_COMPRESSION */
1932 if (state
->uio
== NULL
) {
1933 state
->size
+= bytecount
;
1935 if (bytecount
> uio_resid(state
->uio
)) {
1936 state
->result
= ERANGE
;
1937 return (0); /* stop */
1939 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
1941 state
->result
= result
;
1942 return (0); /* stop */
1945 return (1); /* continue */
1949 * Remove all the attributes from a cnode.
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).
1955 * This function takes the necessary locks on the attribute
1956 * b-tree file and the allocation (bitmap) file.
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.
1966 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
,
1967 bool *open_transaction
)
1969 BTreeIterator
*iterator
= NULL
;
1970 HFSPlusAttrKey
*key
;
1971 struct filefork
*btfile
;
1972 int result
, lockflags
= 0;
1974 *open_transaction
= false;
1976 if (hfsmp
->hfs_attribute_vp
== NULL
)
1979 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1981 iterator
= hfs_mallocz(sizeof(BTreeIterator
));
1983 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1985 /* Loop until there are no more attributes for this file id */
1987 if (!*open_transaction
)
1988 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1990 (void) hfs_buildattrkey(fileid
, NULL
, key
);
1991 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1992 if (result
|| key
->fileID
!= fileid
)
1995 hfs_systemfile_unlock(hfsmp
, lockflags
);
1998 if (*open_transaction
) {
1999 hfs_end_transaction(hfsmp
);
2000 *open_transaction
= false;
2003 if (hfs_start_transaction(hfsmp
) != 0) {
2008 *open_transaction
= true;
2010 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2012 result
= remove_attribute_records(hfsmp
, iterator
);
2014 #if HFS_XATTR_VERBOSE
2016 printf("hfs_removeallattr: unexpected err %d\n", result
);
2022 hfs_free(iterator
, sizeof(*iterator
));
2025 hfs_systemfile_unlock(hfsmp
, lockflags
);
2027 result
= result
== btNotFound
? 0 : MacToVFSError(result
);
2029 if (result
&& *open_transaction
) {
2030 hfs_end_transaction(hfsmp
);
2031 *open_transaction
= false;
2038 hfs_xattr_init(struct hfsmount
* hfsmp
)
2041 if (ISSET(hfsmp
->hfs_flags
, HFS_STANDARD
))
2046 * If there isn't an attributes b-tree then create one.
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
));
2053 if (hfsmp
->hfs_attribute_vp
)
2054 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
2058 * Enable/Disable volume attributes stored as EA for root file system.
2059 * Supported attributes are -
2060 * 1. Extent-based Extended Attributes
2063 hfs_set_volxattr(struct hfsmount
*hfsmp
, unsigned int xattrtype
, int state
)
2065 struct BTreeIterator
* iterator
= NULL
;
2066 struct filefork
*btfile
;
2071 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
2075 if (xattrtype
!= HFSIOC_SET_XATTREXTENTS_STATE
) {
2080 * If there isn't an attributes b-tree then create one.
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
));
2090 iterator
= hfs_mallocz(sizeof(*iterator
));
2093 * Build a b-tree key.
2094 * We use the root's parent id (1) to hold this volume attribute.
2096 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_XATTREXTENTS_NAME
,
2097 (HFSPlusAttrKey
*)&iterator
->key
);
2099 /* Start a transaction for our changes. */
2100 if (hfs_start_transaction(hfsmp
) != 0) {
2104 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
2106 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
2109 /* Remove the attribute. */
2110 result
= BTDeleteRecord(btfile
, iterator
);
2111 if (result
== btNotFound
)
2114 FSBufferDescriptor btdata
;
2115 HFSPlusAttrData attrdata
;
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;
2129 /* Insert the attribute. */
2130 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
2131 if (result
== btExists
)
2134 (void) BTFlushPath(btfile
);
2136 hfs_systemfile_unlock(hfsmp
, lockflags
);
2138 /* Finish the transaction of our changes. */
2139 hfs_end_transaction(hfsmp
);
2141 /* Update the state in the mount point */
2142 hfs_lock_mount (hfsmp
);
2144 hfsmp
->hfs_flags
&= ~HFS_XATTR_EXTENTS
;
2146 hfsmp
->hfs_flags
|= HFS_XATTR_EXTENTS
;
2148 hfs_unlock_mount (hfsmp
);
2151 hfs_free(iterator
, sizeof(*iterator
));
2152 return MacToVFSError(result
);
2157 * hfs_attrkeycompare - compare two attribute b-tree keys.
2159 * The name portion of the key is compared using a 16-bit binary comparison.
2160 * This is called from the b-tree code.
2163 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
2165 u_int32_t searchFileID
, trialFileID
;
2168 searchFileID
= searchKey
->fileID
;
2169 trialFileID
= trialKey
->fileID
;
2172 if (searchFileID
> trialFileID
) {
2174 } else if (searchFileID
< trialFileID
) {
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
;
2184 if (length1
< length2
) {
2187 } else if (length1
> length2
) {
2210 * Names are equal; compare startBlock
2212 if (searchKey
->startBlock
== trialKey
->startBlock
) {
2215 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
2224 * hfs_buildattrkey - build an Attribute b-tree key
2227 hfs_buildattrkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
2230 size_t unicodeBytes
= 0;
2232 if (attrname
!= NULL
) {
2234 * Convert filename from UTF-8 into Unicode
2236 result
= utf8_decodestr((const u_int8_t
*)attrname
, strlen(attrname
), key
->attrName
,
2237 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
2239 if (result
!= ENAMETOOLONG
)
2240 result
= EINVAL
; /* name has invalid characters */
2243 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
2244 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
2246 key
->attrNameLen
= 0;
2247 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
2250 key
->fileID
= fileID
;
2251 key
->startBlock
= 0;
2257 * getnodecount - calculate starting node count for attributes b-tree.
2260 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
2262 u_int64_t freebytes
;
2263 u_int64_t calcbytes
;
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.
2269 * Use no more than 10 % of the remaining free space.
2271 freebytes
= (u_int64_t
)hfs_freeblks(hfsmp
, 0) * (u_int64_t
)hfsmp
->blockSize
;
2273 calcbytes
= MIN(hfsmp
->hfs_catalog_cp
->c_datafork
->ff_size
/ 5, 20 * 1024 * 1024);
2275 calcbytes
= MAX(calcbytes
, hfsmp
->hfs_catalog_cp
->c_datafork
->ff_clumpsize
);
2277 calcbytes
= MIN(calcbytes
, freebytes
/ 10);
2279 return (MAX(2, (int)(calcbytes
/ nodesize
)));
2284 * getmaxinlineattrsize - calculate maximum inline attribute size.
2286 * This yields 3,802 bytes for an 8K node size.
2289 getmaxinlineattrsize(struct vnode
* attrvp
)
2291 struct BTreeInfoRec btinfo
;
2292 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
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
));
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 */
2313 * Initialize vnode for attribute data I/O.
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
2321 * - the attrdata cnode is not locked
2324 * - returns non-zero value
2325 * - the caller does not have to worry about any locks or references
2327 int init_attrdata_vnode(struct hfsmount
*hfsmp
)
2331 struct cat_desc cat_desc
;
2332 struct cat_attr cat_attr
;
2333 struct cat_fork cat_fork
;
2334 int newvnode_flags
= 0;
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
;
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
;
2351 * The attribute data file is a virtual file that spans the
2352 * entire file system space.
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.
2359 * This approach allows the attribute data to be cached without
2360 * incurring the high cost of using a separate vnode per attribute.
2362 * Since we need to acquire the attribute b-tree file lock anyways,
2363 * the virtual file doesn't introduce any additional serialization.
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
;
2371 result
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cat_desc
, 0, &cat_attr
,
2372 &cat_fork
, &vp
, &newvnode_flags
);
2374 hfsmp
->hfs_attrdata_vp
= vp
;
2375 hfs_unlock(VTOC(vp
));
2381 * Read an extent based attribute.
2384 read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2386 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2394 hfs_lock_truncate(VTOC(evp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2396 bufsize
= (int)uio_resid(uio
);
2397 attrsize
= (int)datasize
;
2398 blksize
= (int)hfsmp
->blockSize
;
2401 * Read the attribute data one extent at a time.
2402 * For the typical case there is only one extent.
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
);
2411 result
= cluster_read(evp
, uio
, VTOF(evp
)->ff_size
, IO_SYNC
| IO_UNIT
);
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
);
2422 uio_setresid(uio
, bufsize
);
2423 uio_setoffset(uio
, datasize
);
2425 hfs_unlock_truncate(VTOC(evp
), HFS_LOCK_DEFAULT
);
2430 * Write an extent based attribute.
2433 write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2435 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2444 hfs_lock_truncate(VTOC(evp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2446 bufsize
= uio_resid(uio
);
2447 attrsize
= (int) datasize
;
2448 blksize
= (int) hfsmp
->blockSize
;
2449 filesize
= VTOF(evp
)->ff_size
;
2452 * Write the attribute data one extent at a time.
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
);
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
);
2472 uio_setresid(uio
, bufsize
);
2473 uio_setoffset(uio
, datasize
);
2475 hfs_unlock_truncate(VTOC(evp
), HFS_LOCK_DEFAULT
);
2480 * Allocate blocks for an extent based attribute.
2483 alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
)
2492 startblk
= hfsmp
->hfs_metazone_end
;
2493 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
2494 if (blkcnt
> (int)hfs_freeblks(hfsmp
, 0)) {
2498 maxextents
= extentbufsize
/ sizeof(HFSPlusExtentDescriptor
);
2500 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
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
);
2507 * If we couldn't find anything, then re-try the allocation but allow
2510 if (result
== dskFulErr
) {
2511 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, HFS_ALLOC_FLUSHTXN
,
2512 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
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
);
2521 extents
[i
].startBlock
= 0;
2522 extents
[i
].blockCount
= 0;
2525 blkcnt
-= extents
[i
].blockCount
;
2526 startblk
= extents
[i
].startBlock
+ extents
[i
].blockCount
;
2529 * If it didn't fit in the extents buffer then bail.
2534 #if HFS_XATTR_VERBOSE
2535 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt
);
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;
2546 hfs_systemfile_unlock(hfsmp
, lockflags
);
2547 return MacToVFSError(result
);
2551 * Release blocks from an extent based attribute.
2554 free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
)
2556 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2557 int remblks
= blkcnt
;
2561 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
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
);
2569 extents
[i
].blockCount
= 0;
2572 if (extents
[i
].startBlock
== 0) {
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;
2580 #if HFS_XATTR_VERBOSE
2581 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2582 extents
[i
].startBlock
, extents
[i
].blockCount
);
2584 /* Discard any resident pages for this block range. */
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
);
2594 hfs_systemfile_unlock(hfsmp
, lockflags
);
2598 has_overflow_extents(HFSPlusForkData
*forkdata
)
2602 if (forkdata
->extents
[7].blockCount
== 0)
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
;
2614 return (forkdata
->totalBlocks
> blocks
);
2618 count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
)
2623 for (i
= 0, blocks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
2624 /* Ignore obvious bogus extents. */
2625 if (extents
[i
].blockCount
> (u_int32_t
)maxblks
)
2627 if (extents
[i
].startBlock
== 0 || extents
[i
].blockCount
== 0)
2629 blocks
+= extents
[i
].blockCount
;