2 * Copyright (c) 2004-2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
34 #include <sys/utfconv.h>
35 #include <sys/vnode.h>
36 #include <sys/xattr.h>
37 #include <sys/fcntl.h>
38 #include <sys/fsctl.h>
39 #include <sys/vnode_internal.h>
40 #include <sys/kauth.h>
43 #include "hfs_cnode.h"
44 #include "hfs_mount.h"
45 #include "hfs_format.h"
46 #include "hfs_endian.h"
47 #include "hfs_btreeio.h"
48 #include "hfs_fsctl.h"
50 #include "hfscommon/headers/BTreesInternal.h"
52 #define HFS_XATTR_VERBOSE 0
54 #define ATTRIBUTE_FILE_NODE_SIZE 8192
57 /* State information for the listattr_callback callback function. */
58 struct listattr_callback_state
{
67 #endif /* HFS_COMPRESSION */
71 /* HFS Internal Names */
72 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
73 #define XATTR_XATTREXTENTS_NAME "system.xattrextents"
75 /* Faster version if we already know this is the data fork. */
76 #define RSRC_FORK_EXISTS(CP) \
77 (((CP)->c_attr.ca_blocks - (CP)->c_datafork->ff_data.cf_blocks) > 0)
79 static u_int32_t emptyfinfo
[8] = {0};
81 static int hfs_zero_hidden_fields (struct cnode
*cp
, u_int8_t
*finderinfo
);
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
, HFS_LOCK_DEFAULT
))) {
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
, HFS_LOCK_DEFAULT
))) {
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
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
220 if ((error
= hfs_lock(scp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
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
, HFS_LOCK_DEFAULT
);
234 /* Zero out the date added field for the specified cnode */
235 static int hfs_zero_hidden_fields (struct cnode
*cp
, u_int8_t
*finderinfo
)
237 u_int8_t
*finfo
= finderinfo
;
239 /* Advance finfo by 16 bytes to the 2nd half of the finderinfo */
242 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
243 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
244 extinfo
->document_id
= 0;
245 extinfo
->date_added
= 0;
246 extinfo
->write_gen_counter
= 0;
247 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
248 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
249 extinfo
->document_id
= 0;
250 extinfo
->date_added
= 0;
251 extinfo
->write_gen_counter
= 0;
253 /* Return an error */
261 * Retrieve the data of an extended attribute.
264 hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
)
266 struct vnop_getxattr_args {
267 struct vnodeop_desc *a_desc;
273 vfs_context_t a_context;
277 struct vnode
*vp
= ap
->a_vp
;
279 struct hfsmount
*hfsmp
;
280 uio_t uio
= ap
->a_uio
;
285 if (vp
== cp
->c_vp
) {
287 int decmpfs_hide
= hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1); /* 1 == don't take the cnode lock */
288 if (decmpfs_hide
&& !(ap
->a_options
& XATTR_SHOWCOMPRESSION
))
290 #endif /* HFS_COMPRESSION */
292 /* Get the Finder Info. */
293 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
294 u_int8_t finderinfo
[32];
297 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
300 /* Make a copy since we may not export all of it. */
301 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
304 /* Zero out the date added field in the local copy */
305 hfs_zero_hidden_fields (cp
, finderinfo
);
307 /* Don't expose a symlink's private type/creator. */
308 if (vnode_islnk(vp
)) {
309 struct FndrFileInfo
*fip
;
311 fip
= (struct FndrFileInfo
*)&finderinfo
;
315 /* If Finder Info is empty then it doesn't exist. */
316 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
320 *ap
->a_size
= bufsize
;
323 if ((user_size_t
)uio_resid(uio
) < bufsize
)
326 result
= uiomove((caddr_t
)&finderinfo
, bufsize
, uio
);
330 /* Read the Resource Fork. */
331 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
332 struct vnode
*rvp
= NULL
;
333 int openunlinked
= 0;
336 if ( !S_ISREG(cp
->c_mode
) ) {
339 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
342 namelen
= cp
->c_desc
.cd_namelen
;
344 if ( !RSRC_FORK_EXISTS(cp
)) {
349 if ((cp
->c_flag
& C_DELETED
) && (namelen
== 0)) {
353 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
, FALSE
);
359 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
362 user_ssize_t uio_size
= 0;
364 uio_size
= uio_resid(uio
);
365 #endif /* HFS_COMPRESSION */
366 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
370 (uio_resid(uio
) == uio_size
)) {
372 * We intentionally make the above call to VNOP_READ so that
373 * it can return an authorization/permission/etc. Error
374 * based on ap->a_context and thus deny this operation;
375 * in that case, result != 0 and we won't proceed.
377 * However, if result == 0, it will have returned no data
378 * because hfs_vnop_read hid the resource fork
379 * (hence uio_resid(uio) == uio_size, i.e. the uio is untouched)
381 * In that case, we try again with the decmpfs_ctx context
382 * to get the actual data
384 result
= VNOP_READ(rvp
, uio
, 0, decmpfs_ctx
);
386 #endif /* HFS_COMPRESSION */
388 /* force the rsrc fork vnode to recycle right away */
391 vref
= vnode_ref (rvp
);
403 * Standard HFS only supports native FinderInfo and Resource Forks.
405 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
409 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
413 /* Check for non-rsrc, non-finderinfo EAs */
414 result
= hfs_getxattr_internal (cp
, ap
, VTOHFS(cp
->c_vp
), 0);
418 return MacToVFSError(result
);
426 * We break out this internal function which searches the attributes B-Tree and the
427 * overflow extents file to find non-resource, non-finderinfo EAs. There may be cases
428 * where we need to get EAs in contexts where we are already holding the cnode lock,
429 * and to re-enter hfs_vnop_getxattr would cause us to double-lock the cnode. Instead,
430 * we can just directly call this function.
432 * We pass the hfsmp argument directly here because we may not necessarily have a cnode to
433 * operate on. Under normal conditions, we have a file or directory to query, but if we
434 * are operating on the root directory (id 1), then we may not have a cnode. In this case, if hte
435 * 'cp' argument is NULL, then we need to use the 'fileid' argument as the entry to manipulate
437 * NOTE: This function assumes the cnode lock for 'cp' is held exclusive or shared.
439 int hfs_getxattr_internal (struct cnode
*cp
, struct vnop_getxattr_args
*ap
,
440 struct hfsmount
*hfsmp
, u_int32_t fileid
)
443 struct filefork
*btfile
;
444 struct BTreeIterator
* iterator
= NULL
;
446 HFSPlusAttrRecord
*recp
= NULL
;
447 FSBufferDescriptor btdata
;
450 u_int16_t datasize
= 0;
451 uio_t uio
= ap
->a_uio
;
452 u_int32_t target_id
= 0;
455 target_id
= cp
->c_fileid
;
461 /* Bail if we don't have an EA B-Tree. */
462 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
463 ((cp
) && (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0)) {
468 /* Initialize the B-Tree iterator for searching for the proper EA */
469 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
471 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
472 if (iterator
== NULL
) {
476 bzero(iterator
, sizeof(*iterator
));
478 /* Allocate memory for reading in the attribute record. This buffer is
479 * big enough to read in all types of attribute records. It is not big
480 * enough to read inline attribute data which is read in later.
482 MALLOC(recp
, HFSPlusAttrRecord
*, sizeof(HFSPlusAttrRecord
), M_TEMP
, M_WAITOK
);
487 btdata
.bufferAddress
= recp
;
488 btdata
.itemSize
= sizeof(HFSPlusAttrRecord
);
489 btdata
.itemCount
= 1;
491 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
496 /* Lookup the attribute in the Attribute B-Tree */
497 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
498 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
499 hfs_systemfile_unlock(hfsmp
, lockflags
);
502 if (result
== btNotFound
) {
509 * Operate differently if we have inline EAs that can fit in the attribute B-Tree or if
510 * we have extent based EAs.
512 switch (recp
->recordType
) {
514 /* Attribute fits in the Attribute B-Tree */
515 case kHFSPlusAttrInlineData
: {
517 * Sanity check record size. It's not required to have any
518 * user data, so the minimum size is 2 bytes less that the
519 * size of HFSPlusAttrData (since HFSPlusAttrData struct
520 * has 2 bytes set aside for attribute data).
522 if (datasize
< (sizeof(HFSPlusAttrData
) - 2)) {
523 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
524 hfsmp
->vcbVN
, target_id
, ap
->a_name
, datasize
, sizeof(HFSPlusAttrData
));
528 *ap
->a_size
= recp
->attrData
.attrSize
;
529 if (uio
&& recp
->attrData
.attrSize
!= 0) {
530 if (*ap
->a_size
> (user_size_t
)uio_resid(uio
)) {
531 /* User provided buffer is not large enough for the xattr data */
534 /* Previous BTreeSearchRecord() read in only the attribute record,
535 * and not the attribute data. Now allocate enough memory for
536 * both attribute record and data, and read the attribute record again.
538 bufsize
= sizeof(HFSPlusAttrData
) - 2 + recp
->attrData
.attrSize
;
540 MALLOC(recp
, HFSPlusAttrRecord
*, bufsize
, M_TEMP
, M_WAITOK
);
546 btdata
.bufferAddress
= recp
;
547 btdata
.itemSize
= bufsize
;
548 btdata
.itemCount
= 1;
550 bzero(iterator
, sizeof(*iterator
));
551 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
556 /* Lookup the attribute record and inline data */
557 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
558 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
559 hfs_systemfile_unlock(hfsmp
, lockflags
);
561 if (result
== btNotFound
) {
567 /* Copy-out the attribute data to the user buffer */
568 *ap
->a_size
= recp
->attrData
.attrSize
;
569 result
= uiomove((caddr_t
) &recp
->attrData
.attrData
, recp
->attrData
.attrSize
, uio
);
575 /* Extent-Based EAs */
576 case kHFSPlusAttrForkData
: {
577 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
578 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
579 hfsmp
->vcbVN
, target_id
, ap
->a_name
, datasize
, sizeof(HFSPlusAttrForkData
));
583 *ap
->a_size
= recp
->forkData
.theFork
.logicalSize
;
587 if (*ap
->a_size
> (user_size_t
)uio_resid(uio
)) {
591 /* Process overflow extents if necessary. */
592 if (has_overflow_extents(&recp
->forkData
.theFork
)) {
593 HFSPlusExtentDescriptor
*extentbuf
;
594 HFSPlusExtentDescriptor
*extentptr
;
595 size_t extentbufsize
;
596 u_int32_t totalblocks
;
600 totalblocks
= recp
->forkData
.theFork
.totalBlocks
;
601 /* Ignore bogus block counts. */
602 if (totalblocks
> howmany(HFS_XATTR_MAXSIZE
, hfsmp
->blockSize
)) {
606 attrlen
= recp
->forkData
.theFork
.logicalSize
;
608 /* Get a buffer to hold the worst case amount of extents. */
609 extentbufsize
= totalblocks
* sizeof(HFSPlusExtentDescriptor
);
610 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
611 MALLOC(extentbuf
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
612 if (extentbuf
== NULL
) {
616 bzero(extentbuf
, extentbufsize
);
617 extentptr
= extentbuf
;
619 /* Grab the first 8 extents. */
620 bcopy(&recp
->forkData
.theFork
.extents
[0], extentptr
, sizeof(HFSPlusExtentRecord
));
621 extentptr
+= kHFSPlusExtentDensity
;
622 blkcnt
= count_extent_blocks(totalblocks
, recp
->forkData
.theFork
.extents
);
624 /* Now lookup the overflow extents. */
625 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
626 while (blkcnt
< totalblocks
) {
627 ((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
= blkcnt
;
628 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
630 (recp
->recordType
!= kHFSPlusAttrExtents
) ||
631 (datasize
< sizeof(HFSPlusAttrExtents
))) {
632 printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
633 ap
->a_name
, blkcnt
, totalblocks
);
635 break; /* break from while */
637 /* Grab the next 8 extents. */
638 bcopy(&recp
->overflowExtents
.extents
[0], extentptr
, sizeof(HFSPlusExtentRecord
));
639 extentptr
+= kHFSPlusExtentDensity
;
640 blkcnt
+= count_extent_blocks(totalblocks
, recp
->overflowExtents
.extents
);
643 /* Release Attr B-Tree lock */
644 hfs_systemfile_unlock(hfsmp
, lockflags
);
646 if (blkcnt
< totalblocks
) {
649 result
= read_attr_data(hfsmp
, uio
, attrlen
, extentbuf
);
651 FREE(extentbuf
, M_TEMP
);
653 } else { /* No overflow extents. */
654 result
= read_attr_data(hfsmp
, uio
, recp
->forkData
.theFork
.logicalSize
, recp
->forkData
.theFork
.extents
);
660 /* We only support Extent or inline EAs. Default to ENOATTR for anything else */
667 FREE(iterator
, M_TEMP
);
679 * Set the data of an extended attribute.
682 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
684 struct vnop_setxattr_args {
685 struct vnodeop_desc *a_desc;
690 vfs_context_t a_context;
694 struct vnode
*vp
= ap
->a_vp
;
695 struct cnode
*cp
= NULL
;
696 struct hfsmount
*hfsmp
;
697 uio_t uio
= ap
->a_uio
;
699 void * user_data_ptr
= NULL
;
701 time_t orig_ctime
=VTOC(vp
)->c_ctime
;
703 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
704 return (EINVAL
); /* invalid name */
707 if (VNODE_IS_RSRC(vp
)) {
712 if (hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1) ) { /* 1 == don't take the cnode lock */
713 result
= decmpfs_decompress_file(vp
, VTOCMP(vp
), -1, 1, 0);
717 #endif /* HFS_COMPRESSION */
719 check_for_tracked_file(vp
, orig_ctime
, NAMESPACE_HANDLER_METADATA_WRITE_OP
, NSPACE_REARM_NO_ARG
);
721 /* Set the Finder Info. */
722 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
723 u_int8_t finderinfo
[32];
724 struct FndrFileInfo
*fip
;
725 void * finderinfo_start
;
726 u_int8_t
*finfo
= NULL
;
728 u_int32_t dateadded
= 0;
729 u_int32_t write_gen_counter
= 0;
730 u_int32_t document_id
= 0;
732 attrsize
= sizeof(VTOC(vp
)->c_finderinfo
);
734 if ((user_size_t
)uio_resid(uio
) != attrsize
) {
737 /* Grab the new Finder Info data. */
738 if ((result
= uiomove((caddr_t
)&finderinfo
, attrsize
, uio
))) {
741 fip
= (struct FndrFileInfo
*)&finderinfo
;
743 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
748 /* Symlink's don't have an external type/creator. */
749 if (vnode_islnk(vp
)) {
750 /* Skip over type/creator fields. */
751 finderinfo_start
= &cp
->c_finderinfo
[8];
754 finderinfo_start
= &cp
->c_finderinfo
[0];
756 * Don't allow the external setting of
757 * file type to kHardLinkFileType.
759 if (fip
->fdType
== SWAP_BE32(kHardLinkFileType
)) {
765 /* Grab the current date added from the cnode */
766 dateadded
= hfs_get_dateadded (cp
);
767 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
768 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)((u_int8_t
*)cp
->c_finderinfo
+ 16);
769 write_gen_counter
= extinfo
->write_gen_counter
;
770 document_id
= extinfo
->document_id
;
771 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
772 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)((u_int8_t
*)cp
->c_finderinfo
+ 16);
773 write_gen_counter
= extinfo
->write_gen_counter
;
774 document_id
= extinfo
->document_id
;
777 /* Zero out the date added field to ignore user's attempts to set it */
778 hfs_zero_hidden_fields(cp
, finderinfo
);
780 if (bcmp(finderinfo_start
, emptyfinfo
, attrsize
)) {
781 /* attr exists and "create" was specified. */
782 if (ap
->a_options
& XATTR_CREATE
) {
787 /* attr doesn't exists and "replace" was specified. */
788 if (ap
->a_options
& XATTR_REPLACE
) {
795 * Now restore the date added to the finderinfo to be written out.
796 * Advance to the 2nd half of the finderinfo to write out the date added
799 * Make sure to endian swap the date added back into big endian. When we used
800 * hfs_get_dateadded above to retrieve it, it swapped into local endianness
801 * for us. But now that we're writing it out, put it back into big endian.
803 finfo
= &finderinfo
[16];
805 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
806 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
807 extinfo
->date_added
= OSSwapHostToBigInt32(dateadded
);
808 extinfo
->write_gen_counter
= write_gen_counter
;
809 extinfo
->document_id
= document_id
;
810 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
811 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
812 extinfo
->date_added
= OSSwapHostToBigInt32(dateadded
);
813 extinfo
->write_gen_counter
= write_gen_counter
;
814 extinfo
->document_id
= document_id
;
817 /* Set the cnode's Finder Info. */
818 if (attrsize
== sizeof(cp
->c_finderinfo
)) {
819 bcopy(&finderinfo
[0], finderinfo_start
, attrsize
);
821 bcopy(&finderinfo
[8], finderinfo_start
, attrsize
);
824 /* Updating finderInfo updates change time and modified time */
825 cp
->c_touch_chgtime
= TRUE
;
826 cp
->c_flag
|= C_MODIFIED
;
829 * Mirror the invisible bit to the UF_HIDDEN flag.
831 * The fdFlags for files and frFlags for folders are both 8 bytes
832 * into the userInfo (the first 16 bytes of the Finder Info). They
833 * are both 16-bit fields.
835 fdFlags
= *((u_int16_t
*) &cp
->c_finderinfo
[8]);
836 if (fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
)) {
837 cp
->c_bsdflags
|= UF_HIDDEN
;
839 cp
->c_bsdflags
&= ~UF_HIDDEN
;
842 result
= hfs_update(vp
, FALSE
);
847 /* Write the Resource Fork. */
848 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
849 struct vnode
*rvp
= NULL
;
851 int openunlinked
= 0;
853 if (!vnode_isreg(vp
)) {
856 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
860 namelen
= cp
->c_desc
.cd_namelen
;
862 if (RSRC_FORK_EXISTS(cp
)) {
863 /* attr exists and "create" was specified. */
864 if (ap
->a_options
& XATTR_CREATE
) {
869 /* attr doesn't exists and "replace" was specified. */
870 if (ap
->a_options
& XATTR_REPLACE
) {
877 * Note that we could be called on to grab the rsrc fork vnode
878 * for a file that has become open-unlinked.
880 if ((cp
->c_flag
& C_DELETED
) && (namelen
== 0)) {
884 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
, FALSE
);
889 /* VNOP_WRITE marks cnode as needing a modtime update */
890 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
892 /* if open unlinked, force it inactive */
895 vref
= vnode_ref (rvp
);
901 /* cnode is not open-unlinked, so re-lock cnode to sync */
902 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
908 /* hfs fsync rsrc fork to force to disk and update modtime */
909 result
= hfs_fsync (rvp
, MNT_NOWAIT
, 0, vfs_context_proc (ap
->a_context
));
917 * Standard HFS only supports native FinderInfo and Resource Forks.
919 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
922 attrsize
= uio_resid(uio
);
924 /* Enforce an upper limit. */
925 if (attrsize
> HFS_XATTR_MAXSIZE
) {
931 * Attempt to copy the users attr data before taking any locks,
932 * only if it will be an inline attribute. For larger attributes,
933 * the data will be directly read from the uio.
936 hfsmp
->hfs_max_inline_attrsize
!= 0 &&
937 attrsize
< hfsmp
->hfs_max_inline_attrsize
) {
938 MALLOC(user_data_ptr
, void *, attrsize
, M_TEMP
, M_WAITOK
);
939 if (user_data_ptr
== NULL
) {
944 result
= uiomove((caddr_t
)user_data_ptr
, attrsize
, uio
);
950 result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
957 * If we're trying to set a non-finderinfo, non-resourcefork EA, then
958 * call the breakout function.
960 result
= hfs_setxattr_internal (cp
, user_data_ptr
, attrsize
, ap
, VTOHFS(vp
), 0);
967 FREE(user_data_ptr
, M_TEMP
);
970 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
975 * hfs_setxattr_internal
977 * Internal function to set non-rsrc, non-finderinfo EAs to either the attribute B-Tree or
980 * See comments from hfs_getxattr_internal on why we need to pass 'hfsmp' and fileid here.
981 * The gist is that we could end up writing to the root folder which may not have a cnode.
984 * 1. cnode 'cp' is locked EXCLUSIVE before calling this function.
985 * 2. data_ptr contains data to be written. If gathering data from userland, this must be
986 * done before calling this function.
987 * 3. If data originates entirely in-kernel, use a null UIO, and ensure the size is less than
988 * hfsmp->hfs_max_inline_attrsize bytes long.
990 int hfs_setxattr_internal (struct cnode
*cp
, caddr_t data_ptr
, size_t attrsize
,
991 struct vnop_setxattr_args
*ap
, struct hfsmount
*hfsmp
,
994 uio_t uio
= ap
->a_uio
;
995 struct vnode
*vp
= ap
->a_vp
;
996 int started_transaction
= 0;
997 struct BTreeIterator
* iterator
= NULL
;
998 struct filefork
*btfile
= NULL
;
999 FSBufferDescriptor btdata
;
1000 HFSPlusAttrRecord attrdata
; /* 90 bytes */
1001 HFSPlusAttrRecord
*recp
= NULL
;
1002 HFSPlusExtentDescriptor
*extentptr
= NULL
;
1006 int allocatedblks
= 0;
1007 u_int32_t target_id
;
1011 target_id
= cp
->c_fileid
;
1014 if (target_id
!= 1) {
1016 * If we are manipulating something other than
1017 * the root folder (id 1), and do not have a cnode-in-hand,
1018 * then we must already hold the requisite b-tree locks from
1019 * earlier up the call stack. (See hfs_makenode)
1025 /* Start a transaction for our changes. */
1026 if (hfs_start_transaction(hfsmp
) != 0) {
1030 started_transaction
= 1;
1033 * Once we started the transaction, nobody can compete
1034 * with us, so make sure this file is still there.
1036 if ((cp
) && (cp
->c_flag
& C_NOEXISTS
)) {
1042 * If there isn't an attributes b-tree then create one.
1044 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1045 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
1046 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
1051 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
1052 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
1056 /* Take exclusive access to the attributes b-tree. */
1057 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1060 /* Build the b-tree key. */
1061 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1062 if (iterator
== NULL
) {
1066 bzero(iterator
, sizeof(*iterator
));
1067 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1072 /* Preflight for replace/create semantics. */
1073 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1074 btdata
.bufferAddress
= &attrdata
;
1075 btdata
.itemSize
= sizeof(attrdata
);
1076 btdata
.itemCount
= 1;
1077 exists
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
) == 0;
1079 /* Replace requires that the attribute already exists. */
1080 if ((ap
->a_options
& XATTR_REPLACE
) && !exists
) {
1084 /* Create requires that the attribute doesn't exist. */
1085 if ((ap
->a_options
& XATTR_CREATE
) && exists
) {
1090 /* If it won't fit inline then use extent-based attributes. */
1091 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
1092 size_t extentbufsize
;
1095 u_int32_t
*keystartblk
;
1100 * setxattrs originating from in-kernel are not supported if they are bigger
1101 * than the inline max size. Just return ENOATTR and force them to do it with a
1108 /* Get some blocks. */
1109 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
1110 extentbufsize
= blkcnt
* sizeof(HFSPlusExtentDescriptor
);
1111 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
1112 MALLOC(extentptr
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
1113 if (extentptr
== NULL
) {
1117 bzero(extentptr
, extentbufsize
);
1118 result
= alloc_attr_blks(hfsmp
, attrsize
, extentbufsize
, extentptr
, &allocatedblks
);
1121 goto exit
; /* no more space */
1123 /* Copy data into the blocks. */
1124 result
= write_attr_data(hfsmp
, uio
, attrsize
, extentptr
);
1127 const char *name
= vnode_getname(vp
);
1128 printf("hfs_setxattr: write_attr_data vol=%s err (%d) %s:%s\n",
1129 hfsmp
->vcbVN
, result
, name
? name
: "", ap
->a_name
);
1131 vnode_putname(name
);
1136 /* Now remove any previous attribute. */
1138 result
= remove_attribute_records(hfsmp
, iterator
);
1141 const char *name
= vnode_getname(vp
);
1142 printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n",
1143 hfsmp
->vcbVN
, result
, name
? name
: "", ap
->a_name
);
1145 vnode_putname(name
);
1150 /* Create attribute fork data record. */
1151 MALLOC(recp
, HFSPlusAttrRecord
*, sizeof(HFSPlusAttrRecord
), M_TEMP
, M_WAITOK
);
1156 btdata
.bufferAddress
= recp
;
1157 btdata
.itemCount
= 1;
1158 btdata
.itemSize
= sizeof(HFSPlusAttrForkData
);
1160 recp
->recordType
= kHFSPlusAttrForkData
;
1161 recp
->forkData
.reserved
= 0;
1162 recp
->forkData
.theFork
.logicalSize
= attrsize
;
1163 recp
->forkData
.theFork
.clumpSize
= 0;
1164 recp
->forkData
.theFork
.totalBlocks
= blkcnt
;
1165 bcopy(extentptr
, recp
->forkData
.theFork
.extents
, sizeof(HFSPlusExtentRecord
));
1167 (void) hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1169 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1171 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
1172 hfsmp
->vcbVN
, target_id
, ap
->a_name
, result
);
1175 extentblks
= count_extent_blocks(blkcnt
, recp
->forkData
.theFork
.extents
);
1176 blkcnt
-= extentblks
;
1177 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1180 /* Create overflow extents as needed. */
1181 while (blkcnt
> 0) {
1182 /* Initialize the key and record. */
1183 *keystartblk
+= (u_int32_t
)extentblks
;
1184 btdata
.itemSize
= sizeof(HFSPlusAttrExtents
);
1185 recp
->recordType
= kHFSPlusAttrExtents
;
1186 recp
->overflowExtents
.reserved
= 0;
1188 /* Copy the next set of extents. */
1189 i
+= kHFSPlusExtentDensity
;
1190 bcopy(&extentptr
[i
], recp
->overflowExtents
.extents
, sizeof(HFSPlusExtentRecord
));
1192 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1194 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
1195 hfsmp
->vcbVN
, target_id
, ap
->a_name
, result
);
1198 extentblks
= count_extent_blocks(blkcnt
, recp
->overflowExtents
.extents
);
1199 blkcnt
-= extentblks
;
1201 } else { /* Inline data */
1203 result
= remove_attribute_records(hfsmp
, iterator
);
1209 /* Calculate size of record rounded up to multiple of 2 bytes. */
1210 btdata
.itemSize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
1211 MALLOC(recp
, HFSPlusAttrRecord
*, btdata
.itemSize
, M_TEMP
, M_WAITOK
);
1216 recp
->recordType
= kHFSPlusAttrInlineData
;
1217 recp
->attrData
.reserved
[0] = 0;
1218 recp
->attrData
.reserved
[1] = 0;
1219 recp
->attrData
.attrSize
= attrsize
;
1221 /* Copy in the attribute data (if any). */
1224 bcopy(data_ptr
, &recp
->attrData
.attrData
, attrsize
);
1227 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
1228 * then deny the copy operation.
1234 result
= uiomove((caddr_t
)&recp
->attrData
.attrData
, attrsize
, uio
);
1242 (void) hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1244 btdata
.bufferAddress
= recp
;
1245 btdata
.itemCount
= 1;
1246 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1250 if (btfile
&& started_transaction
) {
1251 (void) BTFlushPath(btfile
);
1254 hfs_systemfile_unlock(hfsmp
, lockflags
);
1259 /* Setting an attribute only updates change time and not
1260 * modified time of the file.
1262 cp
->c_touch_chgtime
= TRUE
;
1263 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
1264 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1265 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
1267 (void) hfs_update(vp
, 0);
1270 if (started_transaction
) {
1271 if (result
&& allocatedblks
) {
1272 free_attr_blks(hfsmp
, allocatedblks
, extentptr
);
1274 hfs_end_transaction(hfsmp
);
1281 FREE(extentptr
, M_TEMP
);
1284 FREE(iterator
, M_TEMP
);
1294 * Remove an extended attribute.
1297 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
1299 struct vnop_removexattr_args {
1300 struct vnodeop_desc *a_desc;
1304 vfs_context_t a_context;
1308 struct vnode
*vp
= ap
->a_vp
;
1309 struct cnode
*cp
= VTOC(vp
);
1310 struct hfsmount
*hfsmp
;
1311 struct BTreeIterator
* iterator
= NULL
;
1314 time_t orig_ctime
=VTOC(vp
)->c_ctime
;
1316 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
1317 return (EINVAL
); /* invalid name */
1320 if (VNODE_IS_RSRC(vp
)) {
1325 if (hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1) && !(ap
->a_options
& XATTR_SHOWCOMPRESSION
)) {
1328 #endif /* HFS_COMPRESSION */
1330 check_for_tracked_file(vp
, orig_ctime
, NAMESPACE_HANDLER_METADATA_DELETE_OP
, NSPACE_REARM_NO_ARG
);
1332 /* If Resource Fork is non-empty then truncate it. */
1333 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1334 struct vnode
*rvp
= NULL
;
1336 if ( !vnode_isreg(vp
) ) {
1339 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1342 if ( !RSRC_FORK_EXISTS(cp
)) {
1346 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
, FALSE
);
1352 hfs_lock_truncate(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
1353 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1354 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1359 /* Start a transaction for encapsulating changes in
1360 * hfs_truncate() and hfs_update()
1362 if ((result
= hfs_start_transaction(hfsmp
))) {
1363 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1369 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, 0, ap
->a_context
);
1371 cp
->c_touch_chgtime
= TRUE
;
1372 cp
->c_flag
|= C_MODIFIED
;
1373 result
= hfs_update(vp
, FALSE
);
1376 hfs_end_transaction(hfsmp
);
1377 hfs_unlock_truncate(VTOC(rvp
), HFS_LOCK_DEFAULT
);
1378 hfs_unlock(VTOC(rvp
));
1383 /* Clear out the Finder Info. */
1384 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1385 void * finderinfo_start
;
1386 int finderinfo_size
;
1387 u_int8_t finderinfo
[32];
1388 u_int32_t date_added
, write_gen_counter
, document_id
;
1389 u_int8_t
*finfo
= NULL
;
1391 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1395 /* Use the local copy to store our temporary changes. */
1396 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
1399 /* Zero out the date added field in the local copy */
1400 hfs_zero_hidden_fields (cp
, finderinfo
);
1402 /* Don't expose a symlink's private type/creator. */
1403 if (vnode_islnk(vp
)) {
1404 struct FndrFileInfo
*fip
;
1406 fip
= (struct FndrFileInfo
*)&finderinfo
;
1411 /* Do the byte compare against the local copy */
1412 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
1418 * If there was other content, zero out everything except
1419 * type/creator and date added. First, save the date added.
1421 finfo
= cp
->c_finderinfo
;
1423 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
1424 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
1425 date_added
= extinfo
->date_added
;
1426 write_gen_counter
= extinfo
->write_gen_counter
;
1427 document_id
= extinfo
->document_id
;
1428 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
1429 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
1430 date_added
= extinfo
->date_added
;
1431 write_gen_counter
= extinfo
->write_gen_counter
;
1432 document_id
= extinfo
->document_id
;
1435 if (vnode_islnk(vp
)) {
1436 /* Ignore type/creator */
1437 finderinfo_start
= &cp
->c_finderinfo
[8];
1438 finderinfo_size
= sizeof(cp
->c_finderinfo
) - 8;
1440 finderinfo_start
= &cp
->c_finderinfo
[0];
1441 finderinfo_size
= sizeof(cp
->c_finderinfo
);
1443 bzero(finderinfo_start
, finderinfo_size
);
1446 /* Now restore the date added */
1447 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
1448 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
1449 extinfo
->date_added
= date_added
;
1450 extinfo
->write_gen_counter
= write_gen_counter
;
1451 extinfo
->document_id
= document_id
;
1452 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
1453 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
1454 extinfo
->date_added
= date_added
;
1455 extinfo
->write_gen_counter
= write_gen_counter
;
1456 extinfo
->document_id
= document_id
;
1459 /* Updating finderInfo updates change time and modified time */
1460 cp
->c_touch_chgtime
= TRUE
;
1461 cp
->c_flag
|= C_MODIFIED
;
1462 hfs_update(vp
, FALSE
);
1469 * Standard HFS only supports native FinderInfo and Resource Forks.
1471 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1474 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1478 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1479 if (iterator
== NULL
) {
1482 bzero(iterator
, sizeof(*iterator
));
1484 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1488 result
= hfs_buildattrkey(cp
->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1493 if (hfs_start_transaction(hfsmp
) != 0) {
1497 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1499 result
= remove_attribute_records(hfsmp
, iterator
);
1501 hfs_systemfile_unlock(hfsmp
, lockflags
);
1504 cp
->c_touch_chgtime
= TRUE
;
1506 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1508 /* If no more attributes exist, clear attribute bit */
1509 result
= file_attribute_exist(hfsmp
, cp
->c_fileid
);
1511 cp
->c_attr
.ca_recflags
&= ~kHFSHasAttributesMask
;
1513 if (result
== EEXIST
) {
1517 hfs_systemfile_unlock(hfsmp
, lockflags
);
1519 /* If ACL was removed, clear security bit */
1520 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1521 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
1523 (void) hfs_update(vp
, 0);
1526 hfs_end_transaction(hfsmp
);
1530 FREE(iterator
, M_TEMP
);
1531 return MacToVFSError(result
);
1534 /* Check if any attribute record exist for given fileID. This function
1535 * is called by hfs_vnop_removexattr to determine if it should clear the
1536 * attribute bit in the catalog record or not.
1538 * Note - you must acquire a shared lock on the attribute btree before
1539 * calling this function.
1542 * EEXIST - If attribute record was found
1543 * 0 - Attribute was not found
1544 * (other) - Other error (such as EIO)
1547 file_attribute_exist(struct hfsmount
*hfsmp
, uint32_t fileID
)
1549 HFSPlusAttrKey
*key
;
1550 struct BTreeIterator
* iterator
= NULL
;
1551 struct filefork
*btfile
;
1554 // if there's no attribute b-tree we sure as heck
1555 // can't have any attributes!
1556 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1560 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1561 if (iterator
== NULL
) {
1565 bzero(iterator
, sizeof(*iterator
));
1566 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1568 result
= hfs_buildattrkey(fileID
, NULL
, key
);
1573 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1574 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1575 if (result
&& (result
!= btNotFound
)) {
1579 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1580 /* If no next record was found or fileID for next record did not match,
1581 * no more attributes exist for this fileID
1583 if ((result
&& (result
== btNotFound
)) || (key
->fileID
!= fileID
)) {
1591 FREE(iterator
, M_TEMP
);
1598 * Remove all the records for a given attribute.
1600 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1601 * - A transaction must have been started.
1602 * - The Attribute b-tree file must be locked exclusive.
1603 * - The Allocation Bitmap file must be locked exclusive.
1604 * - The iterator key must be initialized.
1607 remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
)
1609 struct filefork
*btfile
;
1610 FSBufferDescriptor btdata
;
1611 HFSPlusAttrRecord attrdata
; /* 90 bytes */
1615 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1617 btdata
.bufferAddress
= &attrdata
;
1618 btdata
.itemSize
= sizeof(attrdata
);
1619 btdata
.itemCount
= 1;
1620 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1622 goto exit
; /* no records. */
1625 * Free the blocks from extent based attributes.
1627 * Note that the block references (btree records) are removed
1628 * before releasing the blocks in the allocation bitmap.
1630 if (attrdata
.recordType
== kHFSPlusAttrForkData
) {
1633 u_int32_t
*keystartblk
;
1635 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
1636 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize
, sizeof(HFSPlusAttrForkData
));
1638 totalblks
= attrdata
.forkData
.theFork
.totalBlocks
;
1640 /* Process the first 8 extents. */
1641 extentblks
= count_extent_blocks(totalblks
, attrdata
.forkData
.theFork
.extents
);
1642 if (extentblks
> totalblks
)
1643 panic("hfs: remove_attribute_records: corruption...");
1644 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1645 free_attr_blks(hfsmp
, extentblks
, attrdata
.forkData
.theFork
.extents
);
1647 totalblks
-= extentblks
;
1648 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1650 /* Process any overflow extents. */
1652 *keystartblk
+= (u_int32_t
)extentblks
;
1654 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1656 (attrdata
.recordType
!= kHFSPlusAttrExtents
) ||
1657 (datasize
< sizeof(HFSPlusAttrExtents
))) {
1658 printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n",
1659 hfsmp
->vcbVN
, MacToVFSError(result
), attrdata
.recordType
!= kHFSPlusAttrExtents
, totalblks
);
1661 break; /* break from while */
1663 /* Process the next 8 extents. */
1664 extentblks
= count_extent_blocks(totalblks
, attrdata
.overflowExtents
.extents
);
1665 if (extentblks
> totalblks
)
1666 panic("hfs: remove_attribute_records: corruption...");
1667 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1668 free_attr_blks(hfsmp
, extentblks
, attrdata
.overflowExtents
.extents
);
1670 totalblks
-= extentblks
;
1673 result
= BTDeleteRecord(btfile
, iterator
);
1675 (void) BTFlushPath(btfile
);
1677 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
1682 * Retrieve the list of extended attribute names.
1685 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
1687 struct vnop_listxattr_args {
1688 struct vnodeop_desc *a_desc;
1693 vfs_context_t a_context;
1696 struct vnode
*vp
= ap
->a_vp
;
1697 struct cnode
*cp
= VTOC(vp
);
1698 struct hfsmount
*hfsmp
;
1699 uio_t uio
= ap
->a_uio
;
1700 struct BTreeIterator
* iterator
= NULL
;
1701 struct filefork
*btfile
;
1702 struct listattr_callback_state state
;
1703 user_addr_t user_start
= 0;
1704 user_size_t user_len
= 0;
1707 u_int8_t finderinfo
[32];
1710 if (VNODE_IS_RSRC(vp
)) {
1715 int compressed
= hfs_file_is_compressed(cp
, 1); /* 1 == don't take the cnode lock */
1716 #endif /* HFS_COMPRESSION */
1722 * Take the truncate lock; this serializes us against the ioctl
1723 * to truncate data & reset the decmpfs state
1724 * in the compressed file handler.
1726 hfs_lock_truncate(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
1728 /* Now the regular cnode lock (shared) */
1729 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
1730 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1735 * Make a copy of the cnode's finderinfo to a local so we can
1736 * zero out the date added field. Also zero out the private type/creator
1739 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
1740 hfs_zero_hidden_fields (cp
, finderinfo
);
1742 /* Don't expose a symlink's private type/creator. */
1743 if (vnode_islnk(vp
)) {
1744 struct FndrFileInfo
*fip
;
1746 fip
= (struct FndrFileInfo
*)&finderinfo
;
1752 /* If Finder Info is non-empty then export it's name. */
1753 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) != 0) {
1755 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
1756 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1760 result
= uiomove(XATTR_FINDERINFO_NAME
,
1761 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1766 /* If Resource Fork is non-empty then export it's name. */
1767 if (S_ISREG(cp
->c_mode
) && RSRC_FORK_EXISTS(cp
)) {
1769 if ((ap
->a_options
& XATTR_SHOWCOMPRESSION
) ||
1771 !hfs_hides_rsrc(ap
->a_context
, VTOC(vp
), 1) /* 1 == don't take the cnode lock */
1773 #endif /* HFS_COMPRESSION */
1776 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1777 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1781 result
= uiomove(XATTR_RESOURCEFORK_NAME
,
1782 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
1789 * Standard HFS only supports native FinderInfo and Resource Forks.
1790 * Return at this point.
1792 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1796 /* Bail if we don't have any extended attributes. */
1797 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
1798 (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
1802 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1804 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1805 if (iterator
== NULL
) {
1809 bzero(iterator
, sizeof(*iterator
));
1810 result
= hfs_buildattrkey(cp
->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
1815 * Lock the user's buffer here so that we won't fault on
1816 * it in uiomove while holding the attributes b-tree lock.
1818 if (uio
&& uio_isuserspace(uio
)) {
1819 user_start
= uio_curriovbase(uio
);
1820 user_len
= uio_curriovlen(uio
);
1822 if ((result
= vslock(user_start
, user_len
)) != 0) {
1827 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1829 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1830 if (result
&& result
!= btNotFound
) {
1831 hfs_systemfile_unlock(hfsmp
, lockflags
);
1835 state
.fileID
= cp
->c_fileid
;
1840 state
.showcompressed
= !compressed
|| ap
->a_options
& XATTR_SHOWCOMPRESSION
;
1841 state
.ctx
= ap
->a_context
;
1843 #endif /* HFS_COMPRESSION */
1846 * Process entries starting just after iterator->key.
1848 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
1849 (IterateCallBackProcPtr
)listattr_callback
, &state
);
1850 hfs_systemfile_unlock(hfsmp
, lockflags
);
1852 *ap
->a_size
+= state
.size
;
1855 if (state
.result
|| result
== btNotFound
)
1856 result
= state
.result
;
1860 vsunlock(user_start
, user_len
, TRUE
);
1863 FREE(iterator
, M_TEMP
);
1866 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1868 return MacToVFSError(result
);
1873 * Callback - called for each attribute record
1876 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
1878 char attrname
[XATTR_MAXNAMELEN
+ 1];
1882 if (state
->fileID
!= key
->fileID
) {
1884 return (0); /* stop */
1887 * Skip over non-primary keys
1889 if (key
->startBlock
!= 0) {
1890 return (1); /* continue */
1893 /* Convert the attribute name into UTF-8. */
1894 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
1895 (u_int8_t
*)attrname
, (size_t *)&bytecount
, sizeof(attrname
), '/', 0);
1897 state
->result
= result
;
1898 return (0); /* stop */
1900 bytecount
++; /* account for null termination char */
1902 if (xattr_protected(attrname
))
1903 return (1); /* continue */
1906 if (!state
->showcompressed
&& hfs_hides_xattr(state
->ctx
, VTOC(state
->vp
), attrname
, 1) ) /* 1 == don't take the cnode lock */
1907 return 1; /* continue */
1908 #endif /* HFS_COMPRESSION */
1910 if (state
->uio
== NULL
) {
1911 state
->size
+= bytecount
;
1913 if (bytecount
> uio_resid(state
->uio
)) {
1914 state
->result
= ERANGE
;
1915 return (0); /* stop */
1917 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
1919 state
->result
= result
;
1920 return (0); /* stop */
1923 return (1); /* continue */
1927 * Remove all the attributes from a cnode.
1929 * This function creates/ends its own transaction so that each
1930 * attribute is deleted in its own transaction (to avoid having
1931 * a transaction grow too large).
1933 * This function takes the necessary locks on the attribute
1934 * b-tree file and the allocation (bitmap) file.
1937 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
)
1939 BTreeIterator
*iterator
= NULL
;
1940 HFSPlusAttrKey
*key
;
1941 struct filefork
*btfile
;
1942 int result
, lockflags
;
1944 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1947 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1949 MALLOC(iterator
, BTreeIterator
*, sizeof(BTreeIterator
), M_TEMP
, M_WAITOK
);
1950 if (iterator
== NULL
) {
1953 bzero(iterator
, sizeof(BTreeIterator
));
1954 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1956 /* Loop until there are no more attributes for this file id */
1958 if (hfs_start_transaction(hfsmp
) != 0) {
1963 /* Lock the attribute b-tree and the allocation (bitmap) files */
1964 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1967 * Go to first possible attribute key/record pair
1969 (void) hfs_buildattrkey(fileid
, NULL
, key
);
1970 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1971 if (result
|| key
->fileID
!= fileid
) {
1972 hfs_systemfile_unlock(hfsmp
, lockflags
);
1973 hfs_end_transaction(hfsmp
);
1976 result
= remove_attribute_records(hfsmp
, iterator
);
1978 #if HFS_XATTR_VERBOSE
1980 printf("hfs_removeallattr: unexpected err %d\n", result
);
1983 hfs_systemfile_unlock(hfsmp
, lockflags
);
1984 hfs_end_transaction(hfsmp
);
1989 FREE(iterator
, M_TEMP
);
1990 return (result
== btNotFound
? 0: MacToVFSError(result
));
1995 hfs_xattr_init(struct hfsmount
* hfsmp
)
1998 * If there isn't an attributes b-tree then create one.
2000 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
) &&
2001 (hfsmp
->hfs_attribute_vp
== NULL
) &&
2002 !(hfsmp
->hfs_flags
& HFS_READ_ONLY
)) {
2003 (void) hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
2004 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
2006 if (hfsmp
->hfs_attribute_vp
)
2007 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
2011 * Enable/Disable volume attributes stored as EA for root file system.
2012 * Supported attributes are -
2013 * 1. Extent-based Extended Attributes
2016 hfs_set_volxattr(struct hfsmount
*hfsmp
, unsigned int xattrtype
, int state
)
2018 struct BTreeIterator
* iterator
= NULL
;
2019 struct filefork
*btfile
;
2023 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
2026 if (xattrtype
!= HFS_SET_XATTREXTENTS_STATE
) {
2031 * If there isn't an attributes b-tree then create one.
2033 if (hfsmp
->hfs_attribute_vp
== NULL
) {
2034 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
2035 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
2041 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2042 if (iterator
== NULL
) {
2045 bzero(iterator
, sizeof(*iterator
));
2048 * Build a b-tree key.
2049 * We use the root's parent id (1) to hold this volume attribute.
2051 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_XATTREXTENTS_NAME
,
2052 (HFSPlusAttrKey
*)&iterator
->key
);
2054 /* Start a transaction for our changes. */
2055 if (hfs_start_transaction(hfsmp
) != 0) {
2059 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
2061 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
2064 /* Remove the attribute. */
2065 result
= BTDeleteRecord(btfile
, iterator
);
2066 if (result
== btNotFound
)
2069 FSBufferDescriptor btdata
;
2070 HFSPlusAttrData attrdata
;
2073 datasize
= sizeof(attrdata
);
2074 btdata
.bufferAddress
= &attrdata
;
2075 btdata
.itemSize
= datasize
;
2076 btdata
.itemCount
= 1;
2077 attrdata
.recordType
= kHFSPlusAttrInlineData
;
2078 attrdata
.reserved
[0] = 0;
2079 attrdata
.reserved
[1] = 0;
2080 attrdata
.attrSize
= 2;
2081 attrdata
.attrData
[0] = 0;
2082 attrdata
.attrData
[1] = 0;
2084 /* Insert the attribute. */
2085 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
2086 if (result
== btExists
)
2089 (void) BTFlushPath(btfile
);
2091 hfs_systemfile_unlock(hfsmp
, lockflags
);
2093 /* Finish the transaction of our changes. */
2094 hfs_end_transaction(hfsmp
);
2096 /* Update the state in the mount point */
2097 hfs_lock_mount (hfsmp
);
2099 hfsmp
->hfs_flags
&= ~HFS_XATTR_EXTENTS
;
2101 hfsmp
->hfs_flags
|= HFS_XATTR_EXTENTS
;
2103 hfs_unlock_mount (hfsmp
);
2107 FREE(iterator
, M_TEMP
);
2109 return MacToVFSError(result
);
2114 * hfs_attrkeycompare - compare two attribute b-tree keys.
2116 * The name portion of the key is compared using a 16-bit binary comparison.
2117 * This is called from the b-tree code.
2121 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
2123 u_int32_t searchFileID
, trialFileID
;
2126 searchFileID
= searchKey
->fileID
;
2127 trialFileID
= trialKey
->fileID
;
2130 if (searchFileID
> trialFileID
) {
2132 } else if (searchFileID
< trialFileID
) {
2135 u_int16_t
* str1
= &searchKey
->attrName
[0];
2136 u_int16_t
* str2
= &trialKey
->attrName
[0];
2137 int length1
= searchKey
->attrNameLen
;
2138 int length2
= trialKey
->attrNameLen
;
2142 if (length1
< length2
) {
2145 } else if (length1
> length2
) {
2168 * Names are equal; compare startBlock
2170 if (searchKey
->startBlock
== trialKey
->startBlock
) {
2173 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
2182 * hfs_buildattrkey - build an Attribute b-tree key
2186 hfs_buildattrkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
2189 size_t unicodeBytes
= 0;
2191 if (attrname
!= NULL
) {
2193 * Convert filename from UTF-8 into Unicode
2195 result
= utf8_decodestr((const u_int8_t
*)attrname
, strlen(attrname
), key
->attrName
,
2196 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
2198 if (result
!= ENAMETOOLONG
)
2199 result
= EINVAL
; /* name has invalid characters */
2202 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
2203 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
2205 key
->attrNameLen
= 0;
2206 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
2209 key
->fileID
= fileID
;
2210 key
->startBlock
= 0;
2216 * getnodecount - calculate starting node count for attributes b-tree.
2219 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
2221 u_int64_t freebytes
;
2222 u_int64_t calcbytes
;
2225 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
2226 * 10.5: Attempt to be as big as the catalog clump size.
2228 * Use no more than 10 % of the remaining free space.
2230 freebytes
= (u_int64_t
)hfs_freeblks(hfsmp
, 0) * (u_int64_t
)hfsmp
->blockSize
;
2232 calcbytes
= MIN(hfsmp
->hfs_catalog_cp
->c_datafork
->ff_size
/ 5, 20 * 1024 * 1024);
2234 calcbytes
= MAX(calcbytes
, hfsmp
->hfs_catalog_cp
->c_datafork
->ff_clumpsize
);
2236 calcbytes
= MIN(calcbytes
, freebytes
/ 10);
2238 return (MAX(2, (int)(calcbytes
/ nodesize
)));
2243 * getmaxinlineattrsize - calculate maximum inline attribute size.
2245 * This yields 3,802 bytes for an 8K node size.
2248 getmaxinlineattrsize(struct vnode
* attrvp
)
2250 struct BTreeInfoRec btinfo
;
2251 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
2254 if (attrvp
!= NULL
) {
2255 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2256 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
2257 nodesize
= btinfo
.nodeSize
;
2258 hfs_unlock(VTOC(attrvp
));
2261 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
2262 maxsize
-= 3 * sizeof(u_int16_t
); /* minus 3 index slots */
2263 maxsize
/= 2; /* 2 key/rec pairs minumum */
2264 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
2265 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
2266 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */
2272 * Initialize vnode for attribute data I/O.
2276 * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp
2277 * - an iocount is taken on the attrdata vnode which exists
2278 * for the entire duration of the mount. It is only dropped
2280 * - the attrdata cnode is not locked
2283 * - returns non-zero value
2284 * - the caller does not have to worry about any locks or references
2286 int init_attrdata_vnode(struct hfsmount
*hfsmp
)
2290 struct cat_desc cat_desc
;
2291 struct cat_attr cat_attr
;
2292 struct cat_fork cat_fork
;
2293 int newvnode_flags
= 0;
2295 bzero(&cat_desc
, sizeof(cat_desc
));
2296 cat_desc
.cd_parentcnid
= kHFSRootParentID
;
2297 cat_desc
.cd_nameptr
= (const u_int8_t
*)hfs_attrdatafilename
;
2298 cat_desc
.cd_namelen
= strlen(hfs_attrdatafilename
);
2299 cat_desc
.cd_cnid
= kHFSAttributeDataFileID
;
2300 /* Tag vnode as system file, note that we can still use cluster I/O */
2301 cat_desc
.cd_flags
|= CD_ISMETA
;
2303 bzero(&cat_attr
, sizeof(cat_attr
));
2304 cat_attr
.ca_linkcount
= 1;
2305 cat_attr
.ca_mode
= S_IFREG
;
2306 cat_attr
.ca_fileid
= cat_desc
.cd_cnid
;
2307 cat_attr
.ca_blocks
= hfsmp
->totalBlocks
;
2310 * The attribute data file is a virtual file that spans the
2311 * entire file system space.
2313 * Each extent-based attribute occupies a unique portion of
2314 * in this virtual file. The cluster I/O is done using actual
2315 * allocation block offsets so no additional mapping is needed
2316 * for the VNOP_BLOCKMAP call.
2318 * This approach allows the attribute data to be cached without
2319 * incurring the high cost of using a separate vnode per attribute.
2321 * Since we need to acquire the attribute b-tree file lock anyways,
2322 * the virtual file doesn't introduce any additional serialization.
2324 bzero(&cat_fork
, sizeof(cat_fork
));
2325 cat_fork
.cf_size
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
2326 cat_fork
.cf_blocks
= hfsmp
->totalBlocks
;
2327 cat_fork
.cf_extents
[0].startBlock
= 0;
2328 cat_fork
.cf_extents
[0].blockCount
= cat_fork
.cf_blocks
;
2330 result
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cat_desc
, 0, &cat_attr
,
2331 &cat_fork
, &vp
, &newvnode_flags
);
2333 hfsmp
->hfs_attrdata_vp
= vp
;
2334 hfs_unlock(VTOC(vp
));
2340 * Read an extent based attribute.
2343 read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2345 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2353 hfs_lock_truncate(VTOC(evp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2355 bufsize
= (int)uio_resid(uio
);
2356 attrsize
= (int)datasize
;
2357 blksize
= (int)hfsmp
->blockSize
;
2360 * Read the attribute data one extent at a time.
2361 * For the typical case there is only one extent.
2363 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2364 iosize
= extents
[i
].blockCount
* blksize
;
2365 iosize
= MIN(iosize
, attrsize
);
2366 iosize
= MIN(iosize
, bufsize
);
2367 uio_setresid(uio
, iosize
);
2368 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2370 result
= cluster_read(evp
, uio
, VTOF(evp
)->ff_size
, IO_SYNC
| IO_UNIT
);
2372 #if HFS_XATTR_VERBOSE
2373 printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n",
2374 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2381 uio_setresid(uio
, bufsize
);
2382 uio_setoffset(uio
, datasize
);
2384 hfs_unlock_truncate(VTOC(evp
), HFS_LOCK_DEFAULT
);
2389 * Write an extent based attribute.
2392 write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2394 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2403 hfs_lock_truncate(VTOC(evp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2405 bufsize
= uio_resid(uio
);
2406 attrsize
= (int) datasize
;
2407 blksize
= (int) hfsmp
->blockSize
;
2408 filesize
= VTOF(evp
)->ff_size
;
2411 * Write the attribute data one extent at a time.
2413 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2414 iosize
= extents
[i
].blockCount
* blksize
;
2415 iosize
= MIN(iosize
, attrsize
);
2416 iosize
= MIN(iosize
, bufsize
);
2417 uio_setresid(uio
, iosize
);
2418 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2420 result
= cluster_write(evp
, uio
, filesize
, filesize
, filesize
,
2421 (off_t
) 0, IO_SYNC
| IO_UNIT
);
2422 #if HFS_XATTR_VERBOSE
2423 printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n",
2424 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2431 uio_setresid(uio
, bufsize
);
2432 uio_setoffset(uio
, datasize
);
2434 hfs_unlock_truncate(VTOC(evp
), HFS_LOCK_DEFAULT
);
2439 * Allocate blocks for an extent based attribute.
2442 alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
)
2451 startblk
= hfsmp
->hfs_metazone_end
;
2452 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
2453 if (blkcnt
> (int)hfs_freeblks(hfsmp
, 0)) {
2457 maxextents
= extentbufsize
/ sizeof(HFSPlusExtentDescriptor
);
2459 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2461 for (i
= 0; (blkcnt
> 0) && (i
< maxextents
); i
++) {
2462 /* Try allocating and see if we find something decent */
2463 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, 0,
2464 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
2466 * If we couldn't find anything, then re-try the allocation but allow
2469 if (result
== dskFulErr
) {
2470 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, HFS_ALLOC_FLUSHTXN
,
2471 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
2475 #if HFS_XATTR_VERBOSE
2476 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2477 blkcnt
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2480 extents
[i
].startBlock
= 0;
2481 extents
[i
].blockCount
= 0;
2484 blkcnt
-= extents
[i
].blockCount
;
2485 startblk
= extents
[i
].startBlock
+ extents
[i
].blockCount
;
2488 * If it didn't fit in the extents buffer then bail.
2493 #if HFS_XATTR_VERBOSE
2494 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt
);
2496 for (; i
>= 0; i
--) {
2497 if ((blkcnt
= extents
[i
].blockCount
) != 0) {
2498 (void) BlockDeallocate(hfsmp
, extents
[i
].startBlock
, blkcnt
, 0);
2499 extents
[i
].startBlock
= 0;
2500 extents
[i
].blockCount
= 0;
2505 hfs_systemfile_unlock(hfsmp
, lockflags
);
2506 return MacToVFSError(result
);
2510 * Release blocks from an extent based attribute.
2513 free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
)
2515 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2516 int remblks
= blkcnt
;
2520 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2522 for (i
= 0; (remblks
> 0) && (extents
[i
].blockCount
!= 0); i
++) {
2523 if (extents
[i
].blockCount
> (u_int32_t
)blkcnt
) {
2524 #if HFS_XATTR_VERBOSE
2525 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
2526 extents
[i
].startBlock
, extents
[i
].blockCount
);
2528 extents
[i
].blockCount
= 0;
2531 if (extents
[i
].startBlock
== 0) {
2534 (void)BlockDeallocate(hfsmp
, extents
[i
].startBlock
, extents
[i
].blockCount
, 0);
2535 remblks
-= extents
[i
].blockCount
;
2536 extents
[i
].startBlock
= 0;
2537 extents
[i
].blockCount
= 0;
2539 #if HFS_XATTR_VERBOSE
2540 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2541 extents
[i
].startBlock
, extents
[i
].blockCount
);
2543 /* Discard any resident pages for this block range. */
2547 start
= (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)hfsmp
->blockSize
;
2548 end
= start
+ (u_int64_t
)extents
[i
].blockCount
* (u_int64_t
)hfsmp
->blockSize
;
2549 (void) ubc_msync(hfsmp
->hfs_attrdata_vp
, start
, end
, &start
, UBC_INVALIDATE
);
2553 hfs_systemfile_unlock(hfsmp
, lockflags
);
2557 has_overflow_extents(HFSPlusForkData
*forkdata
)
2561 if (forkdata
->extents
[7].blockCount
== 0)
2564 blocks
= forkdata
->extents
[0].blockCount
+
2565 forkdata
->extents
[1].blockCount
+
2566 forkdata
->extents
[2].blockCount
+
2567 forkdata
->extents
[3].blockCount
+
2568 forkdata
->extents
[4].blockCount
+
2569 forkdata
->extents
[5].blockCount
+
2570 forkdata
->extents
[6].blockCount
+
2571 forkdata
->extents
[7].blockCount
;
2573 return (forkdata
->totalBlocks
> blocks
);
2577 count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
)
2582 for (i
= 0, blocks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
2583 /* Ignore obvious bogus extents. */
2584 if (extents
[i
].blockCount
> (u_int32_t
)maxblks
)
2586 if (extents
[i
].startBlock
== 0 || extents
[i
].blockCount
== 0)
2588 blocks
+= extents
[i
].blockCount
;