2 * Copyright (c) 2004-2014 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>
41 #include <sys/cprotect.h>
42 #include <sys/uio_internal.h>
45 #include "hfs_cnode.h"
46 #include "hfs_mount.h"
47 #include "hfs_format.h"
48 #include "hfs_endian.h"
49 #include "hfs_btreeio.h"
50 #include "hfs_fsctl.h"
52 #include "hfscommon/headers/BTreesInternal.h"
54 #define HFS_XATTR_VERBOSE 0
56 #define ATTRIBUTE_FILE_NODE_SIZE 8192
59 /* State information for the listattr_callback callback function. */
60 struct listattr_callback_state
{
69 #endif /* HFS_COMPRESSION */
73 /* HFS Internal Names */
74 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
75 #define XATTR_XATTREXTENTS_NAME "system.xattrextents"
77 static u_int32_t emptyfinfo
[8] = {0};
79 static int hfs_zero_hidden_fields (struct cnode
*cp
, u_int8_t
*finderinfo
);
81 const char hfs_attrdatafilename
[] = "Attribute Data";
83 static int listattr_callback(const HFSPlusAttrKey
*key
, const HFSPlusAttrData
*data
,
84 struct listattr_callback_state
*state
);
86 static int remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
);
88 static int getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
);
90 static size_t getmaxinlineattrsize(struct vnode
* attrvp
);
92 static int read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
);
94 static int write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
);
96 static int alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
);
98 static void free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
);
100 static int has_overflow_extents(HFSPlusForkData
*forkdata
);
102 static int count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
);
106 * Obtain the vnode for a stream.
109 hfs_vnop_getnamedstream(struct vnop_getnamedstream_args
* ap
)
111 vnode_t vp
= ap
->a_vp
;
112 vnode_t
*svpp
= ap
->a_svpp
;
119 * We only support the "com.apple.ResourceFork" stream.
121 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
125 if ( !S_ISREG(cp
->c_mode
) ) {
129 int hide_rsrc
= hfs_hides_rsrc(ap
->a_context
, VTOC(vp
), 1);
130 #endif /* HFS_COMPRESSION */
131 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
134 if ((!hfs_has_rsrc(cp
)
137 #endif /* HFS_COMPRESSION */
138 ) && (ap
->a_operation
!= NS_OPEN
)) {
142 error
= hfs_vgetrsrc(VTOHFS(vp
), vp
, svpp
);
152 hfs_vnop_makenamedstream(struct vnop_makenamedstream_args
* ap
)
154 vnode_t vp
= ap
->a_vp
;
155 vnode_t
*svpp
= ap
->a_svpp
;
162 * We only support the "com.apple.ResourceFork" stream.
164 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
168 if ( !S_ISREG(cp
->c_mode
) ) {
172 if (hfs_hides_rsrc(ap
->a_context
, VTOC(vp
), 1)) {
173 if (VNODE_IS_RSRC(vp
)) {
176 error
= decmpfs_decompress_file(vp
, VTOCMP(vp
), -1, 1, 0);
181 #endif /* HFS_COMPRESSION */
182 if ((error
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
185 error
= hfs_vgetrsrc(VTOHFS(vp
), vp
, svpp
);
195 hfs_vnop_removenamedstream(struct vnop_removenamedstream_args
* ap
)
197 vnode_t svp
= ap
->a_svp
;
198 cnode_t
*scp
= VTOC(svp
);
202 * We only support the "com.apple.ResourceFork" stream.
204 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
208 if (hfs_hides_rsrc(ap
->a_context
, scp
, 1)) {
212 #endif /* HFS_COMPRESSION */
214 hfs_lock_truncate(scp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
215 if (VTOF(svp
)->ff_size
) {
216 // hfs_truncate will deal with the cnode lock
217 error
= hfs_truncate(svp
, 0, IO_NDELAY
, 0, ap
->a_context
);
219 hfs_unlock_truncate(scp
, HFS_LOCK_DEFAULT
);
226 /* Zero out the date added field for the specified cnode */
227 static int hfs_zero_hidden_fields (struct cnode
*cp
, u_int8_t
*finderinfo
)
229 u_int8_t
*finfo
= finderinfo
;
231 /* Advance finfo by 16 bytes to the 2nd half of the finderinfo */
234 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
235 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
236 extinfo
->document_id
= 0;
237 extinfo
->date_added
= 0;
238 extinfo
->write_gen_counter
= 0;
239 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
240 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
241 extinfo
->document_id
= 0;
242 extinfo
->date_added
= 0;
243 extinfo
->write_gen_counter
= 0;
245 /* Return an error */
253 * Retrieve the data of an extended attribute.
256 hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
)
258 struct vnop_getxattr_args {
259 struct vnodeop_desc *a_desc;
265 vfs_context_t a_context;
269 struct vnode
*vp
= ap
->a_vp
;
271 struct hfsmount
*hfsmp
;
272 uio_t uio
= ap
->a_uio
;
277 if (vp
== cp
->c_vp
) {
279 int decmpfs_hide
= hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1); /* 1 == don't take the cnode lock */
280 if (decmpfs_hide
&& !(ap
->a_options
& XATTR_SHOWCOMPRESSION
))
282 #endif /* HFS_COMPRESSION */
284 /* Get the Finder Info. */
285 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
286 u_int8_t finderinfo
[32];
289 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
292 /* Make a copy since we may not export all of it. */
293 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
296 /* Zero out the date added field in the local copy */
297 hfs_zero_hidden_fields (cp
, finderinfo
);
299 /* Don't expose a symlink's private type/creator. */
300 if (vnode_islnk(vp
)) {
301 struct FndrFileInfo
*fip
;
303 fip
= (struct FndrFileInfo
*)&finderinfo
;
307 /* If Finder Info is empty then it doesn't exist. */
308 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
312 *ap
->a_size
= bufsize
;
315 if ((user_size_t
)uio_resid(uio
) < bufsize
)
318 result
= uiomove((caddr_t
)&finderinfo
, bufsize
, uio
);
322 /* Read the Resource Fork. */
323 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
324 struct vnode
*rvp
= NULL
;
325 int openunlinked
= 0;
328 if ( !S_ISREG(cp
->c_mode
) ) {
331 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
334 namelen
= cp
->c_desc
.cd_namelen
;
336 if (!hfs_has_rsrc(cp
)) {
341 if ((cp
->c_flag
& C_DELETED
) && (namelen
== 0)) {
345 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
);
351 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
354 user_ssize_t uio_size
= 0;
356 uio_size
= uio_resid(uio
);
357 #endif /* HFS_COMPRESSION */
358 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
362 (uio_resid(uio
) == uio_size
)) {
364 * We intentionally make the above call to VNOP_READ so that
365 * it can return an authorization/permission/etc. Error
366 * based on ap->a_context and thus deny this operation;
367 * in that case, result != 0 and we won't proceed.
369 * However, if result == 0, it will have returned no data
370 * because hfs_vnop_read hid the resource fork
371 * (hence uio_resid(uio) == uio_size, i.e. the uio is untouched)
373 * In that case, we try again with the decmpfs_ctx context
374 * to get the actual data
376 result
= VNOP_READ(rvp
, uio
, 0, decmpfs_ctx
);
378 #endif /* HFS_COMPRESSION */
380 /* force the rsrc fork vnode to recycle right away */
383 vref
= vnode_ref (rvp
);
395 * Standard HFS only supports native FinderInfo and Resource Forks.
397 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
401 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
405 /* Check for non-rsrc, non-finderinfo EAs */
406 result
= hfs_getxattr_internal (cp
, ap
, VTOHFS(cp
->c_vp
), 0);
410 return MacToVFSError(result
);
413 // Has same limitations as hfs_getxattr_internal below
414 int hfs_xattr_read(vnode_t vp
, const char *name
, void *data
, size_t *size
)
416 char uio_buf
[UIO_SIZEOF(1)];
417 uio_t uio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
, uio_buf
,
420 uio_addiov(uio
, CAST_USER_ADDR_T(data
), *size
);
422 struct vnop_getxattr_args args
= {
428 return hfs_getxattr_internal(VTOC(vp
), &args
, VTOHFS(vp
), 0);
434 * We break out this internal function which searches the attributes B-Tree and the
435 * overflow extents file to find non-resource, non-finderinfo EAs. There may be cases
436 * where we need to get EAs in contexts where we are already holding the cnode lock,
437 * and to re-enter hfs_vnop_getxattr would cause us to double-lock the cnode. Instead,
438 * we can just directly call this function.
440 * We pass the hfsmp argument directly here because we may not necessarily have a cnode to
441 * operate on. Under normal conditions, we have a file or directory to query, but if we
442 * are operating on the root directory (id 1), then we may not have a cnode. In this case, if hte
443 * 'cp' argument is NULL, then we need to use the 'fileid' argument as the entry to manipulate
445 * NOTE: This function assumes the cnode lock for 'cp' is held exclusive or shared.
447 int hfs_getxattr_internal (struct cnode
*cp
, struct vnop_getxattr_args
*ap
,
448 struct hfsmount
*hfsmp
, u_int32_t fileid
)
451 struct filefork
*btfile
;
452 struct BTreeIterator
* iterator
= NULL
;
454 HFSPlusAttrRecord
*recp
= NULL
;
455 FSBufferDescriptor btdata
;
458 u_int16_t datasize
= 0;
459 uio_t uio
= ap
->a_uio
;
460 u_int32_t target_id
= 0;
463 target_id
= cp
->c_fileid
;
469 /* Bail if we don't have an EA B-Tree. */
470 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
471 ((cp
) && (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0)) {
476 /* Initialize the B-Tree iterator for searching for the proper EA */
477 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
479 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
480 if (iterator
== NULL
) {
484 bzero(iterator
, sizeof(*iterator
));
486 /* Allocate memory for reading in the attribute record. This buffer is
487 * big enough to read in all types of attribute records. It is not big
488 * enough to read inline attribute data which is read in later.
490 MALLOC(recp
, HFSPlusAttrRecord
*, sizeof(HFSPlusAttrRecord
), M_TEMP
, M_WAITOK
);
495 btdata
.bufferAddress
= recp
;
496 btdata
.itemSize
= sizeof(HFSPlusAttrRecord
);
497 btdata
.itemCount
= 1;
499 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
504 /* Lookup the attribute in the Attribute B-Tree */
505 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
506 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
507 hfs_systemfile_unlock(hfsmp
, lockflags
);
510 if (result
== btNotFound
) {
517 * Operate differently if we have inline EAs that can fit in the attribute B-Tree or if
518 * we have extent based EAs.
520 switch (recp
->recordType
) {
522 /* Attribute fits in the Attribute B-Tree */
523 case kHFSPlusAttrInlineData
: {
525 * Sanity check record size. It's not required to have any
526 * user data, so the minimum size is 2 bytes less that the
527 * size of HFSPlusAttrData (since HFSPlusAttrData struct
528 * has 2 bytes set aside for attribute data).
530 if (datasize
< (sizeof(HFSPlusAttrData
) - 2)) {
531 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
532 hfsmp
->vcbVN
, target_id
, ap
->a_name
, datasize
, sizeof(HFSPlusAttrData
));
536 *ap
->a_size
= recp
->attrData
.attrSize
;
537 if (uio
&& recp
->attrData
.attrSize
!= 0) {
538 if (*ap
->a_size
> (user_size_t
)uio_resid(uio
)) {
539 /* User provided buffer is not large enough for the xattr data */
542 /* Previous BTreeSearchRecord() read in only the attribute record,
543 * and not the attribute data. Now allocate enough memory for
544 * both attribute record and data, and read the attribute record again.
546 bufsize
= sizeof(HFSPlusAttrData
) - 2 + recp
->attrData
.attrSize
;
548 MALLOC(recp
, HFSPlusAttrRecord
*, bufsize
, M_TEMP
, M_WAITOK
);
554 btdata
.bufferAddress
= recp
;
555 btdata
.itemSize
= bufsize
;
556 btdata
.itemCount
= 1;
558 bzero(iterator
, sizeof(*iterator
));
559 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
564 /* Lookup the attribute record and inline data */
565 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
566 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
567 hfs_systemfile_unlock(hfsmp
, lockflags
);
569 if (result
== btNotFound
) {
575 /* Copy-out the attribute data to the user buffer */
576 *ap
->a_size
= recp
->attrData
.attrSize
;
577 result
= uiomove((caddr_t
) &recp
->attrData
.attrData
, recp
->attrData
.attrSize
, uio
);
583 /* Extent-Based EAs */
584 case kHFSPlusAttrForkData
: {
585 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
586 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
587 hfsmp
->vcbVN
, target_id
, ap
->a_name
, datasize
, sizeof(HFSPlusAttrForkData
));
591 *ap
->a_size
= recp
->forkData
.theFork
.logicalSize
;
595 if (*ap
->a_size
> (user_size_t
)uio_resid(uio
)) {
599 /* Process overflow extents if necessary. */
600 if (has_overflow_extents(&recp
->forkData
.theFork
)) {
601 HFSPlusExtentDescriptor
*extentbuf
;
602 HFSPlusExtentDescriptor
*extentptr
;
603 size_t extentbufsize
;
604 u_int32_t totalblocks
;
608 totalblocks
= recp
->forkData
.theFork
.totalBlocks
;
609 /* Ignore bogus block counts. */
610 if (totalblocks
> howmany(HFS_XATTR_MAXSIZE
, hfsmp
->blockSize
)) {
614 attrlen
= recp
->forkData
.theFork
.logicalSize
;
616 /* Get a buffer to hold the worst case amount of extents. */
617 extentbufsize
= totalblocks
* sizeof(HFSPlusExtentDescriptor
);
618 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
619 MALLOC(extentbuf
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
620 if (extentbuf
== NULL
) {
624 bzero(extentbuf
, extentbufsize
);
625 extentptr
= extentbuf
;
627 /* Grab the first 8 extents. */
628 bcopy(&recp
->forkData
.theFork
.extents
[0], extentptr
, sizeof(HFSPlusExtentRecord
));
629 extentptr
+= kHFSPlusExtentDensity
;
630 blkcnt
= count_extent_blocks(totalblocks
, recp
->forkData
.theFork
.extents
);
632 /* Now lookup the overflow extents. */
633 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
634 while (blkcnt
< totalblocks
) {
635 ((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
= blkcnt
;
636 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
638 (recp
->recordType
!= kHFSPlusAttrExtents
) ||
639 (datasize
< sizeof(HFSPlusAttrExtents
))) {
640 printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
641 ap
->a_name
, blkcnt
, totalblocks
);
643 break; /* break from while */
645 /* Grab the next 8 extents. */
646 bcopy(&recp
->overflowExtents
.extents
[0], extentptr
, sizeof(HFSPlusExtentRecord
));
647 extentptr
+= kHFSPlusExtentDensity
;
648 blkcnt
+= count_extent_blocks(totalblocks
, recp
->overflowExtents
.extents
);
651 /* Release Attr B-Tree lock */
652 hfs_systemfile_unlock(hfsmp
, lockflags
);
654 if (blkcnt
< totalblocks
) {
657 result
= read_attr_data(hfsmp
, uio
, attrlen
, extentbuf
);
659 FREE(extentbuf
, M_TEMP
);
661 } else { /* No overflow extents. */
662 result
= read_attr_data(hfsmp
, uio
, recp
->forkData
.theFork
.logicalSize
, recp
->forkData
.theFork
.extents
);
668 /* We only support Extent or inline EAs. Default to ENOATTR for anything else */
675 FREE(iterator
, M_TEMP
);
687 * Set the data of an extended attribute.
690 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
692 struct vnop_setxattr_args {
693 struct vnodeop_desc *a_desc;
698 vfs_context_t a_context;
702 struct vnode
*vp
= ap
->a_vp
;
703 struct cnode
*cp
= NULL
;
704 struct hfsmount
*hfsmp
;
705 uio_t uio
= ap
->a_uio
;
707 void * user_data_ptr
= NULL
;
709 time_t orig_ctime
=VTOC(vp
)->c_ctime
;
711 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
712 return (EINVAL
); /* invalid name */
715 if (VNODE_IS_RSRC(vp
)) {
720 if (hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1) ) { /* 1 == don't take the cnode lock */
721 result
= decmpfs_decompress_file(vp
, VTOCMP(vp
), -1, 1, 0);
725 #endif /* HFS_COMPRESSION */
727 check_for_tracked_file(vp
, orig_ctime
, NAMESPACE_HANDLER_METADATA_WRITE_OP
, NSPACE_REARM_NO_ARG
);
729 /* Set the Finder Info. */
730 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
731 u_int8_t finderinfo
[32];
732 struct FndrFileInfo
*fip
;
733 void * finderinfo_start
;
734 u_int8_t
*finfo
= NULL
;
736 u_int32_t dateadded
= 0;
737 u_int32_t write_gen_counter
= 0;
738 u_int32_t document_id
= 0;
740 attrsize
= sizeof(VTOC(vp
)->c_finderinfo
);
742 if ((user_size_t
)uio_resid(uio
) != attrsize
) {
745 /* Grab the new Finder Info data. */
746 if ((result
= uiomove((caddr_t
)&finderinfo
, attrsize
, uio
))) {
749 fip
= (struct FndrFileInfo
*)&finderinfo
;
751 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
756 /* Symlink's don't have an external type/creator. */
757 if (vnode_islnk(vp
)) {
758 /* Skip over type/creator fields. */
759 finderinfo_start
= &cp
->c_finderinfo
[8];
762 finderinfo_start
= &cp
->c_finderinfo
[0];
764 * Don't allow the external setting of
765 * file type to kHardLinkFileType.
767 if (fip
->fdType
== SWAP_BE32(kHardLinkFileType
)) {
773 /* Grab the current date added from the cnode */
774 dateadded
= hfs_get_dateadded (cp
);
775 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
776 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)((u_int8_t
*)cp
->c_finderinfo
+ 16);
778 * Grab generation counter directly from the cnode
779 * instead of calling hfs_get_gencount(), because
780 * for zero generation count values hfs_get_gencount()
781 * lies and bumps it up to one.
783 write_gen_counter
= extinfo
->write_gen_counter
;
784 document_id
= extinfo
->document_id
;
785 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
786 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)((u_int8_t
*)cp
->c_finderinfo
+ 16);
787 write_gen_counter
= extinfo
->write_gen_counter
;
788 document_id
= extinfo
->document_id
;
792 * Zero out the finder info's reserved fields like date added,
793 * generation counter, and document id to ignore user's attempts
796 hfs_zero_hidden_fields(cp
, finderinfo
);
798 if (bcmp(finderinfo_start
, emptyfinfo
, attrsize
)) {
799 /* attr exists and "create" was specified. */
800 if (ap
->a_options
& XATTR_CREATE
) {
805 /* attr doesn't exists and "replace" was specified. */
806 if (ap
->a_options
& XATTR_REPLACE
) {
813 * Now restore the date added and other reserved fields to the finderinfo to
814 * be written out. Advance to the 2nd half of the finderinfo to write them
815 * out into the buffer.
817 * Make sure to endian swap the date added back into big endian. When we used
818 * hfs_get_dateadded above to retrieve it, it swapped into local endianness
819 * for us. But now that we're writing it out, put it back into big endian.
821 finfo
= &finderinfo
[16];
822 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
823 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
824 extinfo
->date_added
= OSSwapHostToBigInt32(dateadded
);
825 extinfo
->write_gen_counter
= write_gen_counter
;
826 extinfo
->document_id
= document_id
;
827 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
828 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
829 extinfo
->date_added
= OSSwapHostToBigInt32(dateadded
);
830 extinfo
->write_gen_counter
= write_gen_counter
;
831 extinfo
->document_id
= document_id
;
834 /* Set the cnode's Finder Info. */
835 if (attrsize
== sizeof(cp
->c_finderinfo
)) {
836 bcopy(&finderinfo
[0], finderinfo_start
, attrsize
);
838 bcopy(&finderinfo
[8], finderinfo_start
, attrsize
);
841 /* Updating finderInfo updates change time and modified time */
842 cp
->c_touch_chgtime
= TRUE
;
843 cp
->c_flag
|= C_MODIFIED
;
846 * Mirror the invisible bit to the UF_HIDDEN flag.
848 * The fdFlags for files and frFlags for folders are both 8 bytes
849 * into the userInfo (the first 16 bytes of the Finder Info). They
850 * are both 16-bit fields.
852 fdFlags
= *((u_int16_t
*) &cp
->c_finderinfo
[8]);
853 if (fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
)) {
854 cp
->c_bsdflags
|= UF_HIDDEN
;
856 cp
->c_bsdflags
&= ~UF_HIDDEN
;
859 result
= hfs_update(vp
, FALSE
);
864 /* Write the Resource Fork. */
865 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
866 struct vnode
*rvp
= NULL
;
868 int openunlinked
= 0;
870 if (!vnode_isreg(vp
)) {
873 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
877 namelen
= cp
->c_desc
.cd_namelen
;
879 if (hfs_has_rsrc(cp
)) {
880 /* attr exists and "create" was specified. */
881 if (ap
->a_options
& XATTR_CREATE
) {
886 /* attr doesn't exists and "replace" was specified. */
887 if (ap
->a_options
& XATTR_REPLACE
) {
894 * Note that we could be called on to grab the rsrc fork vnode
895 * for a file that has become open-unlinked.
897 if ((cp
->c_flag
& C_DELETED
) && (namelen
== 0)) {
901 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
);
906 /* VNOP_WRITE marks cnode as needing a modtime update */
907 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
909 /* if open unlinked, force it inactive */
912 vref
= vnode_ref (rvp
);
918 /* cnode is not open-unlinked, so re-lock cnode to sync */
919 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
925 /* hfs fsync rsrc fork to force to disk and update modtime */
926 result
= hfs_fsync (rvp
, MNT_NOWAIT
, 0, vfs_context_proc (ap
->a_context
));
934 * Standard HFS only supports native FinderInfo and Resource Forks.
936 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
939 attrsize
= uio_resid(uio
);
941 /* Enforce an upper limit. */
942 if (attrsize
> HFS_XATTR_MAXSIZE
) {
948 * Attempt to copy the users attr data before taking any locks,
949 * only if it will be an inline attribute. For larger attributes,
950 * the data will be directly read from the uio.
953 hfsmp
->hfs_max_inline_attrsize
!= 0 &&
954 attrsize
< hfsmp
->hfs_max_inline_attrsize
) {
955 MALLOC(user_data_ptr
, void *, attrsize
, M_TEMP
, M_WAITOK
);
956 if (user_data_ptr
== NULL
) {
961 result
= uiomove((caddr_t
)user_data_ptr
, attrsize
, uio
);
967 result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
974 * If we're trying to set a non-finderinfo, non-resourcefork EA, then
975 * call the breakout function.
977 result
= hfs_setxattr_internal (cp
, user_data_ptr
, attrsize
, ap
, VTOHFS(vp
), 0);
984 FREE(user_data_ptr
, M_TEMP
);
987 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
990 // Has same limitations as hfs_setxattr_internal below
991 int hfs_xattr_write(vnode_t vp
, const char *name
, const void *data
, size_t size
)
993 struct vnop_setxattr_args args
= {
998 return hfs_setxattr_internal(VTOC(vp
), data
, size
, &args
, VTOHFS(vp
), 0);
1002 * hfs_setxattr_internal
1004 * Internal function to set non-rsrc, non-finderinfo EAs to either the attribute B-Tree or
1007 * See comments from hfs_getxattr_internal on why we need to pass 'hfsmp' and fileid here.
1008 * The gist is that we could end up writing to the root folder which may not have a cnode.
1011 * 1. cnode 'cp' is locked EXCLUSIVE before calling this function.
1012 * 2. data_ptr contains data to be written. If gathering data from userland, this must be
1013 * done before calling this function.
1014 * 3. If data originates entirely in-kernel, use a null UIO, and ensure the size is less than
1015 * hfsmp->hfs_max_inline_attrsize bytes long.
1017 int hfs_setxattr_internal (struct cnode
*cp
, const void *data_ptr
, size_t attrsize
,
1018 struct vnop_setxattr_args
*ap
, struct hfsmount
*hfsmp
,
1021 uio_t uio
= ap
->a_uio
;
1022 struct vnode
*vp
= ap
->a_vp
;
1023 int started_transaction
= 0;
1024 struct BTreeIterator
* iterator
= NULL
;
1025 struct filefork
*btfile
= NULL
;
1026 FSBufferDescriptor btdata
;
1027 HFSPlusAttrRecord attrdata
; /* 90 bytes */
1028 HFSPlusAttrRecord
*recp
= NULL
;
1029 HFSPlusExtentDescriptor
*extentptr
= NULL
;
1033 int allocatedblks
= 0;
1034 u_int32_t target_id
;
1038 target_id
= cp
->c_fileid
;
1041 if (target_id
!= 1) {
1043 * If we are manipulating something other than
1044 * the root folder (id 1), and do not have a cnode-in-hand,
1045 * then we must already hold the requisite b-tree locks from
1046 * earlier up the call stack. (See hfs_makenode)
1052 /* Start a transaction for our changes. */
1053 if (hfs_start_transaction(hfsmp
) != 0) {
1057 started_transaction
= 1;
1060 * Once we started the transaction, nobody can compete
1061 * with us, so make sure this file is still there.
1063 if ((cp
) && (cp
->c_flag
& C_NOEXISTS
)) {
1069 * If there isn't an attributes b-tree then create one.
1071 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1072 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
1073 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
1078 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
1079 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
1083 /* Take exclusive access to the attributes b-tree. */
1084 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1087 /* Build the b-tree key. */
1088 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1089 if (iterator
== NULL
) {
1093 bzero(iterator
, sizeof(*iterator
));
1094 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1099 /* Preflight for replace/create semantics. */
1100 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1101 btdata
.bufferAddress
= &attrdata
;
1102 btdata
.itemSize
= sizeof(attrdata
);
1103 btdata
.itemCount
= 1;
1104 exists
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
) == 0;
1106 /* Replace requires that the attribute already exists. */
1107 if ((ap
->a_options
& XATTR_REPLACE
) && !exists
) {
1111 /* Create requires that the attribute doesn't exist. */
1112 if ((ap
->a_options
& XATTR_CREATE
) && exists
) {
1117 /* If it won't fit inline then use extent-based attributes. */
1118 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
1119 size_t extentbufsize
;
1122 u_int32_t
*keystartblk
;
1127 * setxattrs originating from in-kernel are not supported if they are bigger
1128 * than the inline max size. Just return ENOATTR and force them to do it with a
1135 /* Get some blocks. */
1136 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
1137 extentbufsize
= blkcnt
* sizeof(HFSPlusExtentDescriptor
);
1138 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
1139 MALLOC(extentptr
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
1140 if (extentptr
== NULL
) {
1144 bzero(extentptr
, extentbufsize
);
1145 result
= alloc_attr_blks(hfsmp
, attrsize
, extentbufsize
, extentptr
, &allocatedblks
);
1148 goto exit
; /* no more space */
1150 /* Copy data into the blocks. */
1151 result
= write_attr_data(hfsmp
, uio
, attrsize
, extentptr
);
1154 const char *name
= vnode_getname(vp
);
1155 printf("hfs_setxattr: write_attr_data vol=%s err (%d) %s:%s\n",
1156 hfsmp
->vcbVN
, result
, name
? name
: "", ap
->a_name
);
1158 vnode_putname(name
);
1163 /* Now remove any previous attribute. */
1165 result
= remove_attribute_records(hfsmp
, iterator
);
1168 const char *name
= vnode_getname(vp
);
1169 printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n",
1170 hfsmp
->vcbVN
, result
, name
? name
: "", ap
->a_name
);
1172 vnode_putname(name
);
1177 /* Create attribute fork data record. */
1178 MALLOC(recp
, HFSPlusAttrRecord
*, sizeof(HFSPlusAttrRecord
), M_TEMP
, M_WAITOK
);
1183 btdata
.bufferAddress
= recp
;
1184 btdata
.itemCount
= 1;
1185 btdata
.itemSize
= sizeof(HFSPlusAttrForkData
);
1187 recp
->recordType
= kHFSPlusAttrForkData
;
1188 recp
->forkData
.reserved
= 0;
1189 recp
->forkData
.theFork
.logicalSize
= attrsize
;
1190 recp
->forkData
.theFork
.clumpSize
= 0;
1191 recp
->forkData
.theFork
.totalBlocks
= blkcnt
;
1192 bcopy(extentptr
, recp
->forkData
.theFork
.extents
, sizeof(HFSPlusExtentRecord
));
1194 (void) hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1196 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1198 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
1199 hfsmp
->vcbVN
, target_id
, ap
->a_name
, result
);
1202 extentblks
= count_extent_blocks(blkcnt
, recp
->forkData
.theFork
.extents
);
1203 blkcnt
-= extentblks
;
1204 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1207 /* Create overflow extents as needed. */
1208 while (blkcnt
> 0) {
1209 /* Initialize the key and record. */
1210 *keystartblk
+= (u_int32_t
)extentblks
;
1211 btdata
.itemSize
= sizeof(HFSPlusAttrExtents
);
1212 recp
->recordType
= kHFSPlusAttrExtents
;
1213 recp
->overflowExtents
.reserved
= 0;
1215 /* Copy the next set of extents. */
1216 i
+= kHFSPlusExtentDensity
;
1217 bcopy(&extentptr
[i
], recp
->overflowExtents
.extents
, sizeof(HFSPlusExtentRecord
));
1219 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1221 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
1222 hfsmp
->vcbVN
, target_id
, ap
->a_name
, result
);
1225 extentblks
= count_extent_blocks(blkcnt
, recp
->overflowExtents
.extents
);
1226 blkcnt
-= extentblks
;
1228 } else { /* Inline data */
1230 result
= remove_attribute_records(hfsmp
, iterator
);
1236 /* Calculate size of record rounded up to multiple of 2 bytes. */
1237 btdata
.itemSize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
1238 MALLOC(recp
, HFSPlusAttrRecord
*, btdata
.itemSize
, M_TEMP
, M_WAITOK
);
1243 recp
->recordType
= kHFSPlusAttrInlineData
;
1244 recp
->attrData
.reserved
[0] = 0;
1245 recp
->attrData
.reserved
[1] = 0;
1246 recp
->attrData
.attrSize
= attrsize
;
1248 /* Copy in the attribute data (if any). */
1251 bcopy(data_ptr
, &recp
->attrData
.attrData
, attrsize
);
1254 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
1255 * then deny the copy operation.
1261 result
= uiomove((caddr_t
)&recp
->attrData
.attrData
, attrsize
, uio
);
1269 (void) hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1271 btdata
.bufferAddress
= recp
;
1272 btdata
.itemCount
= 1;
1273 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1277 if (btfile
&& started_transaction
) {
1278 (void) BTFlushPath(btfile
);
1281 hfs_systemfile_unlock(hfsmp
, lockflags
);
1286 /* Setting an attribute only updates change time and not
1287 * modified time of the file.
1289 cp
->c_touch_chgtime
= TRUE
;
1290 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
1291 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1292 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
1294 (void) hfs_update(vp
, 0);
1297 if (started_transaction
) {
1298 if (result
&& allocatedblks
) {
1299 free_attr_blks(hfsmp
, allocatedblks
, extentptr
);
1301 hfs_end_transaction(hfsmp
);
1308 FREE(extentptr
, M_TEMP
);
1311 FREE(iterator
, M_TEMP
);
1321 * Remove an extended attribute.
1324 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
1326 struct vnop_removexattr_args {
1327 struct vnodeop_desc *a_desc;
1331 vfs_context_t a_context;
1335 struct vnode
*vp
= ap
->a_vp
;
1336 struct cnode
*cp
= VTOC(vp
);
1337 struct hfsmount
*hfsmp
;
1338 struct BTreeIterator
* iterator
= NULL
;
1341 time_t orig_ctime
=VTOC(vp
)->c_ctime
;
1343 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
1344 return (EINVAL
); /* invalid name */
1347 if (VNODE_IS_RSRC(vp
)) {
1352 if (hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1) && !(ap
->a_options
& XATTR_SHOWCOMPRESSION
)) {
1355 #endif /* HFS_COMPRESSION */
1357 check_for_tracked_file(vp
, orig_ctime
, NAMESPACE_HANDLER_METADATA_DELETE_OP
, NSPACE_REARM_NO_ARG
);
1359 /* If Resource Fork is non-empty then truncate it. */
1360 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1361 struct vnode
*rvp
= NULL
;
1363 if ( !vnode_isreg(vp
) ) {
1366 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1369 if (!hfs_has_rsrc(cp
)) {
1373 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
);
1379 hfs_lock_truncate(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
1381 // Tell UBC now before we take the cnode lock and start the transaction
1382 hfs_ubc_setsize(rvp
, 0, false);
1384 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1385 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1390 /* Start a transaction for encapsulating changes in
1391 * hfs_truncate() and hfs_update()
1393 if ((result
= hfs_start_transaction(hfsmp
))) {
1394 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1400 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, ap
->a_context
);
1402 cp
->c_touch_chgtime
= TRUE
;
1403 cp
->c_flag
|= C_MODIFIED
;
1404 result
= hfs_update(vp
, FALSE
);
1407 hfs_end_transaction(hfsmp
);
1408 hfs_unlock_truncate(VTOC(rvp
), HFS_LOCK_DEFAULT
);
1409 hfs_unlock(VTOC(rvp
));
1414 /* Clear out the Finder Info. */
1415 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1416 void * finderinfo_start
;
1417 int finderinfo_size
;
1418 u_int8_t finderinfo
[32];
1419 u_int32_t date_added
, write_gen_counter
, document_id
;
1420 u_int8_t
*finfo
= NULL
;
1422 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1426 /* Use the local copy to store our temporary changes. */
1427 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
1430 /* Zero out the date added field in the local copy */
1431 hfs_zero_hidden_fields (cp
, finderinfo
);
1433 /* Don't expose a symlink's private type/creator. */
1434 if (vnode_islnk(vp
)) {
1435 struct FndrFileInfo
*fip
;
1437 fip
= (struct FndrFileInfo
*)&finderinfo
;
1442 /* Do the byte compare against the local copy */
1443 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
1449 * If there was other content, zero out everything except
1450 * type/creator and date added. First, save the date added.
1452 finfo
= cp
->c_finderinfo
;
1454 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
1455 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
1456 date_added
= extinfo
->date_added
;
1457 write_gen_counter
= extinfo
->write_gen_counter
;
1458 document_id
= extinfo
->document_id
;
1459 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
1460 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
1461 date_added
= extinfo
->date_added
;
1462 write_gen_counter
= extinfo
->write_gen_counter
;
1463 document_id
= extinfo
->document_id
;
1466 if (vnode_islnk(vp
)) {
1467 /* Ignore type/creator */
1468 finderinfo_start
= &cp
->c_finderinfo
[8];
1469 finderinfo_size
= sizeof(cp
->c_finderinfo
) - 8;
1471 finderinfo_start
= &cp
->c_finderinfo
[0];
1472 finderinfo_size
= sizeof(cp
->c_finderinfo
);
1474 bzero(finderinfo_start
, finderinfo_size
);
1477 /* Now restore the date added */
1478 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
1479 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
1480 extinfo
->date_added
= date_added
;
1481 extinfo
->write_gen_counter
= write_gen_counter
;
1482 extinfo
->document_id
= document_id
;
1483 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
1484 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
1485 extinfo
->date_added
= date_added
;
1486 extinfo
->write_gen_counter
= write_gen_counter
;
1487 extinfo
->document_id
= document_id
;
1490 /* Updating finderInfo updates change time and modified time */
1491 cp
->c_touch_chgtime
= TRUE
;
1492 cp
->c_flag
|= C_MODIFIED
;
1493 hfs_update(vp
, FALSE
);
1500 * Standard HFS only supports native FinderInfo and Resource Forks.
1502 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1505 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1509 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1510 if (iterator
== NULL
) {
1513 bzero(iterator
, sizeof(*iterator
));
1515 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1519 result
= hfs_buildattrkey(cp
->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1524 if (hfs_start_transaction(hfsmp
) != 0) {
1528 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1530 result
= remove_attribute_records(hfsmp
, iterator
);
1532 hfs_systemfile_unlock(hfsmp
, lockflags
);
1535 cp
->c_touch_chgtime
= TRUE
;
1537 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1539 /* If no more attributes exist, clear attribute bit */
1540 result
= file_attribute_exist(hfsmp
, cp
->c_fileid
);
1542 cp
->c_attr
.ca_recflags
&= ~kHFSHasAttributesMask
;
1544 if (result
== EEXIST
) {
1548 hfs_systemfile_unlock(hfsmp
, lockflags
);
1550 /* If ACL was removed, clear security bit */
1551 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1552 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
1554 (void) hfs_update(vp
, 0);
1557 hfs_end_transaction(hfsmp
);
1561 FREE(iterator
, M_TEMP
);
1562 return MacToVFSError(result
);
1565 /* Check if any attribute record exist for given fileID. This function
1566 * is called by hfs_vnop_removexattr to determine if it should clear the
1567 * attribute bit in the catalog record or not.
1569 * Note - you must acquire a shared lock on the attribute btree before
1570 * calling this function.
1573 * EEXIST - If attribute record was found
1574 * 0 - Attribute was not found
1575 * (other) - Other error (such as EIO)
1578 file_attribute_exist(struct hfsmount
*hfsmp
, uint32_t fileID
)
1580 HFSPlusAttrKey
*key
;
1581 struct BTreeIterator
* iterator
= NULL
;
1582 struct filefork
*btfile
;
1585 // if there's no attribute b-tree we sure as heck
1586 // can't have any attributes!
1587 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1591 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1592 if (iterator
== NULL
) {
1596 bzero(iterator
, sizeof(*iterator
));
1597 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1599 result
= hfs_buildattrkey(fileID
, NULL
, key
);
1604 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1605 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1606 if (result
&& (result
!= btNotFound
)) {
1610 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1611 /* If no next record was found or fileID for next record did not match,
1612 * no more attributes exist for this fileID
1614 if ((result
&& (result
== btNotFound
)) || (key
->fileID
!= fileID
)) {
1622 FREE(iterator
, M_TEMP
);
1629 * Remove all the records for a given attribute.
1631 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1632 * - A transaction must have been started.
1633 * - The Attribute b-tree file must be locked exclusive.
1634 * - The Allocation Bitmap file must be locked exclusive.
1635 * - The iterator key must be initialized.
1638 remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
)
1640 struct filefork
*btfile
;
1641 FSBufferDescriptor btdata
;
1642 HFSPlusAttrRecord attrdata
; /* 90 bytes */
1646 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1648 btdata
.bufferAddress
= &attrdata
;
1649 btdata
.itemSize
= sizeof(attrdata
);
1650 btdata
.itemCount
= 1;
1651 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1653 goto exit
; /* no records. */
1656 * Free the blocks from extent based attributes.
1658 * Note that the block references (btree records) are removed
1659 * before releasing the blocks in the allocation bitmap.
1661 if (attrdata
.recordType
== kHFSPlusAttrForkData
) {
1664 u_int32_t
*keystartblk
;
1666 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
1667 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize
, sizeof(HFSPlusAttrForkData
));
1669 totalblks
= attrdata
.forkData
.theFork
.totalBlocks
;
1671 /* Process the first 8 extents. */
1672 extentblks
= count_extent_blocks(totalblks
, attrdata
.forkData
.theFork
.extents
);
1673 if (extentblks
> totalblks
)
1674 panic("hfs: remove_attribute_records: corruption...");
1675 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1676 free_attr_blks(hfsmp
, extentblks
, attrdata
.forkData
.theFork
.extents
);
1678 totalblks
-= extentblks
;
1679 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1681 /* Process any overflow extents. */
1683 *keystartblk
+= (u_int32_t
)extentblks
;
1685 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1687 (attrdata
.recordType
!= kHFSPlusAttrExtents
) ||
1688 (datasize
< sizeof(HFSPlusAttrExtents
))) {
1689 printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n",
1690 hfsmp
->vcbVN
, MacToVFSError(result
), attrdata
.recordType
!= kHFSPlusAttrExtents
, totalblks
);
1692 break; /* break from while */
1694 /* Process the next 8 extents. */
1695 extentblks
= count_extent_blocks(totalblks
, attrdata
.overflowExtents
.extents
);
1696 if (extentblks
> totalblks
)
1697 panic("hfs: remove_attribute_records: corruption...");
1698 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1699 free_attr_blks(hfsmp
, extentblks
, attrdata
.overflowExtents
.extents
);
1701 totalblks
-= extentblks
;
1704 result
= BTDeleteRecord(btfile
, iterator
);
1706 (void) BTFlushPath(btfile
);
1708 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
1713 * Retrieve the list of extended attribute names.
1716 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
1718 struct vnop_listxattr_args {
1719 struct vnodeop_desc *a_desc;
1724 vfs_context_t a_context;
1727 struct vnode
*vp
= ap
->a_vp
;
1728 struct cnode
*cp
= VTOC(vp
);
1729 struct hfsmount
*hfsmp
;
1730 uio_t uio
= ap
->a_uio
;
1731 struct BTreeIterator
* iterator
= NULL
;
1732 struct filefork
*btfile
;
1733 struct listattr_callback_state state
;
1734 user_addr_t user_start
= 0;
1735 user_size_t user_len
= 0;
1738 u_int8_t finderinfo
[32];
1741 if (VNODE_IS_RSRC(vp
)) {
1746 int compressed
= hfs_file_is_compressed(cp
, 1); /* 1 == don't take the cnode lock */
1747 #endif /* HFS_COMPRESSION */
1753 * Take the truncate lock; this serializes us against the ioctl
1754 * to truncate data & reset the decmpfs state
1755 * in the compressed file handler.
1757 hfs_lock_truncate(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
1759 /* Now the regular cnode lock (shared) */
1760 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
1761 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1766 * Make a copy of the cnode's finderinfo to a local so we can
1767 * zero out the date added field. Also zero out the private type/creator
1770 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
1771 hfs_zero_hidden_fields (cp
, finderinfo
);
1773 /* Don't expose a symlink's private type/creator. */
1774 if (vnode_islnk(vp
)) {
1775 struct FndrFileInfo
*fip
;
1777 fip
= (struct FndrFileInfo
*)&finderinfo
;
1783 /* If Finder Info is non-empty then export it's name. */
1784 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) != 0) {
1786 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
1787 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1791 result
= uiomove(XATTR_FINDERINFO_NAME
,
1792 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1797 /* If Resource Fork is non-empty then export it's name. */
1798 if (S_ISREG(cp
->c_mode
) && hfs_has_rsrc(cp
)) {
1800 if ((ap
->a_options
& XATTR_SHOWCOMPRESSION
) ||
1802 !decmpfs_hides_rsrc(ap
->a_context
, VTOCMP(vp
))
1804 #endif /* HFS_COMPRESSION */
1807 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1808 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1812 result
= uiomove(XATTR_RESOURCEFORK_NAME
,
1813 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
1820 * Standard HFS only supports native FinderInfo and Resource Forks.
1821 * Return at this point.
1823 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1827 /* Bail if we don't have any extended attributes. */
1828 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
1829 (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
1833 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1835 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1836 if (iterator
== NULL
) {
1840 bzero(iterator
, sizeof(*iterator
));
1841 result
= hfs_buildattrkey(cp
->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
1846 * Lock the user's buffer here so that we won't fault on
1847 * it in uiomove while holding the attributes b-tree lock.
1849 if (uio
&& uio_isuserspace(uio
)) {
1850 user_start
= uio_curriovbase(uio
);
1851 user_len
= uio_curriovlen(uio
);
1853 if ((result
= vslock(user_start
, user_len
)) != 0) {
1858 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1860 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1861 if (result
&& result
!= btNotFound
) {
1862 hfs_systemfile_unlock(hfsmp
, lockflags
);
1866 state
.fileID
= cp
->c_fileid
;
1871 state
.showcompressed
= !compressed
|| ap
->a_options
& XATTR_SHOWCOMPRESSION
;
1872 state
.ctx
= ap
->a_context
;
1874 #endif /* HFS_COMPRESSION */
1877 * Process entries starting just after iterator->key.
1879 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
1880 (IterateCallBackProcPtr
)listattr_callback
, &state
);
1881 hfs_systemfile_unlock(hfsmp
, lockflags
);
1883 *ap
->a_size
+= state
.size
;
1886 if (state
.result
|| result
== btNotFound
)
1887 result
= state
.result
;
1891 vsunlock(user_start
, user_len
, TRUE
);
1894 FREE(iterator
, M_TEMP
);
1897 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1899 return MacToVFSError(result
);
1904 * Callback - called for each attribute record
1907 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
1909 char attrname
[XATTR_MAXNAMELEN
+ 1];
1913 if (state
->fileID
!= key
->fileID
) {
1915 return (0); /* stop */
1918 * Skip over non-primary keys
1920 if (key
->startBlock
!= 0) {
1921 return (1); /* continue */
1924 /* Convert the attribute name into UTF-8. */
1925 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
1926 (u_int8_t
*)attrname
, (size_t *)&bytecount
, sizeof(attrname
), '/', 0);
1928 state
->result
= result
;
1929 return (0); /* stop */
1931 bytecount
++; /* account for null termination char */
1933 if (xattr_protected(attrname
))
1934 return (1); /* continue */
1937 if (!state
->showcompressed
&& decmpfs_hides_xattr(state
->ctx
, VTOCMP(state
->vp
), attrname
) )
1938 return 1; /* continue */
1939 #endif /* HFS_COMPRESSION */
1941 if (state
->uio
== NULL
) {
1942 state
->size
+= bytecount
;
1944 if (bytecount
> uio_resid(state
->uio
)) {
1945 state
->result
= ERANGE
;
1946 return (0); /* stop */
1948 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
1950 state
->result
= result
;
1951 return (0); /* stop */
1954 return (1); /* continue */
1958 * Remove all the attributes from a cnode.
1960 * This function creates/ends its own transaction so that each
1961 * attribute is deleted in its own transaction (to avoid having
1962 * a transaction grow too large).
1964 * This function takes the necessary locks on the attribute
1965 * b-tree file and the allocation (bitmap) file.
1968 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
)
1970 BTreeIterator
*iterator
= NULL
;
1971 HFSPlusAttrKey
*key
;
1972 struct filefork
*btfile
;
1973 int result
, lockflags
;
1975 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1978 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1980 MALLOC(iterator
, BTreeIterator
*, sizeof(BTreeIterator
), M_TEMP
, M_WAITOK
);
1981 if (iterator
== NULL
) {
1984 bzero(iterator
, sizeof(BTreeIterator
));
1985 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1987 /* Loop until there are no more attributes for this file id */
1989 if (hfs_start_transaction(hfsmp
) != 0) {
1994 /* Lock the attribute b-tree and the allocation (bitmap) files */
1995 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1998 * Go to first possible attribute key/record pair
2000 (void) hfs_buildattrkey(fileid
, NULL
, key
);
2001 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
2002 if (result
|| key
->fileID
!= fileid
) {
2003 hfs_systemfile_unlock(hfsmp
, lockflags
);
2004 hfs_end_transaction(hfsmp
);
2007 result
= remove_attribute_records(hfsmp
, iterator
);
2009 #if HFS_XATTR_VERBOSE
2011 printf("hfs_removeallattr: unexpected err %d\n", result
);
2014 hfs_systemfile_unlock(hfsmp
, lockflags
);
2015 hfs_end_transaction(hfsmp
);
2020 FREE(iterator
, M_TEMP
);
2021 return (result
== btNotFound
? 0: MacToVFSError(result
));
2026 hfs_xattr_init(struct hfsmount
* hfsmp
)
2029 * If there isn't an attributes b-tree then create one.
2031 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
) &&
2032 (hfsmp
->hfs_attribute_vp
== NULL
) &&
2033 !(hfsmp
->hfs_flags
& HFS_READ_ONLY
)) {
2034 (void) hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
2035 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
2037 if (hfsmp
->hfs_attribute_vp
)
2038 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
2042 * Enable/Disable volume attributes stored as EA for root file system.
2043 * Supported attributes are -
2044 * 1. Extent-based Extended Attributes
2047 hfs_set_volxattr(struct hfsmount
*hfsmp
, unsigned int xattrtype
, int state
)
2049 struct BTreeIterator
* iterator
= NULL
;
2050 struct filefork
*btfile
;
2054 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
2057 if (xattrtype
!= HFS_SET_XATTREXTENTS_STATE
) {
2062 * If there isn't an attributes b-tree then create one.
2064 if (hfsmp
->hfs_attribute_vp
== NULL
) {
2065 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
2066 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
2072 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2073 if (iterator
== NULL
) {
2076 bzero(iterator
, sizeof(*iterator
));
2079 * Build a b-tree key.
2080 * We use the root's parent id (1) to hold this volume attribute.
2082 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_XATTREXTENTS_NAME
,
2083 (HFSPlusAttrKey
*)&iterator
->key
);
2085 /* Start a transaction for our changes. */
2086 if (hfs_start_transaction(hfsmp
) != 0) {
2090 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
2092 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
2095 /* Remove the attribute. */
2096 result
= BTDeleteRecord(btfile
, iterator
);
2097 if (result
== btNotFound
)
2100 FSBufferDescriptor btdata
;
2101 HFSPlusAttrData attrdata
;
2104 datasize
= sizeof(attrdata
);
2105 btdata
.bufferAddress
= &attrdata
;
2106 btdata
.itemSize
= datasize
;
2107 btdata
.itemCount
= 1;
2108 attrdata
.recordType
= kHFSPlusAttrInlineData
;
2109 attrdata
.reserved
[0] = 0;
2110 attrdata
.reserved
[1] = 0;
2111 attrdata
.attrSize
= 2;
2112 attrdata
.attrData
[0] = 0;
2113 attrdata
.attrData
[1] = 0;
2115 /* Insert the attribute. */
2116 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
2117 if (result
== btExists
)
2120 (void) BTFlushPath(btfile
);
2122 hfs_systemfile_unlock(hfsmp
, lockflags
);
2124 /* Finish the transaction of our changes. */
2125 hfs_end_transaction(hfsmp
);
2127 /* Update the state in the mount point */
2128 hfs_lock_mount (hfsmp
);
2130 hfsmp
->hfs_flags
&= ~HFS_XATTR_EXTENTS
;
2132 hfsmp
->hfs_flags
|= HFS_XATTR_EXTENTS
;
2134 hfs_unlock_mount (hfsmp
);
2138 FREE(iterator
, M_TEMP
);
2140 return MacToVFSError(result
);
2145 * hfs_attrkeycompare - compare two attribute b-tree keys.
2147 * The name portion of the key is compared using a 16-bit binary comparison.
2148 * This is called from the b-tree code.
2152 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
2154 u_int32_t searchFileID
, trialFileID
;
2157 searchFileID
= searchKey
->fileID
;
2158 trialFileID
= trialKey
->fileID
;
2161 if (searchFileID
> trialFileID
) {
2163 } else if (searchFileID
< trialFileID
) {
2166 u_int16_t
* str1
= &searchKey
->attrName
[0];
2167 u_int16_t
* str2
= &trialKey
->attrName
[0];
2168 int length1
= searchKey
->attrNameLen
;
2169 int length2
= trialKey
->attrNameLen
;
2173 if (length1
< length2
) {
2176 } else if (length1
> length2
) {
2199 * Names are equal; compare startBlock
2201 if (searchKey
->startBlock
== trialKey
->startBlock
) {
2204 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
2213 * hfs_buildattrkey - build an Attribute b-tree key
2217 hfs_buildattrkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
2220 size_t unicodeBytes
= 0;
2222 if (attrname
!= NULL
) {
2224 * Convert filename from UTF-8 into Unicode
2226 result
= utf8_decodestr((const u_int8_t
*)attrname
, strlen(attrname
), key
->attrName
,
2227 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
2229 if (result
!= ENAMETOOLONG
)
2230 result
= EINVAL
; /* name has invalid characters */
2233 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
2234 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
2236 key
->attrNameLen
= 0;
2237 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
2240 key
->fileID
= fileID
;
2241 key
->startBlock
= 0;
2247 * getnodecount - calculate starting node count for attributes b-tree.
2250 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
2252 u_int64_t freebytes
;
2253 u_int64_t calcbytes
;
2256 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
2257 * 10.5: Attempt to be as big as the catalog clump size.
2259 * Use no more than 10 % of the remaining free space.
2261 freebytes
= (u_int64_t
)hfs_freeblks(hfsmp
, 0) * (u_int64_t
)hfsmp
->blockSize
;
2263 calcbytes
= MIN(hfsmp
->hfs_catalog_cp
->c_datafork
->ff_size
/ 5, 20 * 1024 * 1024);
2265 calcbytes
= MAX(calcbytes
, hfsmp
->hfs_catalog_cp
->c_datafork
->ff_clumpsize
);
2267 calcbytes
= MIN(calcbytes
, freebytes
/ 10);
2269 return (MAX(2, (int)(calcbytes
/ nodesize
)));
2274 * getmaxinlineattrsize - calculate maximum inline attribute size.
2276 * This yields 3,802 bytes for an 8K node size.
2279 getmaxinlineattrsize(struct vnode
* attrvp
)
2281 struct BTreeInfoRec btinfo
;
2282 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
2285 if (attrvp
!= NULL
) {
2286 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2287 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
2288 nodesize
= btinfo
.nodeSize
;
2289 hfs_unlock(VTOC(attrvp
));
2292 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
2293 maxsize
-= 3 * sizeof(u_int16_t
); /* minus 3 index slots */
2294 maxsize
/= 2; /* 2 key/rec pairs minumum */
2295 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
2296 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
2297 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */
2303 * Initialize vnode for attribute data I/O.
2307 * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp
2308 * - an iocount is taken on the attrdata vnode which exists
2309 * for the entire duration of the mount. It is only dropped
2311 * - the attrdata cnode is not locked
2314 * - returns non-zero value
2315 * - the caller does not have to worry about any locks or references
2317 int init_attrdata_vnode(struct hfsmount
*hfsmp
)
2321 struct cat_desc cat_desc
;
2322 struct cat_attr cat_attr
;
2323 struct cat_fork cat_fork
;
2324 int newvnode_flags
= 0;
2326 bzero(&cat_desc
, sizeof(cat_desc
));
2327 cat_desc
.cd_parentcnid
= kHFSRootParentID
;
2328 cat_desc
.cd_nameptr
= (const u_int8_t
*)hfs_attrdatafilename
;
2329 cat_desc
.cd_namelen
= strlen(hfs_attrdatafilename
);
2330 cat_desc
.cd_cnid
= kHFSAttributeDataFileID
;
2331 /* Tag vnode as system file, note that we can still use cluster I/O */
2332 cat_desc
.cd_flags
|= CD_ISMETA
;
2334 bzero(&cat_attr
, sizeof(cat_attr
));
2335 cat_attr
.ca_linkcount
= 1;
2336 cat_attr
.ca_mode
= S_IFREG
;
2337 cat_attr
.ca_fileid
= cat_desc
.cd_cnid
;
2338 cat_attr
.ca_blocks
= hfsmp
->totalBlocks
;
2341 * The attribute data file is a virtual file that spans the
2342 * entire file system space.
2344 * Each extent-based attribute occupies a unique portion of
2345 * in this virtual file. The cluster I/O is done using actual
2346 * allocation block offsets so no additional mapping is needed
2347 * for the VNOP_BLOCKMAP call.
2349 * This approach allows the attribute data to be cached without
2350 * incurring the high cost of using a separate vnode per attribute.
2352 * Since we need to acquire the attribute b-tree file lock anyways,
2353 * the virtual file doesn't introduce any additional serialization.
2355 bzero(&cat_fork
, sizeof(cat_fork
));
2356 cat_fork
.cf_size
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
2357 cat_fork
.cf_blocks
= hfsmp
->totalBlocks
;
2358 cat_fork
.cf_extents
[0].startBlock
= 0;
2359 cat_fork
.cf_extents
[0].blockCount
= cat_fork
.cf_blocks
;
2361 result
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cat_desc
, 0, &cat_attr
,
2362 &cat_fork
, &vp
, &newvnode_flags
);
2364 hfsmp
->hfs_attrdata_vp
= vp
;
2365 hfs_unlock(VTOC(vp
));
2371 * Read an extent based attribute.
2374 read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2376 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2384 hfs_lock_truncate(VTOC(evp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2386 bufsize
= (int)uio_resid(uio
);
2387 attrsize
= (int)datasize
;
2388 blksize
= (int)hfsmp
->blockSize
;
2391 * Read the attribute data one extent at a time.
2392 * For the typical case there is only one extent.
2394 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2395 iosize
= extents
[i
].blockCount
* blksize
;
2396 iosize
= MIN(iosize
, attrsize
);
2397 iosize
= MIN(iosize
, bufsize
);
2398 uio_setresid(uio
, iosize
);
2399 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2401 result
= cluster_read(evp
, uio
, VTOF(evp
)->ff_size
, IO_SYNC
| IO_UNIT
);
2403 #if HFS_XATTR_VERBOSE
2404 printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n",
2405 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2412 uio_setresid(uio
, bufsize
);
2413 uio_setoffset(uio
, datasize
);
2415 hfs_unlock_truncate(VTOC(evp
), HFS_LOCK_DEFAULT
);
2420 * Write an extent based attribute.
2423 write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2425 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2434 hfs_lock_truncate(VTOC(evp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2436 bufsize
= uio_resid(uio
);
2437 attrsize
= (int) datasize
;
2438 blksize
= (int) hfsmp
->blockSize
;
2439 filesize
= VTOF(evp
)->ff_size
;
2442 * Write the attribute data one extent at a time.
2444 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2445 iosize
= extents
[i
].blockCount
* blksize
;
2446 iosize
= MIN(iosize
, attrsize
);
2447 iosize
= MIN(iosize
, bufsize
);
2448 uio_setresid(uio
, iosize
);
2449 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2451 result
= cluster_write(evp
, uio
, filesize
, filesize
, filesize
,
2452 (off_t
) 0, IO_SYNC
| IO_UNIT
);
2453 #if HFS_XATTR_VERBOSE
2454 printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n",
2455 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2462 uio_setresid(uio
, bufsize
);
2463 uio_setoffset(uio
, datasize
);
2465 hfs_unlock_truncate(VTOC(evp
), HFS_LOCK_DEFAULT
);
2470 * Allocate blocks for an extent based attribute.
2473 alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
)
2482 startblk
= hfsmp
->hfs_metazone_end
;
2483 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
2484 if (blkcnt
> (int)hfs_freeblks(hfsmp
, 0)) {
2488 maxextents
= extentbufsize
/ sizeof(HFSPlusExtentDescriptor
);
2490 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2492 for (i
= 0; (blkcnt
> 0) && (i
< maxextents
); i
++) {
2493 /* Try allocating and see if we find something decent */
2494 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, 0,
2495 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
2497 * If we couldn't find anything, then re-try the allocation but allow
2500 if (result
== dskFulErr
) {
2501 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, HFS_ALLOC_FLUSHTXN
,
2502 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
2506 #if HFS_XATTR_VERBOSE
2507 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2508 blkcnt
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2511 extents
[i
].startBlock
= 0;
2512 extents
[i
].blockCount
= 0;
2515 blkcnt
-= extents
[i
].blockCount
;
2516 startblk
= extents
[i
].startBlock
+ extents
[i
].blockCount
;
2519 * If it didn't fit in the extents buffer then bail.
2524 #if HFS_XATTR_VERBOSE
2525 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt
);
2527 for (; i
>= 0; i
--) {
2528 if ((blkcnt
= extents
[i
].blockCount
) != 0) {
2529 (void) BlockDeallocate(hfsmp
, extents
[i
].startBlock
, blkcnt
, 0);
2530 extents
[i
].startBlock
= 0;
2531 extents
[i
].blockCount
= 0;
2536 hfs_systemfile_unlock(hfsmp
, lockflags
);
2537 return MacToVFSError(result
);
2541 * Release blocks from an extent based attribute.
2544 free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
)
2546 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2547 int remblks
= blkcnt
;
2551 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2553 for (i
= 0; (remblks
> 0) && (extents
[i
].blockCount
!= 0); i
++) {
2554 if (extents
[i
].blockCount
> (u_int32_t
)blkcnt
) {
2555 #if HFS_XATTR_VERBOSE
2556 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
2557 extents
[i
].startBlock
, extents
[i
].blockCount
);
2559 extents
[i
].blockCount
= 0;
2562 if (extents
[i
].startBlock
== 0) {
2565 (void)BlockDeallocate(hfsmp
, extents
[i
].startBlock
, extents
[i
].blockCount
, 0);
2566 remblks
-= extents
[i
].blockCount
;
2567 extents
[i
].startBlock
= 0;
2568 extents
[i
].blockCount
= 0;
2570 #if HFS_XATTR_VERBOSE
2571 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2572 extents
[i
].startBlock
, extents
[i
].blockCount
);
2574 /* Discard any resident pages for this block range. */
2578 start
= (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)hfsmp
->blockSize
;
2579 end
= start
+ (u_int64_t
)extents
[i
].blockCount
* (u_int64_t
)hfsmp
->blockSize
;
2580 (void) ubc_msync(hfsmp
->hfs_attrdata_vp
, start
, end
, &start
, UBC_INVALIDATE
);
2584 hfs_systemfile_unlock(hfsmp
, lockflags
);
2588 has_overflow_extents(HFSPlusForkData
*forkdata
)
2592 if (forkdata
->extents
[7].blockCount
== 0)
2595 blocks
= forkdata
->extents
[0].blockCount
+
2596 forkdata
->extents
[1].blockCount
+
2597 forkdata
->extents
[2].blockCount
+
2598 forkdata
->extents
[3].blockCount
+
2599 forkdata
->extents
[4].blockCount
+
2600 forkdata
->extents
[5].blockCount
+
2601 forkdata
->extents
[6].blockCount
+
2602 forkdata
->extents
[7].blockCount
;
2604 return (forkdata
->totalBlocks
> blocks
);
2608 count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
)
2613 for (i
= 0, blocks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
2614 /* Ignore obvious bogus extents. */
2615 if (extents
[i
].blockCount
> (u_int32_t
)maxblks
)
2617 if (extents
[i
].startBlock
== 0 || extents
[i
].blockCount
== 0)
2619 blocks
+= extents
[i
].blockCount
;