2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/kernel.h>
26 #include <sys/malloc.h>
27 #include <sys/utfconv.h>
28 #include <sys/vnode.h>
29 #include <sys/xattr.h>
32 #include "hfs_cnode.h"
33 #include "hfs_mount.h"
34 #include "hfs_format.h"
35 #include "hfs_endian.h"
37 #include "hfscommon/headers/BTreesInternal.h"
40 #define ATTRIBUTE_FILE_NODE_SIZE 8192
43 /* State information for the listattr_callback callback function. */
44 struct listattr_callback_state
{
51 #define HFS_MAXATTRIBUTESIZE (1024*1024)
53 /* HFS Internal Names */
54 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
57 #define RESOURCE_FORK_EXISTS(VP) \
58 ((VTOC((VP))->c_blocks - VTOF((VP))->ff_blocks) > 0)
60 static u_int32_t emptyfinfo
[8] = {0};
63 extern int hfs_create_attr_btree(struct hfsmount
*hfsmp
, uint32_t nodesize
, uint32_t nodecnt
);
66 int hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
);
67 int hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
);
68 int hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
);
69 int hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
);
70 int hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
);
74 static int listattr_callback(const HFSPlusAttrKey
*key
, const HFSPlusAttrData
*data
,
75 struct listattr_callback_state
*state
);
77 static int buildkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
);
79 static int getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
);
81 static size_t getmaxinlineattrsize(struct vnode
* attrvp
);
84 * Retrieve the data of an extended attribute.
88 hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
)
90 struct vnop_getxattr_args {
91 struct vnodeop_desc *a_desc;
97 vfs_context_t a_context;
101 struct vnode
*vp
= ap
->a_vp
;
102 struct hfsmount
*hfsmp
;
103 uio_t uio
= ap
->a_uio
;
104 struct BTreeIterator
* iterator
= NULL
;
105 struct filefork
*btfile
;
106 FSBufferDescriptor btdata
;
107 HFSPlusAttrData
* datap
= NULL
;
113 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
114 return (EINVAL
); /* invalid name */
118 if (!VNODE_IS_RSRC(vp
)) {
119 /* Get the Finder Info. */
120 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
123 /* If Finder Info is empty then it doesn't exist. */
124 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
128 *ap
->a_size
= bufsize
;
131 if (uio_resid(uio
) < bufsize
)
134 result
= uiomove((caddr_t
) &VTOC(vp
)->c_finderinfo
, bufsize
, uio
);
138 /* Read the Resource Fork. */
139 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
140 struct vnode
*rvp
= NULL
;
142 if ( !vnode_isreg(vp
) ) {
145 if ( !RESOURCE_FORK_EXISTS(vp
)) {
148 if ((result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, vfs_context_proc(ap
->a_context
)))) {
152 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
154 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
161 * Standard HFS only supports native FinderInfo and Resource Forks.
163 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
166 /* Bail if we don't have any extended attributes. */
167 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
168 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
171 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
173 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
174 bzero(iterator
, sizeof(*iterator
));
176 bufsize
= sizeof(HFSPlusAttrData
) - 2;
178 bufsize
+= uio_resid(uio
);
179 MALLOC(datap
, HFSPlusAttrData
*, bufsize
, M_TEMP
, M_WAITOK
);
180 btdata
.bufferAddress
= datap
;
181 btdata
.itemSize
= bufsize
;
182 btdata
.itemCount
= 1;
184 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
188 /* Lookup the attribute. */
189 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
190 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
191 hfs_systemfile_unlock(hfsmp
, lockflags
);
194 if (result
== btNotFound
)
199 *ap
->a_size
= datap
->attrSize
;
201 /* Copy out the attribute data. */
203 if (datap
->attrSize
> uio_resid(uio
))
206 result
= uiomove((caddr_t
) &datap
->attrData
, datap
->attrSize
, uio
);
210 FREE(iterator
, M_TEMP
);
212 return MacToVFSError(result
);
216 * Set the data of an extended attribute.
220 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
222 struct vnop_setxattr_args {
223 struct vnodeop_desc *a_desc;
228 vfs_context_t a_context;
232 struct vnode
*vp
= ap
->a_vp
;
233 struct hfsmount
*hfsmp
;
234 uio_t uio
= ap
->a_uio
;
235 struct BTreeIterator
* iterator
= NULL
;
236 struct filefork
*btfile
;
238 FSBufferDescriptor btdata
;
239 HFSPlusAttrData
* datap
= NULL
;
244 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
245 return (EINVAL
); /* invalid name */
248 if (VNODE_IS_RSRC(vp
)) {
251 /* Set the Finder Info. */
252 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
255 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
))) {
256 /* attr exists and "create" was specified. */
257 if (ap
->a_options
& XATTR_CREATE
) {
261 /* attr doesn't exists and "replace" was specified. */
262 if (ap
->a_options
& XATTR_REPLACE
) {
266 if (uio_resid(uio
) != attrsize
)
269 result
= uiomove((caddr_t
) &VTOC(vp
)->c_finderinfo
, attrsize
, uio
);
271 VTOC(vp
)->c_touch_chgtime
= TRUE
;
272 VTOC(vp
)->c_flag
|= C_MODIFIED
;
273 result
= hfs_update(vp
, FALSE
);
277 /* Write the Resource Fork. */
278 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
279 struct vnode
*rvp
= NULL
;
281 if (!vnode_isreg(vp
)) {
284 if (RESOURCE_FORK_EXISTS(vp
)) {
285 /* attr exists and "create" was specified. */
286 if (ap
->a_options
& XATTR_CREATE
) {
290 /* attr doesn't exists and "replace" was specified. */
291 if (ap
->a_options
& XATTR_REPLACE
) {
295 if ((result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, vfs_context_proc(ap
->a_context
)))) {
298 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
303 * Standard HFS only supports native FinderInfo and Resource Forks.
305 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
308 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
309 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
311 attrsize
= uio_resid(uio
);
312 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
314 * XXX Need to support extent-based attributes XXX
318 /* Calculate size of record rounded up to multiple of 2 bytes. */
319 datasize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
321 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
322 bzero(iterator
, sizeof(*iterator
));
324 MALLOC(datap
, HFSPlusAttrData
*, datasize
, M_TEMP
, M_WAITOK
);
325 btdata
.bufferAddress
= datap
;
326 btdata
.itemSize
= datasize
;
327 btdata
.itemCount
= 1;
328 datap
->recordType
= kHFSPlusAttrInlineData
;
329 datap
->reserved
[0] = 0;
330 datap
->reserved
[1] = 0;
331 datap
->attrSize
= attrsize
;
333 /* Copy in the attribute data. */
334 result
= uiomove((caddr_t
) &datap
->attrData
, attrsize
, uio
);
338 /* Build a b-tree key. */
339 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
343 /* Start a transaction for our changes. */
344 if (hfs_start_transaction(hfsmp
) != 0) {
349 /* once we started the transaction, nobody can compete with us, so make sure this file is still there */
352 if (cp
->c_flag
& C_NOEXISTS
) { /* this file has already been removed */
358 * If there isn't an attributes b-tree then create one.
360 if (hfsmp
->hfs_attribute_vp
== NULL
) {
361 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
362 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
363 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
364 hfs_systemfile_unlock(hfsmp
, lockflags
);
369 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
371 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
373 if (ap
->a_options
& XATTR_REPLACE
) {
374 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
381 /* Insert the attribute. */
382 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
384 if (result
!= btExists
) {
388 // if it exists and XATTR_CREATE was specified,
389 // the spec says to return EEXIST
390 if (ap
->a_options
& XATTR_CREATE
) {
394 /* XXX need to account for old size in c_attrblks */
395 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
398 (void) BTFlushPath(btfile
);
400 hfs_systemfile_unlock(hfsmp
, lockflags
);
405 cp
->c_touch_chgtime
= TRUE
;
406 if ((cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
407 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
408 (void) hfs_update(vp
, 0);
410 HFS_KNOTE(vp
, NOTE_ATTRIB
);
413 /* Finish the transaction of our changes. */
414 hfs_end_transaction(hfsmp
);
417 FREE(iterator
, M_TEMP
);
419 if (result
== btNotFound
)
422 result
= MacToVFSError(result
);
428 * Remove an extended attribute.
432 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
434 struct vnop_removexattr_args {
435 struct vnodeop_desc *a_desc;
439 vfs_context_t a_context;
443 struct vnode
*vp
= ap
->a_vp
;
444 struct hfsmount
*hfsmp
;
445 struct BTreeIterator
* iterator
= NULL
;
446 struct filefork
*btfile
;
447 struct proc
*p
= vfs_context_proc(ap
->a_context
);
448 FSBufferDescriptor btdata
;
449 HFSPlusAttrData attrdata
;
453 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
454 return (EINVAL
); /* invalid name */
457 if (VNODE_IS_RSRC(vp
)) {
461 /* If Resource Fork is non-empty then truncate it. */
462 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
463 struct vnode
*rvp
= NULL
;
465 if ( !vnode_isreg(vp
) ) {
468 if ( !RESOURCE_FORK_EXISTS(vp
) ) {
471 if ((result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, p
))) {
474 hfs_lock_truncate(VTOC(rvp
), TRUE
);
475 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
))) {
476 hfs_unlock_truncate(VTOC(vp
));
480 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, ap
->a_context
);
482 hfs_unlock_truncate(VTOC(rvp
));
483 hfs_unlock(VTOC(rvp
));
488 /* Clear out the Finder Info. */
489 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
490 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
493 bzero(VTOC(vp
)->c_finderinfo
, sizeof(emptyfinfo
));
497 * Standard HFS only supports native FinderInfo and Resource Forks.
499 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
502 if (hfsmp
->hfs_attribute_vp
== NULL
) {
505 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
507 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
508 bzero(iterator
, sizeof(*iterator
));
510 if (hfs_start_transaction(hfsmp
) != 0) {
515 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
519 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
521 btdata
.bufferAddress
= &attrdata
;
522 btdata
.itemSize
= sizeof(attrdata
);
523 btdata
.itemCount
= 1;
524 result
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
);
528 result
= BTDeleteRecord(btfile
, iterator
);
529 (void) BTFlushPath(btfile
);
531 hfs_systemfile_unlock(hfsmp
, lockflags
);
533 VTOC(vp
)->c_touch_chgtime
= TRUE
;
534 HFS_KNOTE(vp
, NOTE_ATTRIB
);
537 if (result
== btNotFound
) {
540 hfs_end_transaction(hfsmp
);
542 FREE(iterator
, M_TEMP
);
544 return MacToVFSError(result
);
549 * Retrieve the list of extended attribute names.
553 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
555 struct vnop_listxattr_args {
556 struct vnodeop_desc *a_desc;
561 vfs_context_t a_context;
564 struct vnode
*vp
= ap
->a_vp
;
565 struct hfsmount
*hfsmp
;
566 uio_t uio
= ap
->a_uio
;
567 struct BTreeIterator
* iterator
= NULL
;
568 struct filefork
*btfile
;
569 struct listattr_callback_state state
;
573 if (VNODE_IS_RSRC(vp
)) {
579 /* If Finder Info is non-empty then export it. */
580 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) != 0) {
582 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
583 } else if (uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
586 result
= uiomove((caddr_t
)XATTR_FINDERINFO_NAME
,
587 sizeof(XATTR_FINDERINFO_NAME
), uio
);
592 /* If Resource Fork is non-empty then export it. */
593 if (vnode_isreg(vp
) && RESOURCE_FORK_EXISTS(vp
)) {
595 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
596 } else if (uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
599 result
= uiomove((caddr_t
)XATTR_RESOURCEFORK_NAME
,
600 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
606 * Standard HFS only supports native FinderInfo and Resource Forks.
607 * Return at this point.
609 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
612 /* Bail if we don't have any extended attributes. */
613 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
614 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
617 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
619 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
620 bzero(iterator
, sizeof(*iterator
));
621 result
= buildkey(VTOC(vp
)->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
625 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
627 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
628 if (result
&& result
!= btNotFound
) {
629 hfs_systemfile_unlock(hfsmp
, lockflags
);
633 state
.fileID
= VTOC(vp
)->c_fileid
;
639 * Process entries starting just after iterator->key.
641 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
642 (IterateCallBackProcPtr
)listattr_callback
, &state
);
643 hfs_systemfile_unlock(hfsmp
, lockflags
);
645 *ap
->a_size
+= state
.size
;
648 FREE(iterator
, M_TEMP
);
650 if (state
.result
|| result
== btNotFound
)
651 result
= state
.result
;
653 return MacToVFSError(result
);
658 * Callback - called for each attribute
661 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
663 char attrname
[XATTR_MAXNAMELEN
+ 1];
667 if (state
->fileID
!= key
->fileID
) {
669 return (0); /* stop */
672 * Skip over non-primary keys
674 if (key
->startBlock
!= 0) {
675 return (1); /* continue */
678 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
679 attrname
, &bytecount
, sizeof(attrname
), 0, 0);
681 state
->result
= result
;
682 return (0); /* stop */
684 bytecount
++; /* account for null termination char */
686 if (xattr_protected(attrname
))
687 return (1); /* continue */
689 if (state
->uio
== NULL
) {
690 state
->size
+= bytecount
;
692 if (bytecount
> uio_resid(state
->uio
)) {
693 state
->result
= ERANGE
;
694 return (0); /* stop */
696 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
698 state
->result
= result
;
699 return (0); /* stop */
702 return (1); /* continue */
707 * Remove all the attributes from a cnode.
709 * A jornal transaction must be already started.
710 * Attributes b-Tree must have exclusive lock held.
714 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
)
716 BTreeIterator
*next_iterator
, *del_iterator
;
717 HFSPlusAttrKey
*next_key
;
718 struct filefork
*btfile
;
719 int result
, iter_result
;
721 if (hfsmp
->hfs_attribute_vp
== NULL
) {
724 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
726 MALLOC(next_iterator
, BTreeIterator
*, sizeof(BTreeIterator
) * 2, M_TEMP
, M_WAITOK
);
727 bzero(next_iterator
, sizeof(BTreeIterator
) * 2);
728 del_iterator
= &next_iterator
[1];
729 next_key
= (HFSPlusAttrKey
*)&next_iterator
->key
;
732 * Go to first possible attribute key/record pair
734 (void) buildkey(fileid
, NULL
, next_key
);
735 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, next_iterator
, NULL
, NULL
);
736 if (result
|| next_key
->fileID
!= fileid
) {
739 /* Remember iterator of attribute to delete */
740 bcopy(next_iterator
, del_iterator
, sizeof(BTreeIterator
));
742 /* Loop until there are no more attributes for this file id */
744 iter_result
= BTIterateRecord(btfile
, kBTreeNextRecord
, next_iterator
, NULL
, NULL
);
746 /* XXX need to free and extents for record types 0x20 and 0x30 */
747 result
= BTDeleteRecord(btfile
, del_iterator
);
752 result
= iter_result
;
755 if (iter_result
|| next_key
->fileID
!= fileid
) {
756 break; /* end of attributes for this file id */
758 bcopy(next_iterator
, del_iterator
, sizeof(BTreeIterator
));
761 (void) BTFlushPath(btfile
);
763 if (result
== btNotFound
) {
766 FREE(next_iterator
, M_TEMP
);
771 * Enable/Disable extended security (ACLs).
775 hfs_setextendedsecurity(struct hfsmount
*hfsmp
, int state
)
777 struct BTreeIterator
* iterator
= NULL
;
778 struct filefork
*btfile
;
782 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
786 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
787 bzero(iterator
, sizeof(*iterator
));
790 * Build a b-tree key.
791 * We use the root's parent id (1) to hold this volume attribute.
793 (void) buildkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
794 (HFSPlusAttrKey
*)&iterator
->key
);
796 /* Start a transaction for our changes. */
797 if (hfs_start_transaction(hfsmp
) != 0) {
802 * If there isn't an attributes b-tree then create one.
804 if (hfsmp
->hfs_attribute_vp
== NULL
) {
805 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
806 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
807 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
808 hfs_systemfile_unlock(hfsmp
, lockflags
);
813 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
815 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
818 /* Remove the attribute. */
819 result
= BTDeleteRecord(btfile
, iterator
);
820 if (result
== btNotFound
)
823 FSBufferDescriptor btdata
;
824 HFSPlusAttrData attrdata
;
827 datasize
= sizeof(attrdata
);
828 btdata
.bufferAddress
= &attrdata
;
829 btdata
.itemSize
= datasize
;
830 btdata
.itemCount
= 1;
831 attrdata
.recordType
= kHFSPlusAttrInlineData
;
832 attrdata
.reserved
[0] = 0;
833 attrdata
.reserved
[1] = 0;
834 attrdata
.attrSize
= 2;
835 attrdata
.attrData
[0] = 0;
836 attrdata
.attrData
[1] = 0;
838 /* Insert the attribute. */
839 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
840 if (result
== btExists
)
843 (void) BTFlushPath(btfile
);
845 hfs_systemfile_unlock(hfsmp
, lockflags
);
847 /* Finish the transaction of our changes. */
848 hfs_end_transaction(hfsmp
);
850 FREE(iterator
, M_TEMP
);
854 vfs_clearextendedsecurity(HFSTOVFS(hfsmp
));
856 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
857 printf("hfs: %s extended security on %s\n",
858 state
== 0 ? "disabling" : "enabling", hfsmp
->vcbVN
);
861 return MacToVFSError(result
);
865 * Check for extended security (ACLs).
869 hfs_checkextendedsecurity(struct hfsmount
*hfsmp
)
871 struct BTreeIterator
* iterator
;
872 struct filefork
*btfile
;
876 if (hfsmp
->hfs_flags
& HFS_STANDARD
||
877 hfsmp
->hfs_attribute_vp
== NULL
) {
881 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
882 bzero(iterator
, sizeof(*iterator
));
885 * Build a b-tree key.
886 * We use the root's parent id (1) to hold this volume attribute.
888 (void) buildkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
889 (HFSPlusAttrKey
*)&iterator
->key
);
891 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
893 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
895 /* Check for our attribute. */
896 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
898 hfs_systemfile_unlock(hfsmp
, lockflags
);
899 FREE(iterator
, M_TEMP
);
902 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
903 printf("hfs mount: enabling extended security on %s\n", hfsmp
->vcbVN
);
909 * hfs_attrkeycompare - compare two attribute b-tree keys.
911 * The name portion of the key is compared using a 16-bit binary comparison.
912 * This is called from the b-tree code.
916 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
918 u_int32_t searchFileID
, trialFileID
;
921 searchFileID
= searchKey
->fileID
;
922 trialFileID
= trialKey
->fileID
;
925 if (searchFileID
> trialFileID
) {
927 } else if (searchFileID
< trialFileID
) {
930 u_int16_t
* str1
= &searchKey
->attrName
[0];
931 u_int16_t
* str2
= &trialKey
->attrName
[0];
932 int length1
= searchKey
->attrNameLen
;
933 int length2
= trialKey
->attrNameLen
;
937 if (length1
< length2
) {
940 } else if (length1
> length2
) {
963 * Names are equal; compare startBlock
965 if (searchKey
->startBlock
== trialKey
->startBlock
)
968 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
976 * buildkey - build an Attribute b-tree key
979 buildkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
982 size_t unicodeBytes
= 0;
984 if (attrname
!= NULL
) {
986 * Convert filename from UTF-8 into Unicode
988 result
= utf8_decodestr(attrname
, strlen(attrname
), key
->attrName
,
989 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
991 if (result
!= ENAMETOOLONG
)
992 result
= EINVAL
; /* name has invalid characters */
995 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
996 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
998 key
->attrNameLen
= 0;
999 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
1002 key
->fileID
= fileID
;
1003 key
->startBlock
= 0;
1009 * getnodecount - calculate starting node count for attributes b-tree.
1012 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
1018 avedatasize
= sizeof(u_int16_t
); /* index slot */
1019 avedatasize
+= kHFSPlusAttrKeyMinimumLength
+ HFS_AVERAGE_NAME_SIZE
* sizeof(u_int16_t
);
1020 avedatasize
+= sizeof(HFSPlusAttrData
) + 32;
1022 recpernode
= (nodesize
- sizeof(BTNodeDescriptor
)) / avedatasize
;
1024 count
= (hfsmp
->hfs_filecount
+ hfsmp
->hfs_dircount
) / 8;
1025 count
/= recpernode
;
1027 /* XXX should also consider volume size XXX */
1029 return (MAX(count
, (int)(1024 * 1024) / (int)nodesize
));
1034 * getmaxinlineattrsize - calculate maximum inline attribute size.
1036 * This yields 3,802 bytes for an 8K node size.
1039 getmaxinlineattrsize(struct vnode
* attrvp
)
1041 struct BTreeInfoRec btinfo
;
1042 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
1045 if (attrvp
!= NULL
) {
1046 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
);
1047 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
1048 nodesize
= btinfo
.nodeSize
;
1049 hfs_unlock(VTOC(attrvp
));
1052 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
1053 maxsize
-= 3 * sizeof(UInt16
); /* minus 3 index slots */
1054 maxsize
/= 2; /* 2 key/rec pairs minumum */
1055 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
1056 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
1057 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */