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/uio_internal.h>
44 #include "hfs_cnode.h"
45 #include "hfs_mount.h"
46 #include "hfs_format.h"
47 #include "hfs_endian.h"
48 #include "hfs_btreeio.h"
49 #include "hfs_fsctl.h"
50 #include "hfs_cprotect.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
, 0);
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
;
1037 target_id
= cp
->c_fileid
;
1042 /* Start a transaction for our changes. */
1043 if (hfs_start_transaction(hfsmp
) != 0) {
1047 started_transaction
= 1;
1050 * Once we started the transaction, nobody can compete
1051 * with us, so make sure this file is still there.
1053 if ((cp
) && (cp
->c_flag
& C_NOEXISTS
)) {
1059 * If there isn't an attributes b-tree then create one.
1061 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1062 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
1063 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
1068 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
1069 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
1072 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1074 /* Build the b-tree key. */
1075 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1076 if (iterator
== NULL
) {
1080 bzero(iterator
, sizeof(*iterator
));
1081 result
= hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1086 /* Preflight for replace/create semantics. */
1087 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1088 btdata
.bufferAddress
= &attrdata
;
1089 btdata
.itemSize
= sizeof(attrdata
);
1090 btdata
.itemCount
= 1;
1091 exists
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
) == 0;
1093 /* Replace requires that the attribute already exists. */
1094 if ((ap
->a_options
& XATTR_REPLACE
) && !exists
) {
1098 /* Create requires that the attribute doesn't exist. */
1099 if ((ap
->a_options
& XATTR_CREATE
) && exists
) {
1104 /* If it won't fit inline then use extent-based attributes. */
1105 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
1106 size_t extentbufsize
;
1109 u_int32_t
*keystartblk
;
1114 * setxattrs originating from in-kernel are not supported if they are bigger
1115 * than the inline max size. Just return ENOATTR and force them to do it with a
1122 /* Get some blocks. */
1123 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
1124 extentbufsize
= blkcnt
* sizeof(HFSPlusExtentDescriptor
);
1125 extentbufsize
= roundup(extentbufsize
, sizeof(HFSPlusExtentRecord
));
1126 MALLOC(extentptr
, HFSPlusExtentDescriptor
*, extentbufsize
, M_TEMP
, M_WAITOK
);
1127 if (extentptr
== NULL
) {
1131 bzero(extentptr
, extentbufsize
);
1132 result
= alloc_attr_blks(hfsmp
, attrsize
, extentbufsize
, extentptr
, &allocatedblks
);
1135 goto exit
; /* no more space */
1137 /* Copy data into the blocks. */
1138 result
= write_attr_data(hfsmp
, uio
, attrsize
, extentptr
);
1141 const char *name
= vnode_getname(vp
);
1142 printf("hfs_setxattr: write_attr_data vol=%s err (%d) %s:%s\n",
1143 hfsmp
->vcbVN
, result
, name
? name
: "", ap
->a_name
);
1145 vnode_putname(name
);
1150 /* Now remove any previous attribute. */
1152 result
= remove_attribute_records(hfsmp
, iterator
);
1155 const char *name
= vnode_getname(vp
);
1156 printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n",
1157 hfsmp
->vcbVN
, result
, name
? name
: "", ap
->a_name
);
1159 vnode_putname(name
);
1164 /* Create attribute fork data record. */
1165 MALLOC(recp
, HFSPlusAttrRecord
*, sizeof(HFSPlusAttrRecord
), M_TEMP
, M_WAITOK
);
1170 btdata
.bufferAddress
= recp
;
1171 btdata
.itemCount
= 1;
1172 btdata
.itemSize
= sizeof(HFSPlusAttrForkData
);
1174 recp
->recordType
= kHFSPlusAttrForkData
;
1175 recp
->forkData
.reserved
= 0;
1176 recp
->forkData
.theFork
.logicalSize
= attrsize
;
1177 recp
->forkData
.theFork
.clumpSize
= 0;
1178 recp
->forkData
.theFork
.totalBlocks
= blkcnt
;
1179 bcopy(extentptr
, recp
->forkData
.theFork
.extents
, sizeof(HFSPlusExtentRecord
));
1181 (void) hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1183 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1185 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
1186 hfsmp
->vcbVN
, target_id
, ap
->a_name
, result
);
1189 extentblks
= count_extent_blocks(blkcnt
, recp
->forkData
.theFork
.extents
);
1190 blkcnt
-= extentblks
;
1191 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1194 /* Create overflow extents as needed. */
1195 while (blkcnt
> 0) {
1196 /* Initialize the key and record. */
1197 *keystartblk
+= (u_int32_t
)extentblks
;
1198 btdata
.itemSize
= sizeof(HFSPlusAttrExtents
);
1199 recp
->recordType
= kHFSPlusAttrExtents
;
1200 recp
->overflowExtents
.reserved
= 0;
1202 /* Copy the next set of extents. */
1203 i
+= kHFSPlusExtentDensity
;
1204 bcopy(&extentptr
[i
], recp
->overflowExtents
.extents
, sizeof(HFSPlusExtentRecord
));
1206 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1208 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
1209 hfsmp
->vcbVN
, target_id
, ap
->a_name
, result
);
1212 extentblks
= count_extent_blocks(blkcnt
, recp
->overflowExtents
.extents
);
1213 blkcnt
-= extentblks
;
1215 } else { /* Inline data */
1217 result
= remove_attribute_records(hfsmp
, iterator
);
1223 /* Calculate size of record rounded up to multiple of 2 bytes. */
1224 btdata
.itemSize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
1225 MALLOC(recp
, HFSPlusAttrRecord
*, btdata
.itemSize
, M_TEMP
, M_WAITOK
);
1230 recp
->recordType
= kHFSPlusAttrInlineData
;
1231 recp
->attrData
.reserved
[0] = 0;
1232 recp
->attrData
.reserved
[1] = 0;
1233 recp
->attrData
.attrSize
= attrsize
;
1235 /* Copy in the attribute data (if any). */
1238 bcopy(data_ptr
, &recp
->attrData
.attrData
, attrsize
);
1241 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
1242 * then deny the copy operation.
1248 result
= uiomove((caddr_t
)&recp
->attrData
.attrData
, attrsize
, uio
);
1256 (void) hfs_buildattrkey(target_id
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1258 btdata
.bufferAddress
= recp
;
1259 btdata
.itemCount
= 1;
1260 result
= BTInsertRecord(btfile
, iterator
, &btdata
, btdata
.itemSize
);
1264 if (btfile
&& started_transaction
) {
1265 (void) BTFlushPath(btfile
);
1267 hfs_systemfile_unlock(hfsmp
, lockflags
);
1271 /* Setting an attribute only updates change time and not
1272 * modified time of the file.
1274 cp
->c_touch_chgtime
= TRUE
;
1275 cp
->c_flag
|= C_MODIFIED
;
1276 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
1277 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1278 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
1280 (void) hfs_update(vp
, 0);
1283 if (started_transaction
) {
1284 if (result
&& allocatedblks
) {
1285 free_attr_blks(hfsmp
, allocatedblks
, extentptr
);
1287 hfs_end_transaction(hfsmp
);
1294 FREE(extentptr
, M_TEMP
);
1297 FREE(iterator
, M_TEMP
);
1307 * Remove an extended attribute.
1310 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
1312 struct vnop_removexattr_args {
1313 struct vnodeop_desc *a_desc;
1317 vfs_context_t a_context;
1321 struct vnode
*vp
= ap
->a_vp
;
1322 struct cnode
*cp
= VTOC(vp
);
1323 struct hfsmount
*hfsmp
;
1324 struct BTreeIterator
* iterator
= NULL
;
1327 time_t orig_ctime
=VTOC(vp
)->c_ctime
;
1329 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
1330 return (EINVAL
); /* invalid name */
1333 if (VNODE_IS_RSRC(vp
)) {
1338 if (hfs_hides_xattr(ap
->a_context
, VTOC(vp
), ap
->a_name
, 1) && !(ap
->a_options
& XATTR_SHOWCOMPRESSION
)) {
1341 #endif /* HFS_COMPRESSION */
1343 check_for_tracked_file(vp
, orig_ctime
, NAMESPACE_HANDLER_METADATA_DELETE_OP
, NSPACE_REARM_NO_ARG
);
1345 /* If Resource Fork is non-empty then truncate it. */
1346 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1347 struct vnode
*rvp
= NULL
;
1349 if ( !vnode_isreg(vp
) ) {
1352 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1355 if (!hfs_has_rsrc(cp
)) {
1359 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
);
1365 hfs_lock_truncate(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
1367 // Tell UBC now before we take the cnode lock and start the transaction
1368 hfs_ubc_setsize(rvp
, 0, false);
1370 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1371 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1376 /* Start a transaction for encapsulating changes in
1377 * hfs_truncate() and hfs_update()
1379 if ((result
= hfs_start_transaction(hfsmp
))) {
1380 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1386 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, ap
->a_context
);
1388 cp
->c_touch_chgtime
= TRUE
;
1389 cp
->c_flag
|= C_MODIFIED
;
1390 result
= hfs_update(vp
, 0);
1393 hfs_end_transaction(hfsmp
);
1394 hfs_unlock_truncate(VTOC(rvp
), HFS_LOCK_DEFAULT
);
1395 hfs_unlock(VTOC(rvp
));
1400 /* Clear out the Finder Info. */
1401 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1402 void * finderinfo_start
;
1403 int finderinfo_size
;
1404 u_int8_t finderinfo
[32];
1405 u_int32_t date_added
, write_gen_counter
, document_id
;
1406 u_int8_t
*finfo
= NULL
;
1408 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1412 /* Use the local copy to store our temporary changes. */
1413 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
1416 /* Zero out the date added field in the local copy */
1417 hfs_zero_hidden_fields (cp
, finderinfo
);
1419 /* Don't expose a symlink's private type/creator. */
1420 if (vnode_islnk(vp
)) {
1421 struct FndrFileInfo
*fip
;
1423 fip
= (struct FndrFileInfo
*)&finderinfo
;
1428 /* Do the byte compare against the local copy */
1429 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
1435 * If there was other content, zero out everything except
1436 * type/creator and date added. First, save the date added.
1438 finfo
= cp
->c_finderinfo
;
1440 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
1441 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
1442 date_added
= extinfo
->date_added
;
1443 write_gen_counter
= extinfo
->write_gen_counter
;
1444 document_id
= extinfo
->document_id
;
1445 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
1446 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
1447 date_added
= extinfo
->date_added
;
1448 write_gen_counter
= extinfo
->write_gen_counter
;
1449 document_id
= extinfo
->document_id
;
1452 if (vnode_islnk(vp
)) {
1453 /* Ignore type/creator */
1454 finderinfo_start
= &cp
->c_finderinfo
[8];
1455 finderinfo_size
= sizeof(cp
->c_finderinfo
) - 8;
1457 finderinfo_start
= &cp
->c_finderinfo
[0];
1458 finderinfo_size
= sizeof(cp
->c_finderinfo
);
1460 bzero(finderinfo_start
, finderinfo_size
);
1463 /* Now restore the date added */
1464 if (S_ISREG(cp
->c_attr
.ca_mode
) || S_ISLNK(cp
->c_attr
.ca_mode
)) {
1465 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
1466 extinfo
->date_added
= date_added
;
1467 extinfo
->write_gen_counter
= write_gen_counter
;
1468 extinfo
->document_id
= document_id
;
1469 } else if (S_ISDIR(cp
->c_attr
.ca_mode
)) {
1470 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
1471 extinfo
->date_added
= date_added
;
1472 extinfo
->write_gen_counter
= write_gen_counter
;
1473 extinfo
->document_id
= document_id
;
1476 /* Updating finderInfo updates change time and modified time */
1477 cp
->c_touch_chgtime
= TRUE
;
1478 cp
->c_flag
|= C_MODIFIED
;
1486 * Standard HFS only supports native FinderInfo and Resource Forks.
1488 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1491 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1495 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1496 if (iterator
== NULL
) {
1499 bzero(iterator
, sizeof(*iterator
));
1501 if ((result
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1505 result
= hfs_buildattrkey(cp
->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
1510 if (hfs_start_transaction(hfsmp
) != 0) {
1514 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1516 result
= remove_attribute_records(hfsmp
, iterator
);
1518 hfs_systemfile_unlock(hfsmp
, lockflags
);
1521 cp
->c_touch_chgtime
= TRUE
;
1523 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1525 /* If no more attributes exist, clear attribute bit */
1526 result
= file_attribute_exist(hfsmp
, cp
->c_fileid
);
1528 cp
->c_attr
.ca_recflags
&= ~kHFSHasAttributesMask
;
1529 cp
->c_flag
|= C_MODIFIED
;
1531 if (result
== EEXIST
) {
1535 hfs_systemfile_unlock(hfsmp
, lockflags
);
1537 /* If ACL was removed, clear security bit */
1538 if ((bcmp(ap
->a_name
, KAUTH_FILESEC_XATTR
, sizeof(KAUTH_FILESEC_XATTR
)) == 0)) {
1539 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
1540 cp
->c_flag
|= C_MODIFIED
;
1542 (void) hfs_update(vp
, 0);
1545 hfs_end_transaction(hfsmp
);
1549 FREE(iterator
, M_TEMP
);
1550 return MacToVFSError(result
);
1553 /* Check if any attribute record exist for given fileID. This function
1554 * is called by hfs_vnop_removexattr to determine if it should clear the
1555 * attribute bit in the catalog record or not.
1557 * Note - you must acquire a shared lock on the attribute btree before
1558 * calling this function.
1561 * EEXIST - If attribute record was found
1562 * 0 - Attribute was not found
1563 * (other) - Other error (such as EIO)
1566 file_attribute_exist(struct hfsmount
*hfsmp
, uint32_t fileID
)
1568 HFSPlusAttrKey
*key
;
1569 struct BTreeIterator
* iterator
= NULL
;
1570 struct filefork
*btfile
;
1573 // if there's no attribute b-tree we sure as heck
1574 // can't have any attributes!
1575 if (hfsmp
->hfs_attribute_vp
== NULL
) {
1579 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1580 if (iterator
== NULL
) {
1584 bzero(iterator
, sizeof(*iterator
));
1585 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1587 result
= hfs_buildattrkey(fileID
, NULL
, key
);
1592 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1593 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1594 if (result
&& (result
!= btNotFound
)) {
1598 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1599 /* If no next record was found or fileID for next record did not match,
1600 * no more attributes exist for this fileID
1602 if ((result
&& (result
== btNotFound
)) || (key
->fileID
!= fileID
)) {
1610 FREE(iterator
, M_TEMP
);
1617 * Remove all the records for a given attribute.
1619 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1620 * - A transaction must have been started.
1621 * - The Attribute b-tree file must be locked exclusive.
1622 * - The Allocation Bitmap file must be locked exclusive.
1623 * - The iterator key must be initialized.
1626 remove_attribute_records(struct hfsmount
*hfsmp
, BTreeIterator
* iterator
)
1628 struct filefork
*btfile
;
1629 FSBufferDescriptor btdata
;
1630 HFSPlusAttrRecord attrdata
; /* 90 bytes */
1634 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1636 btdata
.bufferAddress
= &attrdata
;
1637 btdata
.itemSize
= sizeof(attrdata
);
1638 btdata
.itemCount
= 1;
1639 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1641 goto exit
; /* no records. */
1644 * Free the blocks from extent based attributes.
1646 * Note that the block references (btree records) are removed
1647 * before releasing the blocks in the allocation bitmap.
1649 if (attrdata
.recordType
== kHFSPlusAttrForkData
) {
1652 u_int32_t
*keystartblk
;
1654 if (datasize
< sizeof(HFSPlusAttrForkData
)) {
1655 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize
, sizeof(HFSPlusAttrForkData
));
1657 totalblks
= attrdata
.forkData
.theFork
.totalBlocks
;
1659 /* Process the first 8 extents. */
1660 extentblks
= count_extent_blocks(totalblks
, attrdata
.forkData
.theFork
.extents
);
1661 if (extentblks
> totalblks
)
1662 panic("hfs: remove_attribute_records: corruption...");
1663 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1664 free_attr_blks(hfsmp
, extentblks
, attrdata
.forkData
.theFork
.extents
);
1666 totalblks
-= extentblks
;
1667 keystartblk
= &((HFSPlusAttrKey
*)&iterator
->key
)->startBlock
;
1669 /* Process any overflow extents. */
1671 *keystartblk
+= (u_int32_t
)extentblks
;
1673 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
1675 (attrdata
.recordType
!= kHFSPlusAttrExtents
) ||
1676 (datasize
< sizeof(HFSPlusAttrExtents
))) {
1677 printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n",
1678 hfsmp
->vcbVN
, MacToVFSError(result
), attrdata
.recordType
!= kHFSPlusAttrExtents
, totalblks
);
1680 break; /* break from while */
1682 /* Process the next 8 extents. */
1683 extentblks
= count_extent_blocks(totalblks
, attrdata
.overflowExtents
.extents
);
1684 if (extentblks
> totalblks
)
1685 panic("hfs: remove_attribute_records: corruption...");
1686 if (BTDeleteRecord(btfile
, iterator
) == 0) {
1687 free_attr_blks(hfsmp
, extentblks
, attrdata
.overflowExtents
.extents
);
1689 totalblks
-= extentblks
;
1692 result
= BTDeleteRecord(btfile
, iterator
);
1694 (void) BTFlushPath(btfile
);
1696 return (result
== btNotFound
? ENOATTR
: MacToVFSError(result
));
1701 * Retrieve the list of extended attribute names.
1704 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
1706 struct vnop_listxattr_args {
1707 struct vnodeop_desc *a_desc;
1712 vfs_context_t a_context;
1715 struct vnode
*vp
= ap
->a_vp
;
1716 struct cnode
*cp
= VTOC(vp
);
1717 struct hfsmount
*hfsmp
;
1718 uio_t uio
= ap
->a_uio
;
1719 struct BTreeIterator
* iterator
= NULL
;
1720 struct filefork
*btfile
;
1721 struct listattr_callback_state state
;
1722 user_addr_t user_start
= 0;
1723 user_size_t user_len
= 0;
1726 u_int8_t finderinfo
[32];
1729 if (VNODE_IS_RSRC(vp
)) {
1734 int compressed
= hfs_file_is_compressed(cp
, 1); /* 1 == don't take the cnode lock */
1735 #endif /* HFS_COMPRESSION */
1741 * Take the truncate lock; this serializes us against the ioctl
1742 * to truncate data & reset the decmpfs state
1743 * in the compressed file handler.
1745 hfs_lock_truncate(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
1747 /* Now the regular cnode lock (shared) */
1748 if ((result
= hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
))) {
1749 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1754 * Make a copy of the cnode's finderinfo to a local so we can
1755 * zero out the date added field. Also zero out the private type/creator
1758 bcopy(cp
->c_finderinfo
, finderinfo
, sizeof(finderinfo
));
1759 hfs_zero_hidden_fields (cp
, finderinfo
);
1761 /* Don't expose a symlink's private type/creator. */
1762 if (vnode_islnk(vp
)) {
1763 struct FndrFileInfo
*fip
;
1765 fip
= (struct FndrFileInfo
*)&finderinfo
;
1771 /* If Finder Info is non-empty then export it's name. */
1772 if (bcmp(finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) != 0) {
1774 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
1775 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1779 result
= uiomove(XATTR_FINDERINFO_NAME
,
1780 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1785 /* If Resource Fork is non-empty then export it's name. */
1786 if (S_ISREG(cp
->c_mode
) && hfs_has_rsrc(cp
)) {
1788 if ((ap
->a_options
& XATTR_SHOWCOMPRESSION
) ||
1790 !decmpfs_hides_rsrc(ap
->a_context
, VTOCMP(vp
))
1792 #endif /* HFS_COMPRESSION */
1795 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1796 } else if ((user_size_t
)uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1800 result
= uiomove(XATTR_RESOURCEFORK_NAME
,
1801 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
1808 * Standard HFS only supports native FinderInfo and Resource Forks.
1809 * Return at this point.
1811 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1815 /* Bail if we don't have any extended attributes. */
1816 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
1817 (cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
1821 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1823 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1824 if (iterator
== NULL
) {
1828 bzero(iterator
, sizeof(*iterator
));
1829 result
= hfs_buildattrkey(cp
->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
1834 * Lock the user's buffer here so that we won't fault on
1835 * it in uiomove while holding the attributes b-tree lock.
1837 if (uio
&& uio_isuserspace(uio
)) {
1838 user_start
= uio_curriovbase(uio
);
1839 user_len
= uio_curriovlen(uio
);
1841 if ((result
= vslock(user_start
, user_len
)) != 0) {
1846 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1848 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
1849 if (result
&& result
!= btNotFound
) {
1850 hfs_systemfile_unlock(hfsmp
, lockflags
);
1854 state
.fileID
= cp
->c_fileid
;
1859 state
.showcompressed
= !compressed
|| ap
->a_options
& XATTR_SHOWCOMPRESSION
;
1860 state
.ctx
= ap
->a_context
;
1862 #endif /* HFS_COMPRESSION */
1865 * Process entries starting just after iterator->key.
1867 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
1868 (IterateCallBackProcPtr
)listattr_callback
, &state
);
1869 hfs_systemfile_unlock(hfsmp
, lockflags
);
1871 *ap
->a_size
+= state
.size
;
1874 if (state
.result
|| result
== btNotFound
)
1875 result
= state
.result
;
1879 vsunlock(user_start
, user_len
, TRUE
);
1882 FREE(iterator
, M_TEMP
);
1885 hfs_unlock_truncate(cp
, HFS_LOCK_DEFAULT
);
1887 return MacToVFSError(result
);
1892 * Callback - called for each attribute record
1895 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
1897 char attrname
[XATTR_MAXNAMELEN
+ 1];
1901 if (state
->fileID
!= key
->fileID
) {
1903 return (0); /* stop */
1906 * Skip over non-primary keys
1908 if (key
->startBlock
!= 0) {
1909 return (1); /* continue */
1912 /* Convert the attribute name into UTF-8. */
1913 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
1914 (u_int8_t
*)attrname
, (size_t *)&bytecount
, sizeof(attrname
), '/', 0);
1916 state
->result
= result
;
1917 return (0); /* stop */
1919 bytecount
++; /* account for null termination char */
1921 if (xattr_protected(attrname
))
1922 return (1); /* continue */
1925 if (!state
->showcompressed
&& decmpfs_hides_xattr(state
->ctx
, VTOCMP(state
->vp
), attrname
) )
1926 return 1; /* continue */
1927 #endif /* HFS_COMPRESSION */
1929 if (state
->uio
== NULL
) {
1930 state
->size
+= bytecount
;
1932 if (bytecount
> uio_resid(state
->uio
)) {
1933 state
->result
= ERANGE
;
1934 return (0); /* stop */
1936 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
1938 state
->result
= result
;
1939 return (0); /* stop */
1942 return (1); /* continue */
1946 * Remove all the attributes from a cnode.
1948 * This function creates/ends its own transaction so that each
1949 * attribute is deleted in its own transaction (to avoid having
1950 * a transaction grow too large).
1952 * This function takes the necessary locks on the attribute
1953 * b-tree file and the allocation (bitmap) file.
1955 * NOTE: Upon sucecss, this function will return with an open
1956 * transaction. The reason we do it this way is because when we
1957 * delete the last attribute, we must make sure the flag in the
1958 * catalog record that indicates there are no more records is cleared.
1959 * The caller is responsible for doing this and *must* do it before
1960 * ending the transaction.
1963 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
,
1964 bool *open_transaction
)
1966 BTreeIterator
*iterator
= NULL
;
1967 HFSPlusAttrKey
*key
;
1968 struct filefork
*btfile
;
1969 int result
, lockflags
= 0;
1971 *open_transaction
= false;
1973 if (hfsmp
->hfs_attribute_vp
== NULL
)
1976 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
1978 MALLOC(iterator
, BTreeIterator
*, sizeof(BTreeIterator
), M_TEMP
, M_WAITOK
);
1979 if (iterator
== NULL
) {
1982 bzero(iterator
, sizeof(BTreeIterator
));
1983 key
= (HFSPlusAttrKey
*)&iterator
->key
;
1985 /* Loop until there are no more attributes for this file id */
1987 if (!*open_transaction
)
1988 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1990 (void) hfs_buildattrkey(fileid
, NULL
, key
);
1991 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1992 if (result
|| key
->fileID
!= fileid
)
1995 hfs_systemfile_unlock(hfsmp
, lockflags
);
1998 if (*open_transaction
) {
1999 hfs_end_transaction(hfsmp
);
2000 *open_transaction
= false;
2003 if (hfs_start_transaction(hfsmp
) != 0) {
2008 *open_transaction
= true;
2010 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2012 result
= remove_attribute_records(hfsmp
, iterator
);
2014 #if HFS_XATTR_VERBOSE
2016 printf("hfs_removeallattr: unexpected err %d\n", result
);
2022 FREE(iterator
, M_TEMP
);
2025 hfs_systemfile_unlock(hfsmp
, lockflags
);
2027 result
= result
== btNotFound
? 0 : MacToVFSError(result
);
2029 if (result
&& *open_transaction
) {
2030 hfs_end_transaction(hfsmp
);
2031 *open_transaction
= false;
2039 hfs_xattr_init(struct hfsmount
* hfsmp
)
2042 * If there isn't an attributes b-tree then create one.
2044 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
) &&
2045 (hfsmp
->hfs_attribute_vp
== NULL
) &&
2046 !(hfsmp
->hfs_flags
& HFS_READ_ONLY
)) {
2047 (void) hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
2048 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
2050 if (hfsmp
->hfs_attribute_vp
)
2051 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
2055 * Enable/Disable volume attributes stored as EA for root file system.
2056 * Supported attributes are -
2057 * 1. Extent-based Extended Attributes
2060 hfs_set_volxattr(struct hfsmount
*hfsmp
, unsigned int xattrtype
, int state
)
2062 struct BTreeIterator
* iterator
= NULL
;
2063 struct filefork
*btfile
;
2067 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
2070 if (xattrtype
!= HFS_SET_XATTREXTENTS_STATE
) {
2075 * If there isn't an attributes b-tree then create one.
2077 if (hfsmp
->hfs_attribute_vp
== NULL
) {
2078 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
2079 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
2085 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2086 if (iterator
== NULL
) {
2089 bzero(iterator
, sizeof(*iterator
));
2092 * Build a b-tree key.
2093 * We use the root's parent id (1) to hold this volume attribute.
2095 (void) hfs_buildattrkey(kHFSRootParentID
, XATTR_XATTREXTENTS_NAME
,
2096 (HFSPlusAttrKey
*)&iterator
->key
);
2098 /* Start a transaction for our changes. */
2099 if (hfs_start_transaction(hfsmp
) != 0) {
2103 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
2105 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
2108 /* Remove the attribute. */
2109 result
= BTDeleteRecord(btfile
, iterator
);
2110 if (result
== btNotFound
)
2113 FSBufferDescriptor btdata
;
2114 HFSPlusAttrData attrdata
;
2117 datasize
= sizeof(attrdata
);
2118 btdata
.bufferAddress
= &attrdata
;
2119 btdata
.itemSize
= datasize
;
2120 btdata
.itemCount
= 1;
2121 attrdata
.recordType
= kHFSPlusAttrInlineData
;
2122 attrdata
.reserved
[0] = 0;
2123 attrdata
.reserved
[1] = 0;
2124 attrdata
.attrSize
= 2;
2125 attrdata
.attrData
[0] = 0;
2126 attrdata
.attrData
[1] = 0;
2128 /* Insert the attribute. */
2129 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
2130 if (result
== btExists
)
2133 (void) BTFlushPath(btfile
);
2135 hfs_systemfile_unlock(hfsmp
, lockflags
);
2137 /* Finish the transaction of our changes. */
2138 hfs_end_transaction(hfsmp
);
2140 /* Update the state in the mount point */
2141 hfs_lock_mount (hfsmp
);
2143 hfsmp
->hfs_flags
&= ~HFS_XATTR_EXTENTS
;
2145 hfsmp
->hfs_flags
|= HFS_XATTR_EXTENTS
;
2147 hfs_unlock_mount (hfsmp
);
2151 FREE(iterator
, M_TEMP
);
2153 return MacToVFSError(result
);
2158 * hfs_attrkeycompare - compare two attribute b-tree keys.
2160 * The name portion of the key is compared using a 16-bit binary comparison.
2161 * This is called from the b-tree code.
2165 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
2167 u_int32_t searchFileID
, trialFileID
;
2170 searchFileID
= searchKey
->fileID
;
2171 trialFileID
= trialKey
->fileID
;
2174 if (searchFileID
> trialFileID
) {
2176 } else if (searchFileID
< trialFileID
) {
2179 u_int16_t
* str1
= &searchKey
->attrName
[0];
2180 u_int16_t
* str2
= &trialKey
->attrName
[0];
2181 int length1
= searchKey
->attrNameLen
;
2182 int length2
= trialKey
->attrNameLen
;
2186 if (length1
< length2
) {
2189 } else if (length1
> length2
) {
2212 * Names are equal; compare startBlock
2214 if (searchKey
->startBlock
== trialKey
->startBlock
) {
2217 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
2226 * hfs_buildattrkey - build an Attribute b-tree key
2230 hfs_buildattrkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
2233 size_t unicodeBytes
= 0;
2235 if (attrname
!= NULL
) {
2237 * Convert filename from UTF-8 into Unicode
2239 result
= utf8_decodestr((const u_int8_t
*)attrname
, strlen(attrname
), key
->attrName
,
2240 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
2242 if (result
!= ENAMETOOLONG
)
2243 result
= EINVAL
; /* name has invalid characters */
2246 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
2247 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
2249 key
->attrNameLen
= 0;
2250 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
2253 key
->fileID
= fileID
;
2254 key
->startBlock
= 0;
2260 * getnodecount - calculate starting node count for attributes b-tree.
2263 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
2265 u_int64_t freebytes
;
2266 u_int64_t calcbytes
;
2269 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
2270 * 10.5: Attempt to be as big as the catalog clump size.
2272 * Use no more than 10 % of the remaining free space.
2274 freebytes
= (u_int64_t
)hfs_freeblks(hfsmp
, 0) * (u_int64_t
)hfsmp
->blockSize
;
2276 calcbytes
= MIN(hfsmp
->hfs_catalog_cp
->c_datafork
->ff_size
/ 5, 20 * 1024 * 1024);
2278 calcbytes
= MAX(calcbytes
, hfsmp
->hfs_catalog_cp
->c_datafork
->ff_clumpsize
);
2280 calcbytes
= MIN(calcbytes
, freebytes
/ 10);
2282 return (MAX(2, (int)(calcbytes
/ nodesize
)));
2287 * getmaxinlineattrsize - calculate maximum inline attribute size.
2289 * This yields 3,802 bytes for an 8K node size.
2292 getmaxinlineattrsize(struct vnode
* attrvp
)
2294 struct BTreeInfoRec btinfo
;
2295 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
2298 if (attrvp
!= NULL
) {
2299 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2300 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
2301 nodesize
= btinfo
.nodeSize
;
2302 hfs_unlock(VTOC(attrvp
));
2305 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
2306 maxsize
-= 3 * sizeof(u_int16_t
); /* minus 3 index slots */
2307 maxsize
/= 2; /* 2 key/rec pairs minumum */
2308 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
2309 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
2310 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */
2316 * Initialize vnode for attribute data I/O.
2320 * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp
2321 * - an iocount is taken on the attrdata vnode which exists
2322 * for the entire duration of the mount. It is only dropped
2324 * - the attrdata cnode is not locked
2327 * - returns non-zero value
2328 * - the caller does not have to worry about any locks or references
2330 int init_attrdata_vnode(struct hfsmount
*hfsmp
)
2334 struct cat_desc cat_desc
;
2335 struct cat_attr cat_attr
;
2336 struct cat_fork cat_fork
;
2337 int newvnode_flags
= 0;
2339 bzero(&cat_desc
, sizeof(cat_desc
));
2340 cat_desc
.cd_parentcnid
= kHFSRootParentID
;
2341 cat_desc
.cd_nameptr
= (const u_int8_t
*)hfs_attrdatafilename
;
2342 cat_desc
.cd_namelen
= strlen(hfs_attrdatafilename
);
2343 cat_desc
.cd_cnid
= kHFSAttributeDataFileID
;
2344 /* Tag vnode as system file, note that we can still use cluster I/O */
2345 cat_desc
.cd_flags
|= CD_ISMETA
;
2347 bzero(&cat_attr
, sizeof(cat_attr
));
2348 cat_attr
.ca_linkcount
= 1;
2349 cat_attr
.ca_mode
= S_IFREG
;
2350 cat_attr
.ca_fileid
= cat_desc
.cd_cnid
;
2351 cat_attr
.ca_blocks
= hfsmp
->totalBlocks
;
2354 * The attribute data file is a virtual file that spans the
2355 * entire file system space.
2357 * Each extent-based attribute occupies a unique portion of
2358 * in this virtual file. The cluster I/O is done using actual
2359 * allocation block offsets so no additional mapping is needed
2360 * for the VNOP_BLOCKMAP call.
2362 * This approach allows the attribute data to be cached without
2363 * incurring the high cost of using a separate vnode per attribute.
2365 * Since we need to acquire the attribute b-tree file lock anyways,
2366 * the virtual file doesn't introduce any additional serialization.
2368 bzero(&cat_fork
, sizeof(cat_fork
));
2369 cat_fork
.cf_size
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
2370 cat_fork
.cf_blocks
= hfsmp
->totalBlocks
;
2371 cat_fork
.cf_extents
[0].startBlock
= 0;
2372 cat_fork
.cf_extents
[0].blockCount
= cat_fork
.cf_blocks
;
2374 result
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cat_desc
, 0, &cat_attr
,
2375 &cat_fork
, &vp
, &newvnode_flags
);
2377 hfsmp
->hfs_attrdata_vp
= vp
;
2378 hfs_unlock(VTOC(vp
));
2384 * Read an extent based attribute.
2387 read_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2389 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2397 hfs_lock_truncate(VTOC(evp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2399 bufsize
= (int)uio_resid(uio
);
2400 attrsize
= (int)datasize
;
2401 blksize
= (int)hfsmp
->blockSize
;
2404 * Read the attribute data one extent at a time.
2405 * For the typical case there is only one extent.
2407 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2408 iosize
= extents
[i
].blockCount
* blksize
;
2409 iosize
= MIN(iosize
, attrsize
);
2410 iosize
= MIN(iosize
, bufsize
);
2411 uio_setresid(uio
, iosize
);
2412 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2414 result
= cluster_read(evp
, uio
, VTOF(evp
)->ff_size
, IO_SYNC
| IO_UNIT
);
2416 #if HFS_XATTR_VERBOSE
2417 printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n",
2418 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2425 uio_setresid(uio
, bufsize
);
2426 uio_setoffset(uio
, datasize
);
2428 hfs_unlock_truncate(VTOC(evp
), HFS_LOCK_DEFAULT
);
2433 * Write an extent based attribute.
2436 write_attr_data(struct hfsmount
*hfsmp
, uio_t uio
, size_t datasize
, HFSPlusExtentDescriptor
*extents
)
2438 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2447 hfs_lock_truncate(VTOC(evp
), HFS_SHARED_LOCK
, HFS_LOCK_DEFAULT
);
2449 bufsize
= uio_resid(uio
);
2450 attrsize
= (int) datasize
;
2451 blksize
= (int) hfsmp
->blockSize
;
2452 filesize
= VTOF(evp
)->ff_size
;
2455 * Write the attribute data one extent at a time.
2457 for (i
= 0; (attrsize
> 0) && (bufsize
> 0) && (extents
[i
].startBlock
!= 0); ++i
) {
2458 iosize
= extents
[i
].blockCount
* blksize
;
2459 iosize
= MIN(iosize
, attrsize
);
2460 iosize
= MIN(iosize
, bufsize
);
2461 uio_setresid(uio
, iosize
);
2462 uio_setoffset(uio
, (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)blksize
);
2464 result
= cluster_write(evp
, uio
, filesize
, filesize
, filesize
,
2465 (off_t
) 0, IO_SYNC
| IO_UNIT
);
2466 #if HFS_XATTR_VERBOSE
2467 printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n",
2468 iosize
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2475 uio_setresid(uio
, bufsize
);
2476 uio_setoffset(uio
, datasize
);
2478 hfs_unlock_truncate(VTOC(evp
), HFS_LOCK_DEFAULT
);
2483 * Allocate blocks for an extent based attribute.
2486 alloc_attr_blks(struct hfsmount
*hfsmp
, size_t attrsize
, size_t extentbufsize
, HFSPlusExtentDescriptor
*extents
, int *blocks
)
2495 startblk
= hfsmp
->hfs_metazone_end
;
2496 blkcnt
= howmany(attrsize
, hfsmp
->blockSize
);
2497 if (blkcnt
> (int)hfs_freeblks(hfsmp
, 0)) {
2501 maxextents
= extentbufsize
/ sizeof(HFSPlusExtentDescriptor
);
2503 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2505 for (i
= 0; (blkcnt
> 0) && (i
< maxextents
); i
++) {
2506 /* Try allocating and see if we find something decent */
2507 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, 0,
2508 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
2510 * If we couldn't find anything, then re-try the allocation but allow
2513 if (result
== dskFulErr
) {
2514 result
= BlockAllocate(hfsmp
, startblk
, blkcnt
, blkcnt
, HFS_ALLOC_FLUSHTXN
,
2515 &extents
[i
].startBlock
, &extents
[i
].blockCount
);
2519 #if HFS_XATTR_VERBOSE
2520 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2521 blkcnt
, extents
[i
].startBlock
, extents
[i
].blockCount
, result
);
2524 extents
[i
].startBlock
= 0;
2525 extents
[i
].blockCount
= 0;
2528 blkcnt
-= extents
[i
].blockCount
;
2529 startblk
= extents
[i
].startBlock
+ extents
[i
].blockCount
;
2532 * If it didn't fit in the extents buffer then bail.
2537 #if HFS_XATTR_VERBOSE
2538 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt
);
2540 for (; i
>= 0; i
--) {
2541 if ((blkcnt
= extents
[i
].blockCount
) != 0) {
2542 (void) BlockDeallocate(hfsmp
, extents
[i
].startBlock
, blkcnt
, 0);
2543 extents
[i
].startBlock
= 0;
2544 extents
[i
].blockCount
= 0;
2549 hfs_systemfile_unlock(hfsmp
, lockflags
);
2550 return MacToVFSError(result
);
2554 * Release blocks from an extent based attribute.
2557 free_attr_blks(struct hfsmount
*hfsmp
, int blkcnt
, HFSPlusExtentDescriptor
*extents
)
2559 vnode_t evp
= hfsmp
->hfs_attrdata_vp
;
2560 int remblks
= blkcnt
;
2564 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
2566 for (i
= 0; (remblks
> 0) && (extents
[i
].blockCount
!= 0); i
++) {
2567 if (extents
[i
].blockCount
> (u_int32_t
)blkcnt
) {
2568 #if HFS_XATTR_VERBOSE
2569 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
2570 extents
[i
].startBlock
, extents
[i
].blockCount
);
2572 extents
[i
].blockCount
= 0;
2575 if (extents
[i
].startBlock
== 0) {
2578 (void)BlockDeallocate(hfsmp
, extents
[i
].startBlock
, extents
[i
].blockCount
, 0);
2579 remblks
-= extents
[i
].blockCount
;
2580 extents
[i
].startBlock
= 0;
2581 extents
[i
].blockCount
= 0;
2583 #if HFS_XATTR_VERBOSE
2584 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
2585 extents
[i
].startBlock
, extents
[i
].blockCount
);
2587 /* Discard any resident pages for this block range. */
2591 start
= (u_int64_t
)extents
[i
].startBlock
* (u_int64_t
)hfsmp
->blockSize
;
2592 end
= start
+ (u_int64_t
)extents
[i
].blockCount
* (u_int64_t
)hfsmp
->blockSize
;
2593 (void) ubc_msync(hfsmp
->hfs_attrdata_vp
, start
, end
, &start
, UBC_INVALIDATE
);
2597 hfs_systemfile_unlock(hfsmp
, lockflags
);
2601 has_overflow_extents(HFSPlusForkData
*forkdata
)
2605 if (forkdata
->extents
[7].blockCount
== 0)
2608 blocks
= forkdata
->extents
[0].blockCount
+
2609 forkdata
->extents
[1].blockCount
+
2610 forkdata
->extents
[2].blockCount
+
2611 forkdata
->extents
[3].blockCount
+
2612 forkdata
->extents
[4].blockCount
+
2613 forkdata
->extents
[5].blockCount
+
2614 forkdata
->extents
[6].blockCount
+
2615 forkdata
->extents
[7].blockCount
;
2617 return (forkdata
->totalBlocks
> blocks
);
2621 count_extent_blocks(int maxblks
, HFSPlusExtentRecord extents
)
2626 for (i
= 0, blocks
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
2627 /* Ignore obvious bogus extents. */
2628 if (extents
[i
].blockCount
> (u_int32_t
)maxblks
)
2630 if (extents
[i
].startBlock
== 0 || extents
[i
].blockCount
== 0)
2632 blocks
+= extents
[i
].blockCount
;