2 * Copyright (c) 2004-2007 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/vnode_internal.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"
49 #include "hfscommon/headers/BTreesInternal.h"
51 #define HFS_XATTR_VERBOSE 0
53 #define ATTRIBUTE_FILE_NODE_SIZE 8192
56 /* State information for the listattr_callback callback function. */
57 struct listattr_callback_state
{
64 #define HFS_MAXATTRIBUTESIZE (128 * 1024)
65 #define HFS_MAXATTRBLKS (32 * 1024)
68 /* HFS Internal Names */
69 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
70 #define XATTR_XATTREXTENTS_NAME "system.xattrextents"
72 /* Faster version if we already know this is the data fork. */
73 #define RSRC_FORK_EXISTS(CP) \
74 (((CP)->c_attr.ca_blocks - (CP)->c_datafork->ff_data.cf_blocks) > 0)
76 static u_int32_t emptyfinfo
[8] = {0};
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 (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
122 if ( !S_ISREG(cp
->c_mode
) ) {
125 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
128 if (!RSRC_FORK_EXISTS(cp
) && (ap
->a_operation
!= NS_OPEN
)) {
132 error
= hfs_vgetrsrc(VTOHFS(vp
), vp
, svpp
, TRUE
);
142 hfs_vnop_makenamedstream(struct vnop_makenamedstream_args
* ap
)
144 vnode_t vp
= ap
->a_vp
;
145 vnode_t
*svpp
= ap
->a_svpp
;
152 * We only support the "com.apple.ResourceFork" stream.
154 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
158 if ( !S_ISREG(cp
->c_mode
) ) {
161 if ((error
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
164 error
= hfs_vgetrsrc(VTOHFS(vp
), vp
, svpp
, TRUE
);
174 hfs_vnop_removenamedstream(struct vnop_removenamedstream_args
* ap
)
176 vnode_t svp
= ap
->a_svp
;
181 * We only support the "com.apple.ResourceFork" stream.
183 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
188 /* Take truncate lock before taking cnode lock. */
189 hfs_lock_truncate(scp
, TRUE
);
190 if ((error
= hfs_lock(scp
, HFS_EXCLUSIVE_LOCK
))) {
193 if (VTOF(svp
)->ff_size
!= 0) {
194 error
= hfs_truncate(svp
, 0, IO_NDELAY
, 0, ap
->a_context
);
198 hfs_unlock_truncate(scp
, TRUE
);
205 * Retrieve the data of an extended attribute.
209 hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
)
211 struct vnop_getxattr_args {
212 struct vnodeop_desc *a_desc;
218 vfs_context_t a_context;
222 struct vnode
*vp
= ap
->a_vp
;
224 struct hfsmount
*hfsmp
;
225 uio_t uio
= ap
->a_uio
;
226 struct BTreeIterator
* iterator
= NULL
;
227 struct filefork
*btfile
;
228 FSBufferDescriptor btdata
;
229 HFSPlusAttrRecord
* recp
= NULL
;
236 if (vp
== cp
->c_vp
) {
237 /* Get the Finder Info. */
238 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
239 u_int8_t finderinfo
[32];
242 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
))) {
245 /* Make a copy since we may not export all of it. */
246 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
249 /* Don't expose a symlink's private type/creator. */
250 if (vnode_islnk(vp
)) {
251 struct FndrFileInfo
*fip
;
253 fip
= (struct FndrFileInfo
*)&finderinfo
;
257 /* If Finder Info is empty then it doesn't exist. */
258 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
262 *ap
->a_size
= bufsize
;
265 if (uio_resid(uio
) < bufsize
)
268 result
= uiomove((caddr_t
)&finderinfo
, bufsize
, uio
);
272 /* Read the Resource Fork. */
273 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
274 struct vnode
*rvp
= NULL
;
276 if ( !S_ISREG(cp
->c_mode
) ) {
279 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
282 if ( !RSRC_FORK_EXISTS(cp
)) {
287 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
);
293 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
295 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
303 * Standard HFS only supports native FinderInfo and Resource Forks.
305 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
309 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
))) {
312 /* Bail if we don't have any extended attributes. */
313 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
314 (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
318 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
320 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
321 if (iterator
== NULL
) {
325 bzero(iterator
, sizeof(*iterator
));
327 bufsize
= sizeof(HFSPlusAttrData
) - 2;
329 bufsize
+= uio_resid(uio
);
330 bufsize
= MAX(bufsize
, sizeof(HFSPlusAttrRecord
));
331 MALLOC(recp
, HFSPlusAttrRecord
*, bufsize
, M_TEMP
, M_WAITOK
);
336 btdata
.bufferAddress
= recp
;
337 btdata
.itemSize
= bufsize
;
338 btdata
.itemCount
= 1;
340 result
= hfs_buildattrkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
344 /* Lookup the attribute. */
345 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
346 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
347 hfs_systemfile_unlock(hfsmp
, lockflags
);
350 if (result
== btNotFound
)
355 switch (recp
->recordType
) {
356 case kHFSPlusAttrInlineData
:
358 * Sanity check record size. It's not required to have any
359 * user data, so the minimum size is 2 bytes less that the
360 * size of HFSPlusAttrData (since HFSPlusAttrData struct
361 * has 2 bytes set aside for attribute data).
363 if (datasize
< (sizeof(HFSPlusAttrData
) - 2)) {
364 printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
365 VTOC(vp
)->c_fileid
, ap
->a_name
, datasize
, sizeof(HFSPlusAttrData
));
369 *ap
->a_size
= recp
->attrData
.attrSize
;
370 if (uio
&& recp
->attrData
.attrSize
!= 0) {
371 if (*ap
->a_size
> uio_resid(uio
))
374 result
= uiomove((caddr_t
) &recp
->attrData
.attrData
, recp
->attrData
.attrSize
, uio
);
378 case kHFSPlusAttrForkData
:
379 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
380 printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
381 VTOC(vp
)->c_fileid
, ap
->a_name
, datasize
, sizeof(HFSPlusAttrForkData
));
385 *ap
->a_size
= recp
->forkData
.theFork
.logicalSize
;
389 if (*ap
->a_size
> uio_resid(uio
)) {
393 /* Process overflow extents if necessary. */
394 if (has_overflow_extents(&recp
->forkData
.theFork
)) {
395 HFSPlusExtentDescriptor
*extentbuf
;
396 HFSPlusExtentDescriptor
*extentptr
;
397 size_t extentbufsize
;
398 u_int32_t totalblocks
;
402 totalblocks
= recp
->forkData
.theFork
.totalBlocks
;
403 /* Ignore bogus block counts. */
404 if (totalblocks
> HFS_MAXATTRBLKS
) {
408 attrlen
= recp
->forkData
.theFork
.logicalSize
;
410 /* Get a buffer to hold the worst case amount of extents. */
411 extentbufsize
= totalblocks
* sizeof(HFSPlusExtentDescriptor
);
412 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
413 MALLOC(extentbuf
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
414 if (extentbuf
== NULL
) {
418 bzero(extentbuf
, extentbufsize
);
419 extentptr
= extentbuf
;
421 /* Grab the first 8 extents. */
422 bcopy(&recp
->forkData
.theFork
.extents
[0], extentptr
, sizeof(HFSPlusExtentRecord
));
423 extentptr
+= kHFSPlusExtentDensity
;
424 blkcnt
= count_extent_blocks(totalblocks
, recp
->forkData
.theFork
.extents
);
426 /* Now lookup the overflow extents. */
427 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
428 while (blkcnt
< totalblocks
) {
429 ((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
= blkcnt
;
430 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
432 (recp
->recordType
!= kHFSPlusAttrExtents
) ||
433 (datasize
< sizeof(HFSPlusAttrExtents
))) {
434 printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
435 ap
->a_name
, blkcnt
, totalblocks
);
437 break; /* break from while */
439 /* Grab the next 8 extents. */
440 bcopy(&recp
->overflowExtents
.extents
[0], extentptr
, sizeof(HFSPlusExtentRecord
));
441 extentptr
+= kHFSPlusExtentDensity
;
442 blkcnt
+= count_extent_blocks(totalblocks
, recp
->overflowExtents
.extents
);
444 hfs_systemfile_unlock(hfsmp
, lockflags
);
446 if (blkcnt
< totalblocks
) {
449 result
= read_attr_data(hfsmp
, uio
, attrlen
, extentbuf
);
451 FREE(extentbuf
, M_TEMP
);
453 } else /* No overflow extents. */ {
454 result
= read_attr_data(hfsmp
, uio
, recp
->forkData
.theFork
.logicalSize
, recp
->forkData
.theFork
.extents
);
466 FREE(iterator
, M_TEMP
);
472 return MacToVFSError(result
);
476 * Set the data of an extended attribute.
480 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
482 struct vnop_setxattr_args {
483 struct vnodeop_desc *a_desc;
488 vfs_context_t a_context;
492 struct vnode
*vp
= ap
->a_vp
;
493 struct cnode
*cp
= NULL
;
494 struct hfsmount
*hfsmp
;
495 uio_t uio
= ap
->a_uio
;
496 struct BTreeIterator
* iterator
= NULL
;
497 struct filefork
*btfile
= NULL
;
499 FSBufferDescriptor btdata
;
500 HFSPlusAttrRecord
*recp
= NULL
;
501 HFSPlusExtentDescriptor
*extentptr
= NULL
;
502 HFSPlusAttrRecord attrdata
; /* 90 bytes */
503 void * user_data_ptr
= NULL
;
504 int started_transaction
= 0;
507 int allocatedblks
= 0;
510 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
511 return (EINVAL
); /* invalid name */
514 if (VNODE_IS_RSRC(vp
)) {
517 /* Set the Finder Info. */
518 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
519 u_int8_t finderinfo
[32];
520 struct FndrFileInfo
*fip
;
521 void * finderinfo_start
;
524 attrsize
= sizeof(VTOC(vp
)->c_finderinfo
);
526 if (uio_resid(uio
) != attrsize
) {
529 /* Grab the new Finder Info data. */
530 if ((result
= uiomove((caddr_t
)&finderinfo
, attrsize
, uio
))) {
533 fip
= (struct FndrFileInfo
*)&finderinfo
;
535 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
540 /* Symlink's don't have an external type/creator. */
541 if (vnode_islnk(vp
)) {
542 /* Skip over type/creator fields. */
543 finderinfo_start
= &cp
->c_finderinfo
[8];
546 finderinfo_start
= &cp
->c_finderinfo
[0];
548 * Don't allow the external setting of
549 * file type to kHardLinkFileType.
551 if (fip
->fdType
== SWAP_BE32(kHardLinkFileType
)) {
557 if (bcmp(finderinfo_start
, emptyfinfo
, attrsize
)) {
558 /* attr exists and "create" was specified. */
559 if (ap
->a_options
& XATTR_CREATE
) {
564 /* attr doesn't exists and "replace" was specified. */
565 if (ap
->a_options
& XATTR_REPLACE
) {
570 /* Set the cnode's Finder Info. */
571 if (attrsize
== sizeof(cp
->c_finderinfo
))
572 bcopy(&finderinfo
[0], finderinfo_start
, attrsize
);
574 bcopy(&finderinfo
[8], finderinfo_start
, attrsize
);
576 /* Updating finderInfo updates change time and modified time */
577 cp
->c_touch_chgtime
= TRUE
;
578 cp
->c_flag
|= C_MODIFIED
;
581 * Mirror the invisible bit to the UF_HIDDEN flag.
583 * The fdFlags for files and frFlags for folders are both 8 bytes
584 * into the userInfo (the first 16 bytes of the Finder Info). They
585 * are both 16-bit fields.
587 fdFlags
= *((u_int16_t
*) &cp
->c_finderinfo
[8]);
588 if (fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
589 cp
->c_flags
|= UF_HIDDEN
;
591 cp
->c_flags
&= ~UF_HIDDEN
;
593 result
= hfs_update(vp
, FALSE
);
598 /* Write the Resource Fork. */
599 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
600 struct vnode
*rvp
= NULL
;
602 if (!vnode_isreg(vp
)) {
605 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
610 if (RSRC_FORK_EXISTS(cp
)) {
611 /* attr exists and "create" was specified. */
612 if (ap
->a_options
& XATTR_CREATE
) {
617 /* attr doesn't exists and "replace" was specified. */
618 if (ap
->a_options
& XATTR_REPLACE
) {
623 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
);
628 /* VNOP_WRITE will update timestamps accordingly */
629 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
634 * Standard HFS only supports native FinderInfo and Resource Forks.
636 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
639 attrsize
= uio_resid(uio
);
641 /* Enforce an upper limit. */
642 if (attrsize
> HFS_MAXATTRIBUTESIZE
) {
647 * Attempt to copy the users attr data before taking any locks.
650 hfsmp
->hfs_max_inline_attrsize
!= 0 &&
651 attrsize
< hfsmp
->hfs_max_inline_attrsize
) {
652 MALLOC(user_data_ptr
, void *, attrsize
, M_TEMP
, M_WAITOK
);
653 if (user_data_ptr
== NULL
) {
657 result
= uiomove((caddr_t
)user_data_ptr
, attrsize
, uio
);
663 result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
669 /* Start a transaction for our changes. */
670 if (hfs_start_transaction(hfsmp
) != 0) {
674 started_transaction
= 1;
677 * Once we started the transaction, nobody can compete
678 * with us, so make sure this file is still there.
680 if (cp
->c_flag
& C_NOEXISTS
) {
686 * If there isn't an attributes b-tree then create one.
688 if (hfsmp
->hfs_attribute_vp
== NULL
) {
689 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
690 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
695 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
696 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
699 /* Take exclusive access to the attributes b-tree. */
700 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
702 /* Build the b-tree key. */
703 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
704 if (iterator
== NULL
) {
708 bzero(iterator
, sizeof(*iterator
));
709 result
= hfs_buildattrkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
714 /* Preflight for replace/create semantics. */
715 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
716 btdata
.bufferAddress
= &attrdata
;
717 btdata
.itemSize
= sizeof(attrdata
);
718 btdata
.itemCount
= 1;
719 exists
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
) == 0;
721 /* Replace requires that the attribute already exists. */
722 if ((ap
->a_options
& XATTR_REPLACE
) && !exists
) {
726 /* Create requires that the attribute doesn't exist. */
727 if ((ap
->a_options
& XATTR_CREATE
) && exists
) {
731 /* If it won't fit inline then use extent-based attributes. */
732 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
733 size_t extentbufsize
;
736 u_int32_t
*keystartblk
;
739 /* Check if volume supports extent-based attributes */
740 if ((hfsmp
->hfs_flags
& HFS_XATTR_EXTENTS
) == 0) {
745 /* Get some blocks. */
746 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
747 extentbufsize
= blkcnt
* sizeof(HFSPlusExtentDescriptor
);
748 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
749 MALLOC(extentptr
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
750 if (extentptr
== NULL
) {
754 bzero(extentptr
, extentbufsize
);
755 result
= alloc_attr_blks(hfsmp
, attrsize
, extentbufsize
, extentptr
, &allocatedblks
);
758 goto exit
; /* no more space */
760 /* Copy data into the blocks. */
761 result
= write_attr_data(hfsmp
, uio
, attrsize
, extentptr
);
763 printf("hfs_setxattr: write_attr_data err (%d) %s:%s\n",
764 result
, vnode_name(vp
) ? vnode_name(vp
) : "", ap
->a_name
);
768 /* Now remove any previous attribute. */
770 result
= remove_attribute_records(hfsmp
, iterator
);
772 printf("hfs_setxattr: remove_attribute_records err (%d) %s:%s\n",
773 result
, vnode_name(vp
) ? vnode_name(vp
) : "", ap
->a_name
);
778 /* Create attribute fork data record. */
779 MALLOC(recp
, HFSPlusAttrRecord
*, sizeof(HFSPlusAttrRecord
), M_TEMP
, M_WAITOK
);
784 btdata
.bufferAddress
= recp
;
785 btdata
.itemCount
= 1;
786 btdata
.itemSize
= sizeof(HFSPlusAttrForkData
);
788 recp
->recordType
= kHFSPlusAttrForkData
;
789 recp
->forkData
.reserved
= 0;
790 recp
->forkData
.theFork
.logicalSize
= attrsize
;
791 recp
->forkData
.theFork
.clumpSize
= 0;
792 recp
->forkData
.theFork
.totalBlocks
= blkcnt
;
793 bcopy(extentptr
, recp
->forkData
.theFork
.extents
, sizeof(HFSPlusExtentRecord
));
795 (void) hfs_buildattrkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
797 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
799 #if HFS_XATTR_VERBOSE
800 printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n",
801 MacToVFSError(result
), vnode_name(vp
) ? vnode_name(vp
) : "", ap
->a_name
);
805 extentblks
= count_extent_blocks(blkcnt
, recp
->forkData
.theFork
.extents
);
806 blkcnt
-= extentblks
;
807 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
810 /* Create overflow extents as needed. */
812 /* Initialize the key and record. */
813 *keystartblk
+= (u_int32_t
)extentblks
;
814 btdata
.itemSize
= sizeof(HFSPlusAttrExtents
);
815 recp
->recordType
= kHFSPlusAttrExtents
;
816 recp
->overflowExtents
.reserved
= 0;
818 /* Copy the next set of extents. */
819 i
+= kHFSPlusExtentDensity
;
820 bcopy(&extentptr
[i
], recp
->overflowExtents
.extents
, sizeof(HFSPlusExtentRecord
));
822 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
824 printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n",
825 MacToVFSError(result
), vnode_name(vp
) ? vnode_name(vp
) : "", ap
->a_name
);
828 extentblks
= count_extent_blocks(blkcnt
, recp
->overflowExtents
.extents
);
829 blkcnt
-= extentblks
;
831 } else /* Inline data */ {
833 result
= remove_attribute_records(hfsmp
, iterator
);
839 /* Calculate size of record rounded up to multiple of 2 bytes. */
840 btdata
.itemSize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
841 MALLOC(recp
, HFSPlusAttrRecord
*, btdata
.itemSize
, M_TEMP
, M_WAITOK
);
846 recp
->recordType
= kHFSPlusAttrInlineData
;
847 recp
->attrData
.reserved
[0] = 0;
848 recp
->attrData
.reserved
[1] = 0;
849 recp
->attrData
.attrSize
= attrsize
;
851 /* Copy in the attribute data (if any). */
854 bcopy(user_data_ptr
, &recp
->attrData
.attrData
, attrsize
);
856 result
= uiomove((caddr_t
)&recp
->attrData
.attrData
, attrsize
, uio
);
862 (void) hfs_buildattrkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
864 btdata
.bufferAddress
= recp
;
865 btdata
.itemCount
= 1;
866 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
869 if (btfile
&& started_transaction
) {
870 (void) BTFlushPath(btfile
);
873 hfs_systemfile_unlock(hfsmp
, lockflags
);
877 /* Setting an attribute only updates change time and not
878 * modified time of the file.
880 cp
->c_touch_chgtime
= TRUE
;
881 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
882 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
883 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
885 (void) hfs_update(vp
, 0);
887 if (started_transaction
) {
888 if (result
&& allocatedblks
) {
889 free_attr_blks(hfsmp
, allocatedblks
, extentptr
);
891 hfs_end_transaction(hfsmp
);
897 HFS_KNOTE(vp
, NOTE_ATTRIB
);
900 FREE(user_data_ptr
, M_TEMP
);
906 FREE(extentptr
, M_TEMP
);
909 FREE(iterator
, M_TEMP
);
911 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
915 * Remove an extended attribute.
919 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
921 struct vnop_removexattr_args {
922 struct vnodeop_desc *a_desc;
926 vfs_context_t a_context;
930 struct vnode
*vp
= ap
->a_vp
;
931 struct cnode
*cp
= VTOC(vp
);
932 struct hfsmount
*hfsmp
;
933 struct BTreeIterator
* iterator
= NULL
;
937 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
938 return (EINVAL
); /* invalid name */
941 if (VNODE_IS_RSRC(vp
)) {
945 /* If Resource Fork is non-empty then truncate it. */
946 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
947 struct vnode
*rvp
= NULL
;
949 if ( !vnode_isreg(vp
) ) {
952 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
955 if ( !RSRC_FORK_EXISTS(cp
)) {
959 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
);
965 hfs_lock_truncate(VTOC(rvp
), TRUE
);
966 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
))) {
967 hfs_unlock_truncate(cp
, TRUE
);
972 /* Start a transaction for encapsulating changes in
973 * hfs_truncate() and hfs_update()
975 if ((result
= hfs_start_transaction(hfsmp
))) {
976 hfs_unlock_truncate(cp
, TRUE
);
982 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, ap
->a_context
);
984 cp
->c_touch_chgtime
= TRUE
;
985 cp
->c_flag
|= C_MODIFIED
;
986 result
= hfs_update(vp
, FALSE
);
989 hfs_end_transaction(hfsmp
);
990 hfs_unlock_truncate(VTOC(rvp
), TRUE
);
991 hfs_unlock(VTOC(rvp
));
996 /* Clear out the Finder Info. */
997 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
998 void * finderinfo_start
;
1001 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
1005 /* Symlink's don't have an external type/creator. */
1006 if (vnode_islnk(vp
)) {
1007 /* Skip over type/creator fields. */
1008 finderinfo_start
= &cp
->c_finderinfo
[8];
1009 finderinfo_size
= sizeof(cp
->c_finderinfo
) - 8;
1011 finderinfo_start
= &cp
->c_finderinfo
[0];
1012 finderinfo_size
= sizeof(cp
->c_finderinfo
);
1014 if (bcmp(finderinfo_start
, emptyfinfo
, finderinfo_size
) == 0) {
1019 bzero(finderinfo_start
, finderinfo_size
);
1021 /* Updating finderInfo updates change time and modified time */
1022 cp
->c_touch_chgtime
= TRUE
;
1023 cp
->c_flag
|= C_MODIFIED
;
1024 hfs_update(vp
, FALSE
);
1031 * Standard HFS only supports native FinderInfo and Resource Forks.
1033 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1036 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1040 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1041 if (iterator
== NULL
) {
1044 bzero(iterator
, sizeof(*iterator
));
1046 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
1050 result
= hfs_buildattrkey(cp
->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1055 if (hfs_start_transaction(hfsmp
) != 0) {
1059 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1061 result
= remove_attribute_records(hfsmp
, iterator
);
1063 hfs_systemfile_unlock(hfsmp
, lockflags
);
1066 cp
->c_touch_chgtime
= TRUE
;
1068 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1070 /* If no more attributes exist, clear attribute bit */
1071 result
= file_attribute_exist(hfsmp
, cp
->c_fileid
);
1073 cp
->c_attr
.ca_recflags
&= ~kHFSHasAttributesMask
;
1075 if (result
== EEXIST
) {
1079 hfs_systemfile_unlock(hfsmp
, lockflags
);
1081 /* If ACL was removed, clear security bit */
1082 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1083 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
1085 (void) hfs_update(vp
, 0);
1088 hfs_end_transaction(hfsmp
);
1092 HFS_KNOTE(vp
, NOTE_ATTRIB
);
1094 FREE(iterator
, M_TEMP
);
1095 return MacToVFSError(result
);
1098 /* Check if any attribute record exist for given fileID. This function
1099 * is called by hfs_vnop_removexattr to determine if it should clear the
1100 * attribute bit in the catalog record or not.
1102 * Note - you must acquire a shared lock on the attribute btree before
1103 * calling this function.
1106 * EEXIST - If attribute record was found
1107 * 0 - Attribute was not found
1108 * (other) - Other error (such as EIO)
1111 file_attribute_exist(struct hfsmount
*hfsmp
, uint32_t fileID
)
1113 HFSPlusAttrKey
*key
;
1114 struct BTreeIterator
* iterator
= NULL
;
1115 struct filefork
*btfile
;
1118 // if there's no attribute b-tree we sure as heck
1119 // can't have any attributes!
1120 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1124 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1125 if (iterator
== NULL
) {
1129 bzero(iterator
, sizeof(*iterator
));
1130 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1132 result
= hfs_buildattrkey(fileID
, NULL
, key
);
1137 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1138 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1139 if (result
&& (result
!= btNotFound
)) {
1143 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1144 /* If no next record was found or fileID for next record did not match,
1145 * no more attributes exist for this fileID
1147 if ((result
&& (result
== btNotFound
)) || (key
->fileID
!= fileID
)) {
1155 FREE(iterator
, M_TEMP
);
1162 * Remove all the records for a given attribute.
1164 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1165 * - A transaction must have been started.
1166 * - The Attribute b-tree file must be locked exclusive.
1167 * - The Allocation Bitmap file must be locked exclusive.
1168 * - The iterator key must be initialized.
1171 remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
)
1173 struct filefork
*btfile
;
1174 FSBufferDescriptor btdata
;
1175 HFSPlusAttrRecord attrdata
; /* 90 bytes */
1179 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1181 btdata
.bufferAddress
= &attrdata
;
1182 btdata
.itemSize
= sizeof(attrdata
);
1183 btdata
.itemCount
= 1;
1184 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1186 goto exit
; /* no records. */
1189 * Free the blocks from extent based attributes.
1191 * Note that the block references (btree records) are removed
1192 * before releasing the blocks in the allocation bitmap.
1194 if (attrdata
.recordType
== kHFSPlusAttrForkData
) {
1197 u_int32_t
*keystartblk
;
1199 #if HFS_XATTR_VERBOSE
1200 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
1201 printf("remove_attribute_records: bad record size %d (expecting %d)\n", datasize
, sizeof(HFSPlusAttrForkData
));
1204 totalblks
= attrdata
.forkData
.theFork
.totalBlocks
;
1206 /* Process the first 8 extents. */
1207 extentblks
= count_extent_blocks(totalblks
, attrdata
.forkData
.theFork
.extents
);
1208 if (extentblks
> totalblks
)
1209 panic("remove_attribute_records: corruption...");
1210 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1211 free_attr_blks(hfsmp
, extentblks
, attrdata
.forkData
.theFork
.extents
);
1213 totalblks
-= extentblks
;
1214 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1216 /* Process any overflow extents. */
1218 *keystartblk
+= (u_int32_t
)extentblks
;
1220 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1222 (attrdata
.recordType
!= kHFSPlusAttrExtents
) ||
1223 (datasize
< sizeof(HFSPlusAttrExtents
))) {
1224 printf("remove_attribute_records: BTSearchRecord %d (%d), totalblks %d\n",
1225 MacToVFSError(result
), attrdata
.recordType
!= kHFSPlusAttrExtents
, totalblks
);
1227 break; /* break from while */
1229 /* Process the next 8 extents. */
1230 extentblks
= count_extent_blocks(totalblks
, attrdata
.overflowExtents
.extents
);
1231 if (extentblks
> totalblks
)
1232 panic("remove_attribute_records: corruption...");
1233 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1234 free_attr_blks(hfsmp
, extentblks
, attrdata
.overflowExtents
.extents
);
1236 totalblks
-= extentblks
;
1239 result
= BTDeleteRecord(btfile
, iterator
);
1241 (void) BTFlushPath(btfile
);
1243 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
1248 * Retrieve the list of extended attribute names.
1252 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
1254 struct vnop_listxattr_args {
1255 struct vnodeop_desc *a_desc;
1260 vfs_context_t a_context;
1263 struct vnode
*vp
= ap
->a_vp
;
1264 struct cnode
*cp
= VTOC(vp
);
1265 struct hfsmount
*hfsmp
;
1266 uio_t uio
= ap
->a_uio
;
1267 struct BTreeIterator
* iterator
= NULL
;
1268 struct filefork
*btfile
;
1269 struct listattr_callback_state state
;
1270 void * finderinfo_start
;
1271 int finderinfo_size
;
1272 user_addr_t user_start
= 0;
1273 user_size_t user_len
= 0;
1277 if (VNODE_IS_RSRC(vp
)) {
1283 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
))) {
1287 /* Don't expose a symlink's private type/creator. */
1288 if (vnode_islnk(vp
)) {
1289 /* Skip over type/creator fields. */
1290 finderinfo_start
= &cp
->c_finderinfo
[8];
1291 finderinfo_size
= sizeof(cp
->c_finderinfo
) - 8;
1293 finderinfo_start
= &cp
->c_finderinfo
[0];
1294 finderinfo_size
= sizeof(cp
->c_finderinfo
);
1296 /* If Finder Info is non-empty then export it's name. */
1297 if (bcmp(finderinfo_start
, emptyfinfo
, finderinfo_size
) != 0) {
1299 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
1300 } else if (uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1304 result
= uiomove(XATTR_FINDERINFO_NAME
,
1305 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1310 /* If Resource Fork is non-empty then export it's name. */
1311 if (S_ISREG(cp
->c_mode
) && RSRC_FORK_EXISTS(cp
)) {
1313 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1314 } else if (uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1318 result
= uiomove(XATTR_RESOURCEFORK_NAME
,
1319 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
1325 * Standard HFS only supports native FinderInfo and Resource Forks.
1326 * Return at this point.
1328 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1332 /* Bail if we don't have any extended attributes. */
1333 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
1334 (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
1338 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1340 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1341 if (iterator
== NULL
) {
1345 bzero(iterator
, sizeof(*iterator
));
1346 result
= hfs_buildattrkey(cp
->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
1351 * Lock the user's buffer here so that we won't fault on
1352 * it in uiomove while holding the attributes b-tree lock.
1354 if (uio
&& uio_isuserspace(uio
)) {
1355 user_start
= uio_curriovbase(uio
);
1356 user_len
= uio_curriovlen(uio
);
1358 if ((result
= vslock(user_start
, user_len
)) != 0) {
1363 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1365 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1366 if (result
&& result
!= btNotFound
) {
1367 hfs_systemfile_unlock(hfsmp
, lockflags
);
1371 state
.fileID
= cp
->c_fileid
;
1377 * Process entries starting just after iterator->key.
1379 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
1380 (IterateCallBackProcPtr
)listattr_callback
, &state
);
1381 hfs_systemfile_unlock(hfsmp
, lockflags
);
1383 *ap
->a_size
+= state
.size
;
1386 if (state
.result
|| result
== btNotFound
)
1387 result
= state
.result
;
1391 vsunlock(user_start
, user_len
, TRUE
);
1393 FREE(iterator
, M_TEMP
);
1397 return MacToVFSError(result
);
1402 * Callback - called for each attribute
1405 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
1407 char attrname
[XATTR_MAXNAMELEN
+ 1];
1411 if (state
->fileID
!= key
->fileID
) {
1413 return (0); /* stop */
1416 * Skip over non-primary keys
1418 if (key
->startBlock
!= 0) {
1419 return (1); /* continue */
1422 /* Convert the attribute name into UTF-8. */
1423 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
1424 (u_int8_t
*)attrname
, &bytecount
, sizeof(attrname
), '/', 0);
1426 state
->result
= result
;
1427 return (0); /* stop */
1429 bytecount
++; /* account for null termination char */
1431 if (xattr_protected(attrname
))
1432 return (1); /* continue */
1434 if (state
->uio
== NULL
) {
1435 state
->size
+= bytecount
;
1437 if (bytecount
> uio_resid(state
->uio
)) {
1438 state
->result
= ERANGE
;
1439 return (0); /* stop */
1441 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
1443 state
->result
= result
;
1444 return (0); /* stop */
1447 return (1); /* continue */
1451 * Remove all the attributes from a cnode.
1453 * This function creates/ends its own transaction so that each
1454 * attribute is deleted in its own transaction (to avoid having
1455 * a transaction grow too large).
1457 * This function takes the necessary locks on the attribute
1458 * b-tree file and the allocation (bitmap) file.
1462 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
)
1464 BTreeIterator
*iterator
;
1465 HFSPlusAttrKey
*key
;
1466 struct filefork
*btfile
;
1467 int result
, lockflags
;
1469 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1472 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1474 MALLOC(iterator
, BTreeIterator
*, sizeof(BTreeIterator
), M_TEMP
, M_WAITOK
);
1475 bzero(iterator
, sizeof(BTreeIterator
));
1476 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1478 /* Loop until there are no more attributes for this file id */
1480 if (hfs_start_transaction(hfsmp
) != 0) {
1485 /* Lock the attribute b-tree and the allocation (bitmap) files */
1486 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1489 * Go to first possible attribute key/record pair
1491 (void) hfs_buildattrkey(fileid
, NULL
, key
);
1492 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1493 if (result
|| key
->fileID
!= fileid
) {
1494 hfs_systemfile_unlock(hfsmp
, lockflags
);
1495 hfs_end_transaction(hfsmp
);
1498 result
= remove_attribute_records(hfsmp
, iterator
);
1500 #if HFS_XATTR_VERBOSE
1502 printf("hfs_removeallattr: unexpected err %d\n", result
);
1505 hfs_systemfile_unlock(hfsmp
, lockflags
);
1506 hfs_end_transaction(hfsmp
);
1511 FREE(iterator
, M_TEMP
);
1512 return (result
== btNotFound
? 0: MacToVFSError(result
));
1517 hfs_xattr_init(struct hfsmount
* hfsmp
)
1520 * If there isn't an attributes b-tree then create one.
1522 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
) &&
1523 (hfsmp
->hfs_attribute_vp
== NULL
) &&
1524 !(hfsmp
->hfs_flags
& HFS_READ_ONLY
)) {
1525 (void) hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
1526 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
1528 if (hfsmp
->hfs_attribute_vp
)
1529 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
1533 * Enable/Disable volume attributes stored as EA for root file system.
1534 * Supported attributes are -
1536 * 2. Extent-based Extended Attributes
1540 hfs_set_volxattr(struct hfsmount
*hfsmp
, unsigned int xattrtype
, int state
)
1542 struct BTreeIterator
* iterator
= NULL
;
1543 struct filefork
*btfile
;
1547 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1552 * If there isn't an attributes b-tree then create one.
1554 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1555 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
1556 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
1562 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1563 if (iterator
== NULL
) {
1566 bzero(iterator
, sizeof(*iterator
));
1569 * Build a b-tree key.
1570 * We use the root's parent id (1) to hold this volume attribute.
1572 if (xattrtype
== HFS_SETACLSTATE
) {
1574 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
1575 (HFSPlusAttrKey
*)&iterator
->key
);
1576 } else if (xattrtype
== HFS_SET_XATTREXTENTS_STATE
) {
1577 /* Extent-based extended attributes */
1578 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_XATTREXTENTS_NAME
,
1579 (HFSPlusAttrKey
*)&iterator
->key
);
1585 /* Start a transaction for our changes. */
1586 if (hfs_start_transaction(hfsmp
) != 0) {
1590 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1592 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1595 /* Remove the attribute. */
1596 result
= BTDeleteRecord(btfile
, iterator
);
1597 if (result
== btNotFound
)
1600 FSBufferDescriptor btdata
;
1601 HFSPlusAttrData attrdata
;
1604 datasize
= sizeof(attrdata
);
1605 btdata
.bufferAddress
= &attrdata
;
1606 btdata
.itemSize
= datasize
;
1607 btdata
.itemCount
= 1;
1608 attrdata
.recordType
= kHFSPlusAttrInlineData
;
1609 attrdata
.reserved
[0] = 0;
1610 attrdata
.reserved
[1] = 0;
1611 attrdata
.attrSize
= 2;
1612 attrdata
.attrData
[0] = 0;
1613 attrdata
.attrData
[1] = 0;
1615 /* Insert the attribute. */
1616 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
1617 if (result
== btExists
)
1620 (void) BTFlushPath(btfile
);
1622 hfs_systemfile_unlock(hfsmp
, lockflags
);
1624 /* Finish the transaction of our changes. */
1625 hfs_end_transaction(hfsmp
);
1628 FREE(iterator
, M_TEMP
);
1631 if (xattrtype
== HFS_SETACLSTATE
) {
1633 vfs_clearextendedsecurity(HFSTOVFS(hfsmp
));
1635 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
1638 /* HFS_SET_XATTREXTENTS_STATE */
1639 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1641 hfsmp
->hfs_flags
&= ~HFS_XATTR_EXTENTS
;
1643 hfsmp
->hfs_flags
|= HFS_XATTR_EXTENTS
;
1645 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1649 return MacToVFSError(result
);
1654 * Check for volume attributes stored as EA for root file system.
1655 * Supported attributes are -
1657 * 2. Extent-based Extended Attributes
1661 hfs_check_volxattr(struct hfsmount
*hfsmp
, unsigned int xattrtype
)
1663 struct BTreeIterator
* iterator
;
1664 struct filefork
*btfile
;
1668 if (hfsmp
->hfs_flags
& HFS_STANDARD
||
1669 hfsmp
->hfs_attribute_vp
== NULL
) {
1673 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1674 if (iterator
== NULL
) {
1677 bzero(iterator
, sizeof(*iterator
));
1680 * Build a b-tree key.
1681 * We use the root's parent id (1) to hold this volume attribute.
1683 if (xattrtype
== HFS_SETACLSTATE
) {
1685 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
1686 (HFSPlusAttrKey
*)&iterator
->key
);
1688 /* Extent-based extended attributes */
1689 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_XATTREXTENTS_NAME
,
1690 (HFSPlusAttrKey
*)&iterator
->key
);
1692 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1694 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1696 /* Check for our attribute. */
1697 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1699 hfs_systemfile_unlock(hfsmp
, lockflags
);
1700 FREE(iterator
, M_TEMP
);
1703 if (xattrtype
== HFS_SETACLSTATE
) {
1704 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
1706 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1707 hfsmp
->hfs_flags
|= HFS_XATTR_EXTENTS
;
1708 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1715 * hfs_attrkeycompare - compare two attribute b-tree keys.
1717 * The name portion of the key is compared using a 16-bit binary comparison.
1718 * This is called from the b-tree code.
1722 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
1724 u_int32_t searchFileID
, trialFileID
;
1727 searchFileID
= searchKey
->fileID
;
1728 trialFileID
= trialKey
->fileID
;
1731 if (searchFileID
> trialFileID
) {
1733 } else if (searchFileID
< trialFileID
) {
1736 u_int16_t
* str1
= &searchKey
->attrName
[0];
1737 u_int16_t
* str2
= &trialKey
->attrName
[0];
1738 int length1
= searchKey
->attrNameLen
;
1739 int length2
= trialKey
->attrNameLen
;
1743 if (length1
< length2
) {
1746 } else if (length1
> length2
) {
1769 * Names are equal; compare startBlock
1771 if (searchKey
->startBlock
== trialKey
->startBlock
)
1774 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
1782 * hfs_buildattrkey - build an Attribute b-tree key
1786 hfs_buildattrkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
1789 size_t unicodeBytes
= 0;
1791 if (attrname
!= NULL
) {
1793 * Convert filename from UTF-8 into Unicode
1795 result
= utf8_decodestr((const u_int8_t
*)attrname
, strlen(attrname
), key
->attrName
,
1796 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
1798 if (result
!= ENAMETOOLONG
)
1799 result
= EINVAL
; /* name has invalid characters */
1802 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
1803 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
1805 key
->attrNameLen
= 0;
1806 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
1809 key
->fileID
= fileID
;
1810 key
->startBlock
= 0;
1816 * getnodecount - calculate starting node count for attributes b-tree.
1819 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
1821 u_int64_t freebytes
;
1822 u_int64_t calcbytes
;
1825 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
1826 * 10.5: Attempt to be as big as the catalog clump size.
1828 * Use no more than 10 % of the remaining free space.
1830 freebytes
= (u_int64_t
)hfs_freeblks(hfsmp
, 0) * (u_int64_t
)hfsmp
->blockSize
;
1832 calcbytes
= MIN(hfsmp
->hfs_catalog_cp
->c_datafork
->ff_size
/ 5, 20 * 1024 * 1024);
1834 calcbytes
= MAX(calcbytes
, hfsmp
->hfs_catalog_cp
->c_datafork
->ff_clumpsize
);
1836 calcbytes
= MIN(calcbytes
, freebytes
/ 10);
1838 return (MAX(2, (int)(calcbytes
/ nodesize
)));
1843 * getmaxinlineattrsize - calculate maximum inline attribute size.
1845 * This yields 3,802 bytes for an 8K node size.
1848 getmaxinlineattrsize(struct vnode
* attrvp
)
1850 struct BTreeInfoRec btinfo
;
1851 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
1854 if (attrvp
!= NULL
) {
1855 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
);
1856 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
1857 nodesize
= btinfo
.nodeSize
;
1858 hfs_unlock(VTOC(attrvp
));
1861 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
1862 maxsize
-= 3 * sizeof(u_int16_t
); /* minus 3 index slots */
1863 maxsize
/= 2; /* 2 key/rec pairs minumum */
1864 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
1865 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
1866 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */
1872 * Get a referenced vnode for attribute data I/O.
1875 get_attr_data_vnode(struct hfsmount
*hfsmp
, vnode_t
*vpp
)
1880 vp
= hfsmp
->hfs_attrdata_vp
;
1882 struct cat_desc cat_desc
;
1883 struct cat_attr cat_attr
;
1884 struct cat_fork cat_fork
;
1886 /* We don't tag it as a system file since we intend to use cluster I/O. */
1887 bzero(&cat_desc
, sizeof(cat_desc
));
1888 cat_desc
.cd_parentcnid
= kHFSRootParentID
;
1889 cat_desc
.cd_nameptr
= (const u_int8_t
*)hfs_attrdatafilename
;
1890 cat_desc
.cd_namelen
= strlen(hfs_attrdatafilename
);
1891 cat_desc
.cd_cnid
= kHFSAttributeDataFileID
;
1893 bzero(&cat_attr
, sizeof(cat_attr
));
1894 cat_attr
.ca_linkcount
= 1;
1895 cat_attr
.ca_mode
= S_IFREG
;
1896 cat_attr
.ca_fileid
= cat_desc
.cd_cnid
;
1897 cat_attr
.ca_blocks
= hfsmp
->totalBlocks
;
1900 * The attribute data file is a virtual file that spans the
1901 * entire file system space.
1903 * Each extent-based attribute occupies a unique portion of
1904 * in this virtual file. The cluster I/O is done using actual
1905 * allocation block offsets so no additional mapping is needed
1906 * for the VNOP_BLOCKMAP call.
1908 * This approach allows the attribute data to be cached without
1909 * incurring the high cost of using a separate vnode per attribute.
1911 * Since we need to acquire the attribute b-tree file lock anyways,
1912 * the virtual file doesn't introduce any additional serialization.
1914 bzero(&cat_fork
, sizeof(cat_fork
));
1915 cat_fork
.cf_size
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
1916 cat_fork
.cf_blocks
= hfsmp
->totalBlocks
;
1917 cat_fork
.cf_extents
[0].startBlock
= 0;
1918 cat_fork
.cf_extents
[0].blockCount
= cat_fork
.cf_blocks
;
1920 result
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cat_desc
, 0, &cat_attr
, &cat_fork
, &vp
);
1922 HFS_MOUNT_LOCK(hfsmp
, 1);
1923 /* Check if someone raced us for creating this vnode. */
1924 if (hfsmp
->hfs_attrdata_vp
!= NULLVP
) {
1925 HFS_MOUNT_UNLOCK(hfsmp
, 1);
1928 vp
= hfsmp
->hfs_attrdata_vp
;
1930 hfsmp
->hfs_attrdata_vp
= vp
;
1931 HFS_MOUNT_UNLOCK(hfsmp
, 1);
1932 /* Keep a reference on this vnode until unmount */
1933 vnode_ref_ext(vp
, O_EVTONLY
);
1934 hfs_unlock(VTOC(vp
));
1938 if ((result
= vnode_get(vp
)))
1946 * Read an extent based attribute.
1949 read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
1951 vnode_t evp
= NULLVP
;
1959 if ((result
= get_attr_data_vnode(hfsmp
, &evp
))) {
1962 hfs_lock_truncate(VTOC(evp
), 0);
1964 bufsize
= (int)uio_resid(uio
);
1965 attrsize
= (int)datasize
;
1966 blksize
= (int)hfsmp
->blockSize
;
1969 * Read the attribute data one extent at a time.
1970 * For the typical case there is only one extent.
1972 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
1973 iosize
= (int)extents
[i
].blockCount
* blksize
;
1974 iosize
= MIN(iosize
, attrsize
);
1975 iosize
= MIN(iosize
, bufsize
);
1976 uio_setresid(uio
, iosize
);
1977 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
1979 result
= cluster_read(evp
, uio
, VTOF(evp
)->ff_size
, IO_SYNC
| IO_UNIT
);
1981 #if HFS_XATTR_VERBOSE
1982 printf("read_attr_data: cr iosize %d [%d, %d] (%d)\n",
1983 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
1990 uio_setresid(uio
, bufsize
);
1991 uio_setoffset(uio
, datasize
);
1993 hfs_unlock_truncate(VTOC(evp
), 0);
1999 * Write an extent based attribute.
2002 write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2004 vnode_t evp
= NULLVP
;
2013 /* Get exclusive use of attribute data vnode. */
2014 if ((result
= get_attr_data_vnode(hfsmp
, &evp
))) {
2017 hfs_lock_truncate(VTOC(evp
), 0);
2019 bufsize
= uio_resid(uio
);
2020 attrsize
= (int) datasize
;
2021 blksize
= (int) hfsmp
->blockSize
;
2022 filesize
= VTOF(evp
)->ff_size
;
2025 * Write the attribute data one extent at a time.
2027 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2028 iosize
= (int)extents
[i
].blockCount
* blksize
;
2029 iosize
= MIN(iosize
, attrsize
);
2030 iosize
= MIN(iosize
, bufsize
);
2031 uio_setresid(uio
, iosize
);
2032 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2034 result
= cluster_write(evp
, uio
, filesize
, filesize
, filesize
,
2035 (off_t
) 0, IO_SYNC
| IO_UNIT
);
2036 #if HFS_XATTR_VERBOSE
2037 printf("write_attr_data: cw iosize %d [%d, %d] (%d)\n",
2038 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2045 uio_setresid(uio
, bufsize
);
2046 uio_setoffset(uio
, datasize
);
2048 hfs_unlock_truncate(VTOC(evp
), 0);
2054 * Allocate blocks for an extent based attribute.
2057 alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
)
2066 startblk
= hfsmp
->hfs_metazone_end
;
2067 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
2068 if (blkcnt
> (int)hfs_freeblks(hfsmp
, 0)) {
2072 maxextents
= extentbufsize
/ sizeof(HFSPlusExtentDescriptor
);
2074 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2076 for (i
= 0; (blkcnt
> 0) && (i
< maxextents
); i
++) {
2077 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, 0, 0,
2078 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
2079 #if HFS_XATTR_VERBOSE
2080 printf("alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2081 blkcnt
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2084 extents
[i
].startBlock
= 0;
2085 extents
[i
].blockCount
= 0;
2088 blkcnt
-= extents
[i
].blockCount
;
2089 startblk
= extents
[i
].startBlock
+ extents
[i
].blockCount
;
2092 * If it didn't fit in the extents buffer then bail.
2097 #if HFS_XATTR_VERBOSE
2098 printf("alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt
);
2100 for (; i
<= 0; i
--) {
2101 if ((blkcnt
= extents
[i
].blockCount
) != 0) {
2102 (void) BlockDeallocate(hfsmp
, extents
[i
].startBlock
, blkcnt
);
2103 extents
[i
].startBlock
= 0;
2104 extents
[i
].blockCount
= 0;
2109 hfs_systemfile_unlock(hfsmp
, lockflags
);
2110 return MacToVFSError(result
);
2114 * Release blocks from an extent based attribute.
2117 free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
)
2119 vnode_t evp
= NULLVP
;
2120 int remblks
= blkcnt
;
2124 if (get_attr_data_vnode(hfsmp
, &evp
) != 0) {
2127 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2129 for (i
= 0; (remblks
> 0) && (extents
[i
].blockCount
!= 0); i
++) {
2130 if (extents
[i
].blockCount
> (u_int32_t
)blkcnt
) {
2131 #if HFS_XATTR_VERBOSE
2132 printf("free_attr_blks: skipping bad extent [%d, %d]\n",
2133 extents
[i
].startBlock
, extents
[i
].blockCount
);
2135 extents
[i
].blockCount
= 0;
2138 if (extents
[i
].startBlock
== 0) {
2141 (void)BlockDeallocate(hfsmp
, extents
[i
].startBlock
, extents
[i
].blockCount
);
2142 extents
[i
].startBlock
= 0;
2143 extents
[i
].blockCount
= 0;
2144 remblks
-= extents
[i
].blockCount
;
2146 #if HFS_XATTR_VERBOSE
2147 printf("free_attr_blks: BlockDeallocate [%d, %d]\n",
2148 extents
[i
].startBlock
, extents
[i
].blockCount
);
2150 /* Discard any resident pages for this block range. */
2154 start
= (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)hfsmp
->blockSize
;
2155 end
= start
+ (u_int64_t
)extents
[i
].blockCount
* (u_int64_t
)hfsmp
->blockSize
;
2156 (void) ubc_msync(hfsmp
->hfs_attrdata_vp
, start
, end
, &start
, UBC_INVALIDATE
);
2160 hfs_systemfile_unlock(hfsmp
, lockflags
);
2167 has_overflow_extents(HFSPlusForkData
*forkdata
)
2171 if (forkdata
->extents
[7].blockCount
== 0)
2174 blocks
= forkdata
->extents
[0].blockCount
+
2175 forkdata
->extents
[1].blockCount
+
2176 forkdata
->extents
[2].blockCount
+
2177 forkdata
->extents
[3].blockCount
+
2178 forkdata
->extents
[4].blockCount
+
2179 forkdata
->extents
[5].blockCount
+
2180 forkdata
->extents
[6].blockCount
+
2181 forkdata
->extents
[7].blockCount
;
2183 return (forkdata
->totalBlocks
> blocks
);
2187 count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
)
2192 for (i
= 0, blocks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
2193 /* Ignore obvious bogus extents. */
2194 if (extents
[i
].blockCount
> (u_int32_t
)maxblks
)
2196 if (extents
[i
].startBlock
== 0 || extents
[i
].blockCount
== 0)
2198 blocks
+= extents
[i
].blockCount
;