2 * Copyright (c) 2004-2008 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
{
66 #endif /* HFS_COMPRESSION */
69 #define HFS_MAXATTRIBUTESIZE (128 * 1024)
70 #define HFS_MAXATTRBLKS (32 * 1024)
73 /* HFS Internal Names */
74 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
75 #define XATTR_XATTREXTENTS_NAME "system.xattrextents"
77 /* Faster version if we already know this is the data fork. */
78 #define RSRC_FORK_EXISTS(CP) \
79 (((CP)->c_attr.ca_blocks - (CP)->c_datafork->ff_data.cf_blocks) > 0)
81 static u_int32_t emptyfinfo
[8] = {0};
83 const char hfs_attrdatafilename
[] = "Attribute Data";
85 static int listattr_callback(const HFSPlusAttrKey
*key
, const HFSPlusAttrData
*data
,
86 struct listattr_callback_state
*state
);
88 static int remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
);
90 static int getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
);
92 static size_t getmaxinlineattrsize(struct vnode
* attrvp
);
94 static int read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
);
96 static int write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
);
98 static int alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
);
100 static void free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
);
102 static int has_overflow_extents(HFSPlusForkData
*forkdata
);
104 static int count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
);
108 * Obtain the vnode for a stream.
111 hfs_vnop_getnamedstream(struct vnop_getnamedstream_args
* ap
)
113 vnode_t vp
= ap
->a_vp
;
114 vnode_t
*svpp
= ap
->a_svpp
;
121 * We only support the "com.apple.ResourceFork" stream.
123 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
127 if ( !S_ISREG(cp
->c_mode
) ) {
131 int hide_rsrc
= hfs_hides_rsrc(ap
->a_context
, VTOC(vp
), 1);
132 #endif /* HFS_COMPRESSION */
133 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
136 if ((!RSRC_FORK_EXISTS(cp
)
139 #endif /* HFS_COMPRESSION */
140 ) && (ap
->a_operation
!= NS_OPEN
)) {
144 error
= hfs_vgetrsrc(VTOHFS(vp
), vp
, svpp
, TRUE
, FALSE
);
154 hfs_vnop_makenamedstream(struct vnop_makenamedstream_args
* ap
)
156 vnode_t vp
= ap
->a_vp
;
157 vnode_t
*svpp
= ap
->a_svpp
;
164 * We only support the "com.apple.ResourceFork" stream.
166 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
170 if ( !S_ISREG(cp
->c_mode
) ) {
174 if (hfs_hides_rsrc(ap
->a_context
, VTOC(vp
), 1)) {
175 if (VNODE_IS_RSRC(vp
)) {
178 error
= decmpfs_decompress_file(vp
, VTOCMP(vp
), -1, 1, 0);
183 #endif /* HFS_COMPRESSION */
184 if ((error
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
187 error
= hfs_vgetrsrc(VTOHFS(vp
), vp
, svpp
, TRUE
, FALSE
);
197 hfs_vnop_removenamedstream(struct vnop_removenamedstream_args
* ap
)
199 vnode_t svp
= ap
->a_svp
;
204 * We only support the "com.apple.ResourceFork" stream.
206 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
210 if (hfs_hides_rsrc(ap
->a_context
, VTOC(svp
), 1)) {
214 #endif /* HFS_COMPRESSION */
218 /* Take truncate lock before taking cnode lock. */
219 hfs_lock_truncate(scp
, TRUE
);
220 if ((error
= hfs_lock(scp
, HFS_EXCLUSIVE_LOCK
))) {
223 if (VTOF(svp
)->ff_size
!= 0) {
224 error
= hfs_truncate(svp
, 0, IO_NDELAY
, 0, 0, ap
->a_context
);
228 hfs_unlock_truncate(scp
, TRUE
);
235 * Retrieve the data of an extended attribute.
239 hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
)
241 struct vnop_getxattr_args {
242 struct vnodeop_desc *a_desc;
248 vfs_context_t a_context;
252 struct vnode
*vp
= ap
->a_vp
;
254 struct hfsmount
*hfsmp
;
255 uio_t uio
= ap
->a_uio
;
256 struct BTreeIterator
* iterator
= NULL
;
257 struct filefork
*btfile
;
258 FSBufferDescriptor btdata
;
259 HFSPlusAttrRecord
* recp
= NULL
;
266 if (vp
== cp
->c_vp
) {
268 int decmpfs_hide
= hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1); /* 1 == don't take the cnode lock */
269 if (decmpfs_hide
&& !(ap
->a_options
& XATTR_SHOWCOMPRESSION
))
271 #endif /* HFS_COMPRESSION */
273 /* Get the Finder Info. */
274 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
275 u_int8_t finderinfo
[32];
278 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
))) {
281 /* Make a copy since we may not export all of it. */
282 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
285 /* Don't expose a symlink's private type/creator. */
286 if (vnode_islnk(vp
)) {
287 struct FndrFileInfo
*fip
;
289 fip
= (struct FndrFileInfo
*)&finderinfo
;
293 /* If Finder Info is empty then it doesn't exist. */
294 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
298 *ap
->a_size
= bufsize
;
301 if ((user_size_t
)uio_resid(uio
) < bufsize
)
304 result
= uiomove((caddr_t
)&finderinfo
, bufsize
, uio
);
308 /* Read the Resource Fork. */
309 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
310 struct vnode
*rvp
= NULL
;
311 int openunlinked
= 0;
314 if ( !S_ISREG(cp
->c_mode
) ) {
317 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
320 namelen
= cp
->c_desc
.cd_namelen
;
322 if ( !RSRC_FORK_EXISTS(cp
)) {
327 if ((cp
->c_flag
& C_DELETED
) && (namelen
== 0)) {
331 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
, FALSE
);
337 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
340 user_ssize_t uio_size
= 0;
342 uio_size
= uio_resid(uio
);
343 #endif /* HFS_COMPRESSION */
344 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
348 (uio_resid(uio
) == uio_size
)) {
350 we intentionally make the above call to VNOP_READ so that
351 it can return an authorization/permission/etc. error
352 based on ap->a_context and thus deny this operation;
353 in that case, result != 0 and we won't proceed
355 however, if result == 0, it will have returned no data
356 because hfs_vnop_read hid the resource fork
357 (hence uio_resid(uio) == uio_size, i.e. the uio is untouched)
359 in that case, we try again with the decmpfs_ctx context
360 to get the actual data
362 result
= VNOP_READ(rvp
, uio
, 0, decmpfs_ctx
);
364 #endif /* HFS_COMPRESSION */
366 /* force the rsrc fork vnode to recycle right away */
369 vref
= vnode_ref (rvp
);
381 * Standard HFS only supports native FinderInfo and Resource Forks.
383 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
387 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
))) {
390 /* Bail if we don't have any extended attributes. */
391 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
392 (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
396 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
398 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
399 if (iterator
== NULL
) {
403 bzero(iterator
, sizeof(*iterator
));
405 bufsize
= sizeof(HFSPlusAttrData
) - 2;
407 bufsize
+= uio_resid(uio
);
408 bufsize
= MAX(bufsize
, sizeof(HFSPlusAttrRecord
));
409 MALLOC(recp
, HFSPlusAttrRecord
*, bufsize
, M_TEMP
, M_WAITOK
);
414 btdata
.bufferAddress
= recp
;
415 btdata
.itemSize
= bufsize
;
416 btdata
.itemCount
= 1;
418 result
= hfs_buildattrkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
422 /* Lookup the attribute. */
423 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
424 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
425 hfs_systemfile_unlock(hfsmp
, lockflags
);
428 if (result
== btNotFound
)
433 switch (recp
->recordType
) {
434 case kHFSPlusAttrInlineData
:
436 * Sanity check record size. It's not required to have any
437 * user data, so the minimum size is 2 bytes less that the
438 * size of HFSPlusAttrData (since HFSPlusAttrData struct
439 * has 2 bytes set aside for attribute data).
441 if (datasize
< (sizeof(HFSPlusAttrData
) - 2)) {
442 printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
443 VTOC(vp
)->c_fileid
, ap
->a_name
, datasize
, sizeof(HFSPlusAttrData
));
447 *ap
->a_size
= recp
->attrData
.attrSize
;
448 if (uio
&& recp
->attrData
.attrSize
!= 0) {
449 if (*ap
->a_size
> (user_size_t
)uio_resid(uio
))
452 result
= uiomove((caddr_t
) &recp
->attrData
.attrData
, recp
->attrData
.attrSize
, uio
);
456 case kHFSPlusAttrForkData
:
457 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
458 printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
459 VTOC(vp
)->c_fileid
, ap
->a_name
, datasize
, sizeof(HFSPlusAttrForkData
));
463 *ap
->a_size
= recp
->forkData
.theFork
.logicalSize
;
467 if (*ap
->a_size
> (user_size_t
)uio_resid(uio
)) {
471 /* Process overflow extents if necessary. */
472 if (has_overflow_extents(&recp
->forkData
.theFork
)) {
473 HFSPlusExtentDescriptor
*extentbuf
;
474 HFSPlusExtentDescriptor
*extentptr
;
475 size_t extentbufsize
;
476 u_int32_t totalblocks
;
480 totalblocks
= recp
->forkData
.theFork
.totalBlocks
;
481 /* Ignore bogus block counts. */
482 if (totalblocks
> HFS_MAXATTRBLKS
) {
486 attrlen
= recp
->forkData
.theFork
.logicalSize
;
488 /* Get a buffer to hold the worst case amount of extents. */
489 extentbufsize
= totalblocks
* sizeof(HFSPlusExtentDescriptor
);
490 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
491 MALLOC(extentbuf
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
492 if (extentbuf
== NULL
) {
496 bzero(extentbuf
, extentbufsize
);
497 extentptr
= extentbuf
;
499 /* Grab the first 8 extents. */
500 bcopy(&recp
->forkData
.theFork
.extents
[0], extentptr
, sizeof(HFSPlusExtentRecord
));
501 extentptr
+= kHFSPlusExtentDensity
;
502 blkcnt
= count_extent_blocks(totalblocks
, recp
->forkData
.theFork
.extents
);
504 /* Now lookup the overflow extents. */
505 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
506 while (blkcnt
< totalblocks
) {
507 ((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
= blkcnt
;
508 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
510 (recp
->recordType
!= kHFSPlusAttrExtents
) ||
511 (datasize
< sizeof(HFSPlusAttrExtents
))) {
512 printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
513 ap
->a_name
, blkcnt
, totalblocks
);
515 break; /* break from while */
517 /* Grab the next 8 extents. */
518 bcopy(&recp
->overflowExtents
.extents
[0], extentptr
, sizeof(HFSPlusExtentRecord
));
519 extentptr
+= kHFSPlusExtentDensity
;
520 blkcnt
+= count_extent_blocks(totalblocks
, recp
->overflowExtents
.extents
);
522 hfs_systemfile_unlock(hfsmp
, lockflags
);
524 if (blkcnt
< totalblocks
) {
527 result
= read_attr_data(hfsmp
, uio
, attrlen
, extentbuf
);
529 FREE(extentbuf
, M_TEMP
);
531 } else /* No overflow extents. */ {
532 result
= read_attr_data(hfsmp
, uio
, recp
->forkData
.theFork
.logicalSize
, recp
->forkData
.theFork
.extents
);
544 FREE(iterator
, M_TEMP
);
550 return MacToVFSError(result
);
554 * Set the data of an extended attribute.
558 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
560 struct vnop_setxattr_args {
561 struct vnodeop_desc *a_desc;
566 vfs_context_t a_context;
570 struct vnode
*vp
= ap
->a_vp
;
571 struct cnode
*cp
= NULL
;
572 struct hfsmount
*hfsmp
;
573 uio_t uio
= ap
->a_uio
;
574 struct BTreeIterator
* iterator
= NULL
;
575 struct filefork
*btfile
= NULL
;
577 FSBufferDescriptor btdata
;
578 HFSPlusAttrRecord
*recp
= NULL
;
579 HFSPlusExtentDescriptor
*extentptr
= NULL
;
580 HFSPlusAttrRecord attrdata
; /* 90 bytes */
581 void * user_data_ptr
= NULL
;
582 int started_transaction
= 0;
585 int allocatedblks
= 0;
588 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
589 return (EINVAL
); /* invalid name */
592 if (VNODE_IS_RSRC(vp
)) {
597 if (hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1) ) { /* 1 == don't take the cnode lock */
598 result
= decmpfs_decompress_file(vp
, VTOCMP(vp
), -1, 1, 0);
602 #endif /* HFS_COMPRESSION */
604 /* Set the Finder Info. */
605 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
606 u_int8_t finderinfo
[32];
607 struct FndrFileInfo
*fip
;
608 void * finderinfo_start
;
611 attrsize
= sizeof(VTOC(vp
)->c_finderinfo
);
613 if ((user_size_t
)uio_resid(uio
) != attrsize
) {
616 /* Grab the new Finder Info data. */
617 if ((result
= uiomove((caddr_t
)&finderinfo
, attrsize
, uio
))) {
620 fip
= (struct FndrFileInfo
*)&finderinfo
;
622 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
627 /* Symlink's don't have an external type/creator. */
628 if (vnode_islnk(vp
)) {
629 /* Skip over type/creator fields. */
630 finderinfo_start
= &cp
->c_finderinfo
[8];
633 finderinfo_start
= &cp
->c_finderinfo
[0];
635 * Don't allow the external setting of
636 * file type to kHardLinkFileType.
638 if (fip
->fdType
== SWAP_BE32(kHardLinkFileType
)) {
644 if (bcmp(finderinfo_start
, emptyfinfo
, attrsize
)) {
645 /* attr exists and "create" was specified. */
646 if (ap
->a_options
& XATTR_CREATE
) {
651 /* attr doesn't exists and "replace" was specified. */
652 if (ap
->a_options
& XATTR_REPLACE
) {
657 /* Set the cnode's Finder Info. */
658 if (attrsize
== sizeof(cp
->c_finderinfo
))
659 bcopy(&finderinfo
[0], finderinfo_start
, attrsize
);
661 bcopy(&finderinfo
[8], finderinfo_start
, attrsize
);
663 /* Updating finderInfo updates change time and modified time */
664 cp
->c_touch_chgtime
= TRUE
;
665 cp
->c_flag
|= C_MODIFIED
;
668 * Mirror the invisible bit to the UF_HIDDEN flag.
670 * The fdFlags for files and frFlags for folders are both 8 bytes
671 * into the userInfo (the first 16 bytes of the Finder Info). They
672 * are both 16-bit fields.
674 fdFlags
= *((u_int16_t
*) &cp
->c_finderinfo
[8]);
675 if (fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
676 cp
->c_flags
|= UF_HIDDEN
;
678 cp
->c_flags
&= ~UF_HIDDEN
;
680 result
= hfs_update(vp
, FALSE
);
685 /* Write the Resource Fork. */
686 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
687 struct vnode
*rvp
= NULL
;
689 int openunlinked
= 0;
691 if (!vnode_isreg(vp
)) {
694 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
698 namelen
= cp
->c_desc
.cd_namelen
;
700 if (RSRC_FORK_EXISTS(cp
)) {
701 /* attr exists and "create" was specified. */
702 if (ap
->a_options
& XATTR_CREATE
) {
707 /* attr doesn't exists and "replace" was specified. */
708 if (ap
->a_options
& XATTR_REPLACE
) {
715 * Note that we could be called on to grab the rsrc fork vnode
716 * for a file that has become open-unlinked.
718 if ((cp
->c_flag
& C_DELETED
) && (namelen
== 0)) {
722 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
, FALSE
);
728 * VNOP_WRITE marks the vnode as needing a modtime update.
730 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
732 /* if open unlinked, force it inactive and recycle */
735 vref
= vnode_ref (rvp
);
742 /* re-lock the cnode so we can update the modtimes */
743 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
749 /* HFS fsync the resource fork to force it out to disk */
750 result
= hfs_fsync (rvp
, MNT_NOWAIT
, 0, vfs_context_proc(ap
->a_context
));
759 * Standard HFS only supports native FinderInfo and Resource Forks.
761 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
764 attrsize
= uio_resid(uio
);
766 /* Enforce an upper limit. */
767 if (attrsize
> HFS_MAXATTRIBUTESIZE
) {
772 * Attempt to copy the users attr data before taking any locks.
775 hfsmp
->hfs_max_inline_attrsize
!= 0 &&
776 attrsize
< hfsmp
->hfs_max_inline_attrsize
) {
777 MALLOC(user_data_ptr
, void *, attrsize
, M_TEMP
, M_WAITOK
);
778 if (user_data_ptr
== NULL
) {
783 result
= uiomove((caddr_t
)user_data_ptr
, attrsize
, uio
);
789 result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
795 /* Start a transaction for our changes. */
796 if (hfs_start_transaction(hfsmp
) != 0) {
800 started_transaction
= 1;
803 * Once we started the transaction, nobody can compete
804 * with us, so make sure this file is still there.
806 if (cp
->c_flag
& C_NOEXISTS
) {
812 * If there isn't an attributes b-tree then create one.
814 if (hfsmp
->hfs_attribute_vp
== NULL
) {
815 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
816 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
821 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
822 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
825 /* Take exclusive access to the attributes b-tree. */
826 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
828 /* Build the b-tree key. */
829 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
830 if (iterator
== NULL
) {
834 bzero(iterator
, sizeof(*iterator
));
835 result
= hfs_buildattrkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
840 /* Preflight for replace/create semantics. */
841 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
842 btdata
.bufferAddress
= &attrdata
;
843 btdata
.itemSize
= sizeof(attrdata
);
844 btdata
.itemCount
= 1;
845 exists
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
) == 0;
847 /* Replace requires that the attribute already exists. */
848 if ((ap
->a_options
& XATTR_REPLACE
) && !exists
) {
852 /* Create requires that the attribute doesn't exist. */
853 if ((ap
->a_options
& XATTR_CREATE
) && exists
) {
857 /* If it won't fit inline then use extent-based attributes. */
858 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
859 size_t extentbufsize
;
862 u_int32_t
*keystartblk
;
865 /* Check if volume supports extent-based attributes */
866 if ((hfsmp
->hfs_flags
& HFS_XATTR_EXTENTS
) == 0) {
871 /* Get some blocks. */
872 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
873 extentbufsize
= blkcnt
* sizeof(HFSPlusExtentDescriptor
);
874 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
875 MALLOC(extentptr
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
876 if (extentptr
== NULL
) {
880 bzero(extentptr
, extentbufsize
);
881 result
= alloc_attr_blks(hfsmp
, attrsize
, extentbufsize
, extentptr
, &allocatedblks
);
884 goto exit
; /* no more space */
886 /* Copy data into the blocks. */
887 result
= write_attr_data(hfsmp
, uio
, attrsize
, extentptr
);
889 const char *name
= vnode_getname(vp
);
890 printf("hfs_setxattr: write_attr_data err (%d) %s:%s\n",
891 result
, name
? name
: "", ap
->a_name
);
897 /* Now remove any previous attribute. */
899 result
= remove_attribute_records(hfsmp
, iterator
);
901 const char *name
= vnode_getname(vp
);
902 printf("hfs_setxattr: remove_attribute_records err (%d) %s:%s\n",
903 result
, name
? name
: "", ap
->a_name
);
910 /* Create attribute fork data record. */
911 MALLOC(recp
, HFSPlusAttrRecord
*, sizeof(HFSPlusAttrRecord
), M_TEMP
, M_WAITOK
);
916 btdata
.bufferAddress
= recp
;
917 btdata
.itemCount
= 1;
918 btdata
.itemSize
= sizeof(HFSPlusAttrForkData
);
920 recp
->recordType
= kHFSPlusAttrForkData
;
921 recp
->forkData
.reserved
= 0;
922 recp
->forkData
.theFork
.logicalSize
= attrsize
;
923 recp
->forkData
.theFork
.clumpSize
= 0;
924 recp
->forkData
.theFork
.totalBlocks
= blkcnt
;
925 bcopy(extentptr
, recp
->forkData
.theFork
.extents
, sizeof(HFSPlusExtentRecord
));
927 (void) hfs_buildattrkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
929 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
931 #if HFS_XATTR_VERBOSE
932 const char *name
= vnode_getname(vp
);
933 printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n",
934 MacToVFSError(result
), name
? name
: "", ap
->a_name
);
940 extentblks
= count_extent_blocks(blkcnt
, recp
->forkData
.theFork
.extents
);
941 blkcnt
-= extentblks
;
942 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
945 /* Create overflow extents as needed. */
947 /* Initialize the key and record. */
948 *keystartblk
+= (u_int32_t
)extentblks
;
949 btdata
.itemSize
= sizeof(HFSPlusAttrExtents
);
950 recp
->recordType
= kHFSPlusAttrExtents
;
951 recp
->overflowExtents
.reserved
= 0;
953 /* Copy the next set of extents. */
954 i
+= kHFSPlusExtentDensity
;
955 bcopy(&extentptr
[i
], recp
->overflowExtents
.extents
, sizeof(HFSPlusExtentRecord
));
957 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
959 const char *name
= vnode_getname(vp
);
960 printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n",
961 MacToVFSError(result
), name
? name
: "", ap
->a_name
);
966 extentblks
= count_extent_blocks(blkcnt
, recp
->overflowExtents
.extents
);
967 blkcnt
-= extentblks
;
969 } else /* Inline data */ {
971 result
= remove_attribute_records(hfsmp
, iterator
);
977 /* Calculate size of record rounded up to multiple of 2 bytes. */
978 btdata
.itemSize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
979 MALLOC(recp
, HFSPlusAttrRecord
*, btdata
.itemSize
, M_TEMP
, M_WAITOK
);
984 recp
->recordType
= kHFSPlusAttrInlineData
;
985 recp
->attrData
.reserved
[0] = 0;
986 recp
->attrData
.reserved
[1] = 0;
987 recp
->attrData
.attrSize
= attrsize
;
989 /* Copy in the attribute data (if any). */
992 bcopy(user_data_ptr
, &recp
->attrData
.attrData
, attrsize
);
994 result
= uiomove((caddr_t
)&recp
->attrData
.attrData
, attrsize
, uio
);
1000 (void) hfs_buildattrkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1002 btdata
.bufferAddress
= recp
;
1003 btdata
.itemCount
= 1;
1004 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1007 if (btfile
&& started_transaction
) {
1008 (void) BTFlushPath(btfile
);
1011 hfs_systemfile_unlock(hfsmp
, lockflags
);
1015 /* Setting an attribute only updates change time and not
1016 * modified time of the file.
1018 cp
->c_touch_chgtime
= TRUE
;
1019 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
1020 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1021 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
1023 (void) hfs_update(vp
, 0);
1025 if (started_transaction
) {
1026 if (result
&& allocatedblks
) {
1027 free_attr_blks(hfsmp
, allocatedblks
, extentptr
);
1029 hfs_end_transaction(hfsmp
);
1034 if (user_data_ptr
) {
1035 FREE(user_data_ptr
, M_TEMP
);
1041 FREE(extentptr
, M_TEMP
);
1044 FREE(iterator
, M_TEMP
);
1046 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
1050 * Remove an extended attribute.
1054 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
1056 struct vnop_removexattr_args {
1057 struct vnodeop_desc *a_desc;
1061 vfs_context_t a_context;
1065 struct vnode
*vp
= ap
->a_vp
;
1066 struct cnode
*cp
= VTOC(vp
);
1067 struct hfsmount
*hfsmp
;
1068 struct BTreeIterator
* iterator
= NULL
;
1072 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
1073 return (EINVAL
); /* invalid name */
1076 if (VNODE_IS_RSRC(vp
)) {
1081 if (hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1) && !(ap
->a_options
& XATTR_SHOWCOMPRESSION
))
1083 #endif /* HFS_COMPRESSION */
1085 /* If Resource Fork is non-empty then truncate it. */
1086 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1087 struct vnode
*rvp
= NULL
;
1089 if ( !vnode_isreg(vp
) ) {
1092 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
1095 if ( !RSRC_FORK_EXISTS(cp
)) {
1099 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
, FALSE
);
1105 hfs_lock_truncate(VTOC(rvp
), TRUE
);
1106 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
))) {
1107 hfs_unlock_truncate(cp
, TRUE
);
1112 /* Start a transaction for encapsulating changes in
1113 * hfs_truncate() and hfs_update()
1115 if ((result
= hfs_start_transaction(hfsmp
))) {
1116 hfs_unlock_truncate(cp
, TRUE
);
1122 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, 0, ap
->a_context
);
1124 cp
->c_touch_chgtime
= TRUE
;
1125 cp
->c_flag
|= C_MODIFIED
;
1126 result
= hfs_update(vp
, FALSE
);
1129 hfs_end_transaction(hfsmp
);
1130 hfs_unlock_truncate(VTOC(rvp
), TRUE
);
1131 hfs_unlock(VTOC(rvp
));
1136 /* Clear out the Finder Info. */
1137 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1138 void * finderinfo_start
;
1139 int finderinfo_size
;
1141 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
1145 /* Symlink's don't have an external type/creator. */
1146 if (vnode_islnk(vp
)) {
1147 /* Skip over type/creator fields. */
1148 finderinfo_start
= &cp
->c_finderinfo
[8];
1149 finderinfo_size
= sizeof(cp
->c_finderinfo
) - 8;
1151 finderinfo_start
= &cp
->c_finderinfo
[0];
1152 finderinfo_size
= sizeof(cp
->c_finderinfo
);
1154 if (bcmp(finderinfo_start
, emptyfinfo
, finderinfo_size
) == 0) {
1159 bzero(finderinfo_start
, finderinfo_size
);
1161 /* Updating finderInfo updates change time and modified time */
1162 cp
->c_touch_chgtime
= TRUE
;
1163 cp
->c_flag
|= C_MODIFIED
;
1164 hfs_update(vp
, FALSE
);
1171 * Standard HFS only supports native FinderInfo and Resource Forks.
1173 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1176 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1180 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1181 if (iterator
== NULL
) {
1184 bzero(iterator
, sizeof(*iterator
));
1186 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
))) {
1190 result
= hfs_buildattrkey(cp
->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1195 if (hfs_start_transaction(hfsmp
) != 0) {
1199 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1201 result
= remove_attribute_records(hfsmp
, iterator
);
1203 hfs_systemfile_unlock(hfsmp
, lockflags
);
1206 cp
->c_touch_chgtime
= TRUE
;
1208 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1210 /* If no more attributes exist, clear attribute bit */
1211 result
= file_attribute_exist(hfsmp
, cp
->c_fileid
);
1213 cp
->c_attr
.ca_recflags
&= ~kHFSHasAttributesMask
;
1215 if (result
== EEXIST
) {
1219 hfs_systemfile_unlock(hfsmp
, lockflags
);
1221 /* If ACL was removed, clear security bit */
1222 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1223 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
1225 (void) hfs_update(vp
, 0);
1228 hfs_end_transaction(hfsmp
);
1231 FREE(iterator
, M_TEMP
);
1232 return MacToVFSError(result
);
1235 /* Check if any attribute record exist for given fileID. This function
1236 * is called by hfs_vnop_removexattr to determine if it should clear the
1237 * attribute bit in the catalog record or not.
1239 * Note - you must acquire a shared lock on the attribute btree before
1240 * calling this function.
1243 * EEXIST - If attribute record was found
1244 * 0 - Attribute was not found
1245 * (other) - Other error (such as EIO)
1248 file_attribute_exist(struct hfsmount
*hfsmp
, uint32_t fileID
)
1250 HFSPlusAttrKey
*key
;
1251 struct BTreeIterator
* iterator
= NULL
;
1252 struct filefork
*btfile
;
1255 // if there's no attribute b-tree we sure as heck
1256 // can't have any attributes!
1257 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1261 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1262 if (iterator
== NULL
) {
1266 bzero(iterator
, sizeof(*iterator
));
1267 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1269 result
= hfs_buildattrkey(fileID
, NULL
, key
);
1274 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1275 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1276 if (result
&& (result
!= btNotFound
)) {
1280 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1281 /* If no next record was found or fileID for next record did not match,
1282 * no more attributes exist for this fileID
1284 if ((result
&& (result
== btNotFound
)) || (key
->fileID
!= fileID
)) {
1292 FREE(iterator
, M_TEMP
);
1299 * Remove all the records for a given attribute.
1301 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1302 * - A transaction must have been started.
1303 * - The Attribute b-tree file must be locked exclusive.
1304 * - The Allocation Bitmap file must be locked exclusive.
1305 * - The iterator key must be initialized.
1308 remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
)
1310 struct filefork
*btfile
;
1311 FSBufferDescriptor btdata
;
1312 HFSPlusAttrRecord attrdata
; /* 90 bytes */
1316 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1318 btdata
.bufferAddress
= &attrdata
;
1319 btdata
.itemSize
= sizeof(attrdata
);
1320 btdata
.itemCount
= 1;
1321 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1323 goto exit
; /* no records. */
1326 * Free the blocks from extent based attributes.
1328 * Note that the block references (btree records) are removed
1329 * before releasing the blocks in the allocation bitmap.
1331 if (attrdata
.recordType
== kHFSPlusAttrForkData
) {
1334 u_int32_t
*keystartblk
;
1336 #if HFS_XATTR_VERBOSE
1337 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
1338 printf("hfs: remove_attribute_records: bad record size %d (expecting %d)\n", datasize
, sizeof(HFSPlusAttrForkData
));
1341 totalblks
= attrdata
.forkData
.theFork
.totalBlocks
;
1343 /* Process the first 8 extents. */
1344 extentblks
= count_extent_blocks(totalblks
, attrdata
.forkData
.theFork
.extents
);
1345 if (extentblks
> totalblks
)
1346 panic("hfs: remove_attribute_records: corruption...");
1347 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1348 free_attr_blks(hfsmp
, extentblks
, attrdata
.forkData
.theFork
.extents
);
1350 totalblks
-= extentblks
;
1351 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1353 /* Process any overflow extents. */
1355 *keystartblk
+= (u_int32_t
)extentblks
;
1357 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1359 (attrdata
.recordType
!= kHFSPlusAttrExtents
) ||
1360 (datasize
< sizeof(HFSPlusAttrExtents
))) {
1361 printf("hfs: remove_attribute_records: BTSearchRecord %d (%d), totalblks %d\n",
1362 MacToVFSError(result
), attrdata
.recordType
!= kHFSPlusAttrExtents
, totalblks
);
1364 break; /* break from while */
1366 /* Process the next 8 extents. */
1367 extentblks
= count_extent_blocks(totalblks
, attrdata
.overflowExtents
.extents
);
1368 if (extentblks
> totalblks
)
1369 panic("hfs: remove_attribute_records: corruption...");
1370 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1371 free_attr_blks(hfsmp
, extentblks
, attrdata
.overflowExtents
.extents
);
1373 totalblks
-= extentblks
;
1376 result
= BTDeleteRecord(btfile
, iterator
);
1378 (void) BTFlushPath(btfile
);
1380 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
1385 * Retrieve the list of extended attribute names.
1389 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
1391 struct vnop_listxattr_args {
1392 struct vnodeop_desc *a_desc;
1397 vfs_context_t a_context;
1400 struct vnode
*vp
= ap
->a_vp
;
1401 struct cnode
*cp
= VTOC(vp
);
1402 struct hfsmount
*hfsmp
;
1403 uio_t uio
= ap
->a_uio
;
1404 struct BTreeIterator
* iterator
= NULL
;
1405 struct filefork
*btfile
;
1406 struct listattr_callback_state state
;
1407 void * finderinfo_start
;
1408 int finderinfo_size
;
1409 user_addr_t user_start
= 0;
1410 user_size_t user_len
= 0;
1414 if (VNODE_IS_RSRC(vp
)) {
1419 int compressed
= hfs_file_is_compressed(cp
, 1); /* 1 == don't take the cnode lock */
1420 #endif /* HFS_COMPRESSION */
1425 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
))) {
1429 /* Don't expose a symlink's private type/creator. */
1430 if (vnode_islnk(vp
)) {
1431 /* Skip over type/creator fields. */
1432 finderinfo_start
= &cp
->c_finderinfo
[8];
1433 finderinfo_size
= sizeof(cp
->c_finderinfo
) - 8;
1435 finderinfo_start
= &cp
->c_finderinfo
[0];
1436 finderinfo_size
= sizeof(cp
->c_finderinfo
);
1438 /* If Finder Info is non-empty then export it's name. */
1439 if (bcmp(finderinfo_start
, emptyfinfo
, finderinfo_size
) != 0) {
1441 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
1442 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1446 result
= uiomove(XATTR_FINDERINFO_NAME
,
1447 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1452 /* If Resource Fork is non-empty then export it's name. */
1453 if (S_ISREG(cp
->c_mode
) && RSRC_FORK_EXISTS(cp
)) {
1455 if ((ap
->a_options
& XATTR_SHOWCOMPRESSION
) ||
1457 !hfs_hides_rsrc(ap
->a_context
, VTOC(vp
), 1) /* 1 == don't take the cnode lock */
1459 #endif /* HFS_COMPRESSION */
1462 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1463 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1467 result
= uiomove(XATTR_RESOURCEFORK_NAME
,
1468 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
1475 * Standard HFS only supports native FinderInfo and Resource Forks.
1476 * Return at this point.
1478 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1482 /* Bail if we don't have any extended attributes. */
1483 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
1484 (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
1488 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1490 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1491 if (iterator
== NULL
) {
1495 bzero(iterator
, sizeof(*iterator
));
1496 result
= hfs_buildattrkey(cp
->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
1501 * Lock the user's buffer here so that we won't fault on
1502 * it in uiomove while holding the attributes b-tree lock.
1504 if (uio
&& uio_isuserspace(uio
)) {
1505 user_start
= uio_curriovbase(uio
);
1506 user_len
= uio_curriovlen(uio
);
1508 if ((result
= vslock(user_start
, user_len
)) != 0) {
1513 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1515 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1516 if (result
&& result
!= btNotFound
) {
1517 hfs_systemfile_unlock(hfsmp
, lockflags
);
1521 state
.fileID
= cp
->c_fileid
;
1526 state
.showcompressed
= !compressed
|| ap
->a_options
& XATTR_SHOWCOMPRESSION
;
1527 state
.ctx
= ap
->a_context
;
1529 #endif /* HFS_COMPRESSION */
1532 * Process entries starting just after iterator->key.
1534 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
1535 (IterateCallBackProcPtr
)listattr_callback
, &state
);
1536 hfs_systemfile_unlock(hfsmp
, lockflags
);
1538 *ap
->a_size
+= state
.size
;
1541 if (state
.result
|| result
== btNotFound
)
1542 result
= state
.result
;
1546 vsunlock(user_start
, user_len
, TRUE
);
1548 FREE(iterator
, M_TEMP
);
1552 return MacToVFSError(result
);
1557 * Callback - called for each attribute
1560 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
1562 char attrname
[XATTR_MAXNAMELEN
+ 1];
1566 if (state
->fileID
!= key
->fileID
) {
1568 return (0); /* stop */
1571 * Skip over non-primary keys
1573 if (key
->startBlock
!= 0) {
1574 return (1); /* continue */
1577 /* Convert the attribute name into UTF-8. */
1578 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
1579 (u_int8_t
*)attrname
, (size_t *)&bytecount
, sizeof(attrname
), '/', 0);
1581 state
->result
= result
;
1582 return (0); /* stop */
1584 bytecount
++; /* account for null termination char */
1586 if (xattr_protected(attrname
))
1587 return (1); /* continue */
1590 if (!state
->showcompressed
&& hfs_hides_xattr(state
->ctx
, VTOC(state
->vp
), attrname
, 1) ) /* 1 == don't take the cnode lock */
1591 return 1; /* continue */
1592 #endif /* HFS_COMPRESSION */
1594 if (state
->uio
== NULL
) {
1595 state
->size
+= bytecount
;
1597 if (bytecount
> uio_resid(state
->uio
)) {
1598 state
->result
= ERANGE
;
1599 return (0); /* stop */
1601 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
1603 state
->result
= result
;
1604 return (0); /* stop */
1607 return (1); /* continue */
1611 * Remove all the attributes from a cnode.
1613 * This function creates/ends its own transaction so that each
1614 * attribute is deleted in its own transaction (to avoid having
1615 * a transaction grow too large).
1617 * This function takes the necessary locks on the attribute
1618 * b-tree file and the allocation (bitmap) file.
1622 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
)
1624 BTreeIterator
*iterator
= NULL
;
1625 HFSPlusAttrKey
*key
;
1626 struct filefork
*btfile
;
1627 int result
, lockflags
;
1629 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1632 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1634 MALLOC(iterator
, BTreeIterator
*, sizeof(BTreeIterator
), M_TEMP
, M_WAITOK
);
1635 if (iterator
== NULL
) {
1638 bzero(iterator
, sizeof(BTreeIterator
));
1639 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1641 /* Loop until there are no more attributes for this file id */
1643 if (hfs_start_transaction(hfsmp
) != 0) {
1648 /* Lock the attribute b-tree and the allocation (bitmap) files */
1649 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1652 * Go to first possible attribute key/record pair
1654 (void) hfs_buildattrkey(fileid
, NULL
, key
);
1655 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1656 if (result
|| key
->fileID
!= fileid
) {
1657 hfs_systemfile_unlock(hfsmp
, lockflags
);
1658 hfs_end_transaction(hfsmp
);
1661 result
= remove_attribute_records(hfsmp
, iterator
);
1663 #if HFS_XATTR_VERBOSE
1665 printf("hfs_removeallattr: unexpected err %d\n", result
);
1668 hfs_systemfile_unlock(hfsmp
, lockflags
);
1669 hfs_end_transaction(hfsmp
);
1674 FREE(iterator
, M_TEMP
);
1675 return (result
== btNotFound
? 0: MacToVFSError(result
));
1680 hfs_xattr_init(struct hfsmount
* hfsmp
)
1683 * If there isn't an attributes b-tree then create one.
1685 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
) &&
1686 (hfsmp
->hfs_attribute_vp
== NULL
) &&
1687 !(hfsmp
->hfs_flags
& HFS_READ_ONLY
)) {
1688 (void) hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
1689 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
1691 if (hfsmp
->hfs_attribute_vp
)
1692 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
1696 * Enable/Disable volume attributes stored as EA for root file system.
1697 * Supported attributes are -
1699 * 2. Extent-based Extended Attributes
1703 hfs_set_volxattr(struct hfsmount
*hfsmp
, unsigned int xattrtype
, int state
)
1705 struct BTreeIterator
* iterator
= NULL
;
1706 struct filefork
*btfile
;
1710 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1715 * If there isn't an attributes b-tree then create one.
1717 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1718 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
1719 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
1725 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1726 if (iterator
== NULL
) {
1729 bzero(iterator
, sizeof(*iterator
));
1732 * Build a b-tree key.
1733 * We use the root's parent id (1) to hold this volume attribute.
1735 if (xattrtype
== HFS_SETACLSTATE
) {
1737 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
1738 (HFSPlusAttrKey
*)&iterator
->key
);
1739 } else if (xattrtype
== HFS_SET_XATTREXTENTS_STATE
) {
1740 /* Extent-based extended attributes */
1741 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_XATTREXTENTS_NAME
,
1742 (HFSPlusAttrKey
*)&iterator
->key
);
1748 /* Start a transaction for our changes. */
1749 if (hfs_start_transaction(hfsmp
) != 0) {
1753 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1755 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1758 /* Remove the attribute. */
1759 result
= BTDeleteRecord(btfile
, iterator
);
1760 if (result
== btNotFound
)
1763 FSBufferDescriptor btdata
;
1764 HFSPlusAttrData attrdata
;
1767 datasize
= sizeof(attrdata
);
1768 btdata
.bufferAddress
= &attrdata
;
1769 btdata
.itemSize
= datasize
;
1770 btdata
.itemCount
= 1;
1771 attrdata
.recordType
= kHFSPlusAttrInlineData
;
1772 attrdata
.reserved
[0] = 0;
1773 attrdata
.reserved
[1] = 0;
1774 attrdata
.attrSize
= 2;
1775 attrdata
.attrData
[0] = 0;
1776 attrdata
.attrData
[1] = 0;
1778 /* Insert the attribute. */
1779 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
1780 if (result
== btExists
)
1783 (void) BTFlushPath(btfile
);
1785 hfs_systemfile_unlock(hfsmp
, lockflags
);
1787 /* Finish the transaction of our changes. */
1788 hfs_end_transaction(hfsmp
);
1791 FREE(iterator
, M_TEMP
);
1794 if (xattrtype
== HFS_SETACLSTATE
) {
1796 vfs_clearextendedsecurity(HFSTOVFS(hfsmp
));
1798 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
1801 /* HFS_SET_XATTREXTENTS_STATE */
1802 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1804 hfsmp
->hfs_flags
&= ~HFS_XATTR_EXTENTS
;
1806 hfsmp
->hfs_flags
|= HFS_XATTR_EXTENTS
;
1808 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1812 return MacToVFSError(result
);
1817 * Check for volume attributes stored as EA for root file system.
1818 * Supported attributes are -
1820 * 2. Extent-based Extended Attributes
1824 hfs_check_volxattr(struct hfsmount
*hfsmp
, unsigned int xattrtype
)
1826 struct BTreeIterator
* iterator
;
1827 struct filefork
*btfile
;
1831 if (hfsmp
->hfs_flags
& HFS_STANDARD
||
1832 hfsmp
->hfs_attribute_vp
== NULL
) {
1836 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1837 if (iterator
== NULL
) {
1840 bzero(iterator
, sizeof(*iterator
));
1843 * Build a b-tree key.
1844 * We use the root's parent id (1) to hold this volume attribute.
1846 if (xattrtype
== HFS_SETACLSTATE
) {
1848 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
1849 (HFSPlusAttrKey
*)&iterator
->key
);
1851 /* Extent-based extended attributes */
1852 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_XATTREXTENTS_NAME
,
1853 (HFSPlusAttrKey
*)&iterator
->key
);
1855 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1857 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1859 /* Check for our attribute. */
1860 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1862 hfs_systemfile_unlock(hfsmp
, lockflags
);
1863 FREE(iterator
, M_TEMP
);
1866 if (xattrtype
== HFS_SETACLSTATE
) {
1867 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
1869 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
1870 hfsmp
->hfs_flags
|= HFS_XATTR_EXTENTS
;
1871 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1878 * hfs_attrkeycompare - compare two attribute b-tree keys.
1880 * The name portion of the key is compared using a 16-bit binary comparison.
1881 * This is called from the b-tree code.
1885 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
1887 u_int32_t searchFileID
, trialFileID
;
1890 searchFileID
= searchKey
->fileID
;
1891 trialFileID
= trialKey
->fileID
;
1894 if (searchFileID
> trialFileID
) {
1896 } else if (searchFileID
< trialFileID
) {
1899 u_int16_t
* str1
= &searchKey
->attrName
[0];
1900 u_int16_t
* str2
= &trialKey
->attrName
[0];
1901 int length1
= searchKey
->attrNameLen
;
1902 int length2
= trialKey
->attrNameLen
;
1906 if (length1
< length2
) {
1909 } else if (length1
> length2
) {
1932 * Names are equal; compare startBlock
1934 if (searchKey
->startBlock
== trialKey
->startBlock
)
1937 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
1945 * hfs_buildattrkey - build an Attribute b-tree key
1949 hfs_buildattrkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
1952 size_t unicodeBytes
= 0;
1954 if (attrname
!= NULL
) {
1956 * Convert filename from UTF-8 into Unicode
1958 result
= utf8_decodestr((const u_int8_t
*)attrname
, strlen(attrname
), key
->attrName
,
1959 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
1961 if (result
!= ENAMETOOLONG
)
1962 result
= EINVAL
; /* name has invalid characters */
1965 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
1966 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
1968 key
->attrNameLen
= 0;
1969 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
1972 key
->fileID
= fileID
;
1973 key
->startBlock
= 0;
1979 * getnodecount - calculate starting node count for attributes b-tree.
1982 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
1984 u_int64_t freebytes
;
1985 u_int64_t calcbytes
;
1988 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
1989 * 10.5: Attempt to be as big as the catalog clump size.
1991 * Use no more than 10 % of the remaining free space.
1993 freebytes
= (u_int64_t
)hfs_freeblks(hfsmp
, 0) * (u_int64_t
)hfsmp
->blockSize
;
1995 calcbytes
= MIN(hfsmp
->hfs_catalog_cp
->c_datafork
->ff_size
/ 5, 20 * 1024 * 1024);
1997 calcbytes
= MAX(calcbytes
, hfsmp
->hfs_catalog_cp
->c_datafork
->ff_clumpsize
);
1999 calcbytes
= MIN(calcbytes
, freebytes
/ 10);
2001 return (MAX(2, (int)(calcbytes
/ nodesize
)));
2006 * getmaxinlineattrsize - calculate maximum inline attribute size.
2008 * This yields 3,802 bytes for an 8K node size.
2011 getmaxinlineattrsize(struct vnode
* attrvp
)
2013 struct BTreeInfoRec btinfo
;
2014 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
2017 if (attrvp
!= NULL
) {
2018 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
);
2019 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
2020 nodesize
= btinfo
.nodeSize
;
2021 hfs_unlock(VTOC(attrvp
));
2024 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
2025 maxsize
-= 3 * sizeof(u_int16_t
); /* minus 3 index slots */
2026 maxsize
/= 2; /* 2 key/rec pairs minumum */
2027 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
2028 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
2029 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */
2035 * Get a referenced vnode for attribute data I/O.
2038 get_attr_data_vnode(struct hfsmount
*hfsmp
, vnode_t
*vpp
)
2043 vp
= hfsmp
->hfs_attrdata_vp
;
2045 struct cat_desc cat_desc
;
2046 struct cat_attr cat_attr
;
2047 struct cat_fork cat_fork
;
2049 /* We don't tag it as a system file since we intend to use cluster I/O. */
2050 bzero(&cat_desc
, sizeof(cat_desc
));
2051 cat_desc
.cd_parentcnid
= kHFSRootParentID
;
2052 cat_desc
.cd_nameptr
= (const u_int8_t
*)hfs_attrdatafilename
;
2053 cat_desc
.cd_namelen
= strlen(hfs_attrdatafilename
);
2054 cat_desc
.cd_cnid
= kHFSAttributeDataFileID
;
2056 bzero(&cat_attr
, sizeof(cat_attr
));
2057 cat_attr
.ca_linkcount
= 1;
2058 cat_attr
.ca_mode
= S_IFREG
;
2059 cat_attr
.ca_fileid
= cat_desc
.cd_cnid
;
2060 cat_attr
.ca_blocks
= hfsmp
->totalBlocks
;
2063 * The attribute data file is a virtual file that spans the
2064 * entire file system space.
2066 * Each extent-based attribute occupies a unique portion of
2067 * in this virtual file. The cluster I/O is done using actual
2068 * allocation block offsets so no additional mapping is needed
2069 * for the VNOP_BLOCKMAP call.
2071 * This approach allows the attribute data to be cached without
2072 * incurring the high cost of using a separate vnode per attribute.
2074 * Since we need to acquire the attribute b-tree file lock anyways,
2075 * the virtual file doesn't introduce any additional serialization.
2077 bzero(&cat_fork
, sizeof(cat_fork
));
2078 cat_fork
.cf_size
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
2079 cat_fork
.cf_blocks
= hfsmp
->totalBlocks
;
2080 cat_fork
.cf_extents
[0].startBlock
= 0;
2081 cat_fork
.cf_extents
[0].blockCount
= cat_fork
.cf_blocks
;
2083 result
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cat_desc
, 0, &cat_attr
, &cat_fork
, &vp
);
2085 HFS_MOUNT_LOCK(hfsmp
, 1);
2086 /* Check if someone raced us for creating this vnode. */
2087 if (hfsmp
->hfs_attrdata_vp
!= NULLVP
) {
2088 HFS_MOUNT_UNLOCK(hfsmp
, 1);
2091 vp
= hfsmp
->hfs_attrdata_vp
;
2093 hfsmp
->hfs_attrdata_vp
= vp
;
2094 HFS_MOUNT_UNLOCK(hfsmp
, 1);
2095 /* Keep a reference on this vnode until unmount */
2096 vnode_ref_ext(vp
, O_EVTONLY
);
2097 hfs_unlock(VTOC(vp
));
2101 if ((result
= vnode_get(vp
)))
2109 * Read an extent based attribute.
2112 read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2114 vnode_t evp
= NULLVP
;
2122 if ((result
= get_attr_data_vnode(hfsmp
, &evp
))) {
2125 hfs_lock_truncate(VTOC(evp
), 0);
2127 bufsize
= (int)uio_resid(uio
);
2128 attrsize
= (int)datasize
;
2129 blksize
= (int)hfsmp
->blockSize
;
2132 * Read the attribute data one extent at a time.
2133 * For the typical case there is only one extent.
2135 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2136 iosize
= (int)extents
[i
].blockCount
* blksize
;
2137 iosize
= MIN(iosize
, attrsize
);
2138 iosize
= MIN(iosize
, bufsize
);
2139 uio_setresid(uio
, iosize
);
2140 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2142 result
= cluster_read(evp
, uio
, VTOF(evp
)->ff_size
, IO_SYNC
| IO_UNIT
);
2144 #if HFS_XATTR_VERBOSE
2145 printf("hfs: read_attr_data: cr iosize %d [%d, %d] (%d)\n",
2146 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2153 uio_setresid(uio
, bufsize
);
2154 uio_setoffset(uio
, datasize
);
2156 hfs_unlock_truncate(VTOC(evp
), 0);
2162 * Write an extent based attribute.
2165 write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2167 vnode_t evp
= NULLVP
;
2176 /* Get exclusive use of attribute data vnode. */
2177 if ((result
= get_attr_data_vnode(hfsmp
, &evp
))) {
2180 hfs_lock_truncate(VTOC(evp
), 0);
2182 bufsize
= uio_resid(uio
);
2183 attrsize
= (int) datasize
;
2184 blksize
= (int) hfsmp
->blockSize
;
2185 filesize
= VTOF(evp
)->ff_size
;
2188 * Write the attribute data one extent at a time.
2190 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2191 iosize
= (int)extents
[i
].blockCount
* blksize
;
2192 iosize
= MIN(iosize
, attrsize
);
2193 iosize
= MIN(iosize
, bufsize
);
2194 uio_setresid(uio
, iosize
);
2195 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2197 result
= cluster_write(evp
, uio
, filesize
, filesize
, filesize
,
2198 (off_t
) 0, IO_SYNC
| IO_UNIT
);
2199 #if HFS_XATTR_VERBOSE
2200 printf("hfs: write_attr_data: cw iosize %d [%d, %d] (%d)\n",
2201 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2208 uio_setresid(uio
, bufsize
);
2209 uio_setoffset(uio
, datasize
);
2211 hfs_unlock_truncate(VTOC(evp
), 0);
2217 * Allocate blocks for an extent based attribute.
2220 alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
)
2229 startblk
= hfsmp
->hfs_metazone_end
;
2230 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
2231 if (blkcnt
> (int)hfs_freeblks(hfsmp
, 0)) {
2235 maxextents
= extentbufsize
/ sizeof(HFSPlusExtentDescriptor
);
2237 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2239 for (i
= 0; (blkcnt
> 0) && (i
< maxextents
); i
++) {
2240 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, 0,
2241 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
2242 #if HFS_XATTR_VERBOSE
2243 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2244 blkcnt
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2247 extents
[i
].startBlock
= 0;
2248 extents
[i
].blockCount
= 0;
2251 blkcnt
-= extents
[i
].blockCount
;
2252 startblk
= extents
[i
].startBlock
+ extents
[i
].blockCount
;
2255 * If it didn't fit in the extents buffer then bail.
2260 #if HFS_XATTR_VERBOSE
2261 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt
);
2263 for (; i
<= 0; i
--) {
2264 if ((blkcnt
= extents
[i
].blockCount
) != 0) {
2265 (void) BlockDeallocate(hfsmp
, extents
[i
].startBlock
, blkcnt
, 0);
2266 extents
[i
].startBlock
= 0;
2267 extents
[i
].blockCount
= 0;
2272 hfs_systemfile_unlock(hfsmp
, lockflags
);
2273 return MacToVFSError(result
);
2277 * Release blocks from an extent based attribute.
2280 free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
)
2282 vnode_t evp
= NULLVP
;
2283 int remblks
= blkcnt
;
2287 if (get_attr_data_vnode(hfsmp
, &evp
) != 0) {
2290 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2292 for (i
= 0; (remblks
> 0) && (extents
[i
].blockCount
!= 0); i
++) {
2293 if (extents
[i
].blockCount
> (u_int32_t
)blkcnt
) {
2294 #if HFS_XATTR_VERBOSE
2295 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
2296 extents
[i
].startBlock
, extents
[i
].blockCount
);
2298 extents
[i
].blockCount
= 0;
2301 if (extents
[i
].startBlock
== 0) {
2304 (void)BlockDeallocate(hfsmp
, extents
[i
].startBlock
, extents
[i
].blockCount
, 0);
2305 remblks
-= extents
[i
].blockCount
;
2306 extents
[i
].startBlock
= 0;
2307 extents
[i
].blockCount
= 0;
2309 #if HFS_XATTR_VERBOSE
2310 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2311 extents
[i
].startBlock
, extents
[i
].blockCount
);
2313 /* Discard any resident pages for this block range. */
2317 start
= (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)hfsmp
->blockSize
;
2318 end
= start
+ (u_int64_t
)extents
[i
].blockCount
* (u_int64_t
)hfsmp
->blockSize
;
2319 (void) ubc_msync(hfsmp
->hfs_attrdata_vp
, start
, end
, &start
, UBC_INVALIDATE
);
2323 hfs_systemfile_unlock(hfsmp
, lockflags
);
2330 has_overflow_extents(HFSPlusForkData
*forkdata
)
2334 if (forkdata
->extents
[7].blockCount
== 0)
2337 blocks
= forkdata
->extents
[0].blockCount
+
2338 forkdata
->extents
[1].blockCount
+
2339 forkdata
->extents
[2].blockCount
+
2340 forkdata
->extents
[3].blockCount
+
2341 forkdata
->extents
[4].blockCount
+
2342 forkdata
->extents
[5].blockCount
+
2343 forkdata
->extents
[6].blockCount
+
2344 forkdata
->extents
[7].blockCount
;
2346 return (forkdata
->totalBlocks
> blocks
);
2350 count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
)
2355 for (i
= 0, blocks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
2356 /* Ignore obvious bogus extents. */
2357 if (extents
[i
].blockCount
> (u_int32_t
)maxblks
)
2359 if (extents
[i
].startBlock
== 0 || extents
[i
].blockCount
== 0)
2361 blocks
+= extents
[i
].blockCount
;