2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/utfconv.h>
36 #include <sys/vnode.h>
37 #include <sys/xattr.h>
40 #include "hfs_cnode.h"
41 #include "hfs_mount.h"
42 #include "hfs_format.h"
43 #include "hfs_endian.h"
45 #include "hfscommon/headers/BTreesInternal.h"
48 #define ATTRIBUTE_FILE_NODE_SIZE 8192
51 /* State information for the listattr_callback callback function. */
52 struct listattr_callback_state
{
59 #define HFS_MAXATTRIBUTESIZE (1024*1024)
61 /* HFS Internal Names */
62 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
65 #define RESOURCE_FORK_EXISTS(VP) \
66 ((VTOC((VP))->c_blocks - VTOF((VP))->ff_blocks) > 0)
68 static u_int32_t emptyfinfo
[8] = {0};
71 extern int hfs_create_attr_btree(struct hfsmount
*hfsmp
, uint32_t nodesize
, uint32_t nodecnt
);
74 int hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
);
75 int hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
);
76 int hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
);
77 int hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
);
78 int hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
);
82 static int listattr_callback(const HFSPlusAttrKey
*key
, const HFSPlusAttrData
*data
,
83 struct listattr_callback_state
*state
);
85 static int buildkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
);
87 static int getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
);
89 static size_t getmaxinlineattrsize(struct vnode
* attrvp
);
92 * Retrieve the data of an extended attribute.
96 hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
)
98 struct vnop_getxattr_args {
99 struct vnodeop_desc *a_desc;
105 vfs_context_t a_context;
109 struct vnode
*vp
= ap
->a_vp
;
110 struct hfsmount
*hfsmp
;
111 uio_t uio
= ap
->a_uio
;
112 struct BTreeIterator
* iterator
= NULL
;
113 struct filefork
*btfile
;
114 FSBufferDescriptor btdata
;
115 HFSPlusAttrData
* datap
= NULL
;
121 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
122 return (EINVAL
); /* invalid name */
126 if (!VNODE_IS_RSRC(vp
)) {
127 /* Get the Finder Info. */
128 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
131 /* If Finder Info is empty then it doesn't exist. */
132 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
136 *ap
->a_size
= bufsize
;
139 if (uio_resid(uio
) < bufsize
)
142 result
= uiomove((caddr_t
) &VTOC(vp
)->c_finderinfo
, bufsize
, uio
);
146 /* Read the Resource Fork. */
147 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
148 struct vnode
*rvp
= NULL
;
150 if ( !vnode_isreg(vp
) ) {
153 if ( !RESOURCE_FORK_EXISTS(vp
)) {
156 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
159 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, vfs_context_proc(ap
->a_context
));
160 hfs_unlock(VTOC(vp
));
165 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
167 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
174 * Standard HFS only supports native FinderInfo and Resource Forks.
176 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
179 /* Bail if we don't have any extended attributes. */
180 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
181 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
184 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
186 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
187 bzero(iterator
, sizeof(*iterator
));
189 bufsize
= sizeof(HFSPlusAttrData
) - 2;
191 bufsize
+= uio_resid(uio
);
192 MALLOC(datap
, HFSPlusAttrData
*, bufsize
, M_TEMP
, M_WAITOK
);
193 btdata
.bufferAddress
= datap
;
194 btdata
.itemSize
= bufsize
;
195 btdata
.itemCount
= 1;
197 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
201 /* Lookup the attribute. */
202 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
203 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
204 hfs_systemfile_unlock(hfsmp
, lockflags
);
207 if (result
== btNotFound
)
212 *ap
->a_size
= datap
->attrSize
;
214 /* Copy out the attribute data. */
216 if (datap
->attrSize
> uio_resid(uio
))
219 result
= uiomove((caddr_t
) &datap
->attrData
, datap
->attrSize
, uio
);
223 FREE(iterator
, M_TEMP
);
225 return MacToVFSError(result
);
229 * Set the data of an extended attribute.
233 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
235 struct vnop_setxattr_args {
236 struct vnodeop_desc *a_desc;
241 vfs_context_t a_context;
245 struct vnode
*vp
= ap
->a_vp
;
246 struct hfsmount
*hfsmp
;
247 uio_t uio
= ap
->a_uio
;
248 struct BTreeIterator
* iterator
= NULL
;
249 struct filefork
*btfile
;
251 FSBufferDescriptor btdata
;
252 HFSPlusAttrData
* datap
= NULL
;
257 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
258 return (EINVAL
); /* invalid name */
261 if (VNODE_IS_RSRC(vp
)) {
264 /* Set the Finder Info. */
265 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
268 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
))) {
269 /* attr exists and "create" was specified. */
270 if (ap
->a_options
& XATTR_CREATE
) {
274 /* attr doesn't exists and "replace" was specified. */
275 if (ap
->a_options
& XATTR_REPLACE
) {
279 if (uio_resid(uio
) != attrsize
)
282 result
= uiomove((caddr_t
) &VTOC(vp
)->c_finderinfo
, attrsize
, uio
);
284 VTOC(vp
)->c_touch_chgtime
= TRUE
;
285 VTOC(vp
)->c_flag
|= C_MODIFIED
;
286 result
= hfs_update(vp
, FALSE
);
290 /* Write the Resource Fork. */
291 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
292 struct vnode
*rvp
= NULL
;
294 if (!vnode_isreg(vp
)) {
297 if (RESOURCE_FORK_EXISTS(vp
)) {
298 /* attr exists and "create" was specified. */
299 if (ap
->a_options
& XATTR_CREATE
) {
303 /* attr doesn't exists and "replace" was specified. */
304 if (ap
->a_options
& XATTR_REPLACE
) {
308 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
311 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, vfs_context_proc(ap
->a_context
));
312 hfs_unlock(VTOC(vp
));
316 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
321 * Standard HFS only supports native FinderInfo and Resource Forks.
323 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
326 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
327 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
329 attrsize
= uio_resid(uio
);
330 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
332 * XXX Need to support extent-based attributes XXX
336 /* Calculate size of record rounded up to multiple of 2 bytes. */
337 datasize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
339 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
340 bzero(iterator
, sizeof(*iterator
));
342 MALLOC(datap
, HFSPlusAttrData
*, datasize
, M_TEMP
, M_WAITOK
);
343 btdata
.bufferAddress
= datap
;
344 btdata
.itemSize
= datasize
;
345 btdata
.itemCount
= 1;
346 datap
->recordType
= kHFSPlusAttrInlineData
;
347 datap
->reserved
[0] = 0;
348 datap
->reserved
[1] = 0;
349 datap
->attrSize
= attrsize
;
351 /* Copy in the attribute data. */
352 result
= uiomove((caddr_t
) &datap
->attrData
, attrsize
, uio
);
356 /* Build a b-tree key. */
357 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
361 /* Start a transaction for our changes. */
362 if (hfs_start_transaction(hfsmp
) != 0) {
367 /* once we started the transaction, nobody can compete with us, so make sure this file is still there */
370 if (cp
->c_flag
& C_NOEXISTS
) { /* this file has already been removed */
376 * If there isn't an attributes b-tree then create one.
378 if (hfsmp
->hfs_attribute_vp
== NULL
) {
379 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
380 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
381 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
382 hfs_systemfile_unlock(hfsmp
, lockflags
);
387 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
389 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
391 if (ap
->a_options
& XATTR_REPLACE
) {
392 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
399 /* Insert the attribute. */
400 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
402 if (result
!= btExists
) {
406 // if it exists and XATTR_CREATE was specified,
407 // the spec says to return EEXIST
408 if (ap
->a_options
& XATTR_CREATE
) {
412 /* XXX need to account for old size in c_attrblks */
413 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
416 (void) BTFlushPath(btfile
);
418 hfs_systemfile_unlock(hfsmp
, lockflags
);
423 cp
->c_touch_chgtime
= TRUE
;
424 if ((cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
425 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
426 (void) hfs_update(vp
, 0);
428 HFS_KNOTE(vp
, NOTE_ATTRIB
);
431 /* Finish the transaction of our changes. */
432 hfs_end_transaction(hfsmp
);
435 FREE(iterator
, M_TEMP
);
437 if (result
== btNotFound
)
440 result
= MacToVFSError(result
);
446 * Remove an extended attribute.
450 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
452 struct vnop_removexattr_args {
453 struct vnodeop_desc *a_desc;
457 vfs_context_t a_context;
461 struct vnode
*vp
= ap
->a_vp
;
462 struct hfsmount
*hfsmp
;
463 struct BTreeIterator
* iterator
= NULL
;
464 struct filefork
*btfile
;
465 struct proc
*p
= vfs_context_proc(ap
->a_context
);
466 FSBufferDescriptor btdata
;
467 HFSPlusAttrData attrdata
;
471 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
472 return (EINVAL
); /* invalid name */
475 if (VNODE_IS_RSRC(vp
)) {
479 /* If Resource Fork is non-empty then truncate it. */
480 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
481 struct vnode
*rvp
= NULL
;
483 if ( !vnode_isreg(vp
) ) {
486 if ( !RESOURCE_FORK_EXISTS(vp
) ) {
489 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
492 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, p
);
493 hfs_unlock(VTOC(vp
));
497 hfs_lock_truncate(VTOC(rvp
), TRUE
);
498 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
))) {
499 hfs_unlock_truncate(VTOC(vp
));
503 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, ap
->a_context
);
505 hfs_unlock_truncate(VTOC(rvp
));
506 hfs_unlock(VTOC(rvp
));
511 /* Clear out the Finder Info. */
512 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
513 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
516 bzero(VTOC(vp
)->c_finderinfo
, sizeof(emptyfinfo
));
520 * Standard HFS only supports native FinderInfo and Resource Forks.
522 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
525 if (hfsmp
->hfs_attribute_vp
== NULL
) {
528 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
530 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
531 bzero(iterator
, sizeof(*iterator
));
533 if (hfs_start_transaction(hfsmp
) != 0) {
538 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
542 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
544 btdata
.bufferAddress
= &attrdata
;
545 btdata
.itemSize
= sizeof(attrdata
);
546 btdata
.itemCount
= 1;
547 result
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
);
551 result
= BTDeleteRecord(btfile
, iterator
);
552 (void) BTFlushPath(btfile
);
554 hfs_systemfile_unlock(hfsmp
, lockflags
);
556 VTOC(vp
)->c_touch_chgtime
= TRUE
;
557 HFS_KNOTE(vp
, NOTE_ATTRIB
);
560 if (result
== btNotFound
) {
563 hfs_end_transaction(hfsmp
);
565 FREE(iterator
, M_TEMP
);
567 return MacToVFSError(result
);
572 * Retrieve the list of extended attribute names.
576 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
578 struct vnop_listxattr_args {
579 struct vnodeop_desc *a_desc;
584 vfs_context_t a_context;
587 struct vnode
*vp
= ap
->a_vp
;
588 struct hfsmount
*hfsmp
;
589 uio_t uio
= ap
->a_uio
;
590 struct BTreeIterator
* iterator
= NULL
;
591 struct filefork
*btfile
;
592 struct listattr_callback_state state
;
596 if (VNODE_IS_RSRC(vp
)) {
602 /* If Finder Info is non-empty then export it. */
603 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) != 0) {
605 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
606 } else if (uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
609 result
= uiomove((caddr_t
)XATTR_FINDERINFO_NAME
,
610 sizeof(XATTR_FINDERINFO_NAME
), uio
);
615 /* If Resource Fork is non-empty then export it. */
616 if (vnode_isreg(vp
) && RESOURCE_FORK_EXISTS(vp
)) {
618 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
619 } else if (uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
622 result
= uiomove((caddr_t
)XATTR_RESOURCEFORK_NAME
,
623 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
629 * Standard HFS only supports native FinderInfo and Resource Forks.
630 * Return at this point.
632 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
635 /* Bail if we don't have any extended attributes. */
636 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
637 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
640 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
642 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
643 bzero(iterator
, sizeof(*iterator
));
644 result
= buildkey(VTOC(vp
)->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
648 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
650 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
651 if (result
&& result
!= btNotFound
) {
652 hfs_systemfile_unlock(hfsmp
, lockflags
);
656 state
.fileID
= VTOC(vp
)->c_fileid
;
662 * Process entries starting just after iterator->key.
664 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
665 (IterateCallBackProcPtr
)listattr_callback
, &state
);
666 hfs_systemfile_unlock(hfsmp
, lockflags
);
668 *ap
->a_size
+= state
.size
;
671 FREE(iterator
, M_TEMP
);
673 if (state
.result
|| result
== btNotFound
)
674 result
= state
.result
;
676 return MacToVFSError(result
);
681 * Callback - called for each attribute
684 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
686 char attrname
[XATTR_MAXNAMELEN
+ 1];
690 if (state
->fileID
!= key
->fileID
) {
692 return (0); /* stop */
695 * Skip over non-primary keys
697 if (key
->startBlock
!= 0) {
698 return (1); /* continue */
701 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
702 attrname
, &bytecount
, sizeof(attrname
), 0, 0);
704 state
->result
= result
;
705 return (0); /* stop */
707 bytecount
++; /* account for null termination char */
709 if (xattr_protected(attrname
))
710 return (1); /* continue */
712 if (state
->uio
== NULL
) {
713 state
->size
+= bytecount
;
715 if (bytecount
> uio_resid(state
->uio
)) {
716 state
->result
= ERANGE
;
717 return (0); /* stop */
719 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
721 state
->result
= result
;
722 return (0); /* stop */
725 return (1); /* continue */
730 * Remove all the attributes from a cnode.
732 * A jornal transaction must be already started.
733 * Attributes b-Tree must have exclusive lock held.
737 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
)
739 BTreeIterator
*next_iterator
, *del_iterator
;
740 HFSPlusAttrKey
*next_key
;
741 struct filefork
*btfile
;
742 int result
, iter_result
;
744 if (hfsmp
->hfs_attribute_vp
== NULL
) {
747 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
749 MALLOC(next_iterator
, BTreeIterator
*, sizeof(BTreeIterator
) * 2, M_TEMP
, M_WAITOK
);
750 bzero(next_iterator
, sizeof(BTreeIterator
) * 2);
751 del_iterator
= &next_iterator
[1];
752 next_key
= (HFSPlusAttrKey
*)&next_iterator
->key
;
755 * Go to first possible attribute key/record pair
757 (void) buildkey(fileid
, NULL
, next_key
);
758 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, next_iterator
, NULL
, NULL
);
759 if (result
|| next_key
->fileID
!= fileid
) {
762 /* Remember iterator of attribute to delete */
763 bcopy(next_iterator
, del_iterator
, sizeof(BTreeIterator
));
765 /* Loop until there are no more attributes for this file id */
767 iter_result
= BTIterateRecord(btfile
, kBTreeNextRecord
, next_iterator
, NULL
, NULL
);
769 /* XXX need to free and extents for record types 0x20 and 0x30 */
770 result
= BTDeleteRecord(btfile
, del_iterator
);
775 result
= iter_result
;
778 if (iter_result
|| next_key
->fileID
!= fileid
) {
779 break; /* end of attributes for this file id */
781 bcopy(next_iterator
, del_iterator
, sizeof(BTreeIterator
));
784 (void) BTFlushPath(btfile
);
786 if (result
== btNotFound
) {
789 FREE(next_iterator
, M_TEMP
);
794 * Enable/Disable extended security (ACLs).
798 hfs_setextendedsecurity(struct hfsmount
*hfsmp
, int state
)
800 struct BTreeIterator
* iterator
= NULL
;
801 struct filefork
*btfile
;
805 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
809 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
810 bzero(iterator
, sizeof(*iterator
));
813 * Build a b-tree key.
814 * We use the root's parent id (1) to hold this volume attribute.
816 (void) buildkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
817 (HFSPlusAttrKey
*)&iterator
->key
);
819 /* Start a transaction for our changes. */
820 if (hfs_start_transaction(hfsmp
) != 0) {
825 * If there isn't an attributes b-tree then create one.
827 if (hfsmp
->hfs_attribute_vp
== NULL
) {
828 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
829 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
830 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
831 hfs_systemfile_unlock(hfsmp
, lockflags
);
836 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
838 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
841 /* Remove the attribute. */
842 result
= BTDeleteRecord(btfile
, iterator
);
843 if (result
== btNotFound
)
846 FSBufferDescriptor btdata
;
847 HFSPlusAttrData attrdata
;
850 datasize
= sizeof(attrdata
);
851 btdata
.bufferAddress
= &attrdata
;
852 btdata
.itemSize
= datasize
;
853 btdata
.itemCount
= 1;
854 attrdata
.recordType
= kHFSPlusAttrInlineData
;
855 attrdata
.reserved
[0] = 0;
856 attrdata
.reserved
[1] = 0;
857 attrdata
.attrSize
= 2;
858 attrdata
.attrData
[0] = 0;
859 attrdata
.attrData
[1] = 0;
861 /* Insert the attribute. */
862 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
863 if (result
== btExists
)
866 (void) BTFlushPath(btfile
);
868 hfs_systemfile_unlock(hfsmp
, lockflags
);
870 /* Finish the transaction of our changes. */
871 hfs_end_transaction(hfsmp
);
873 FREE(iterator
, M_TEMP
);
877 vfs_clearextendedsecurity(HFSTOVFS(hfsmp
));
879 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
880 printf("hfs: %s extended security on %s\n",
881 state
== 0 ? "disabling" : "enabling", hfsmp
->vcbVN
);
884 return MacToVFSError(result
);
888 * Check for extended security (ACLs).
892 hfs_checkextendedsecurity(struct hfsmount
*hfsmp
)
894 struct BTreeIterator
* iterator
;
895 struct filefork
*btfile
;
899 if (hfsmp
->hfs_flags
& HFS_STANDARD
||
900 hfsmp
->hfs_attribute_vp
== NULL
) {
904 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
905 bzero(iterator
, sizeof(*iterator
));
908 * Build a b-tree key.
909 * We use the root's parent id (1) to hold this volume attribute.
911 (void) buildkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
912 (HFSPlusAttrKey
*)&iterator
->key
);
914 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
916 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
918 /* Check for our attribute. */
919 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
921 hfs_systemfile_unlock(hfsmp
, lockflags
);
922 FREE(iterator
, M_TEMP
);
925 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
926 printf("hfs mount: enabling extended security on %s\n", hfsmp
->vcbVN
);
932 * hfs_attrkeycompare - compare two attribute b-tree keys.
934 * The name portion of the key is compared using a 16-bit binary comparison.
935 * This is called from the b-tree code.
939 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
941 u_int32_t searchFileID
, trialFileID
;
944 searchFileID
= searchKey
->fileID
;
945 trialFileID
= trialKey
->fileID
;
948 if (searchFileID
> trialFileID
) {
950 } else if (searchFileID
< trialFileID
) {
953 u_int16_t
* str1
= &searchKey
->attrName
[0];
954 u_int16_t
* str2
= &trialKey
->attrName
[0];
955 int length1
= searchKey
->attrNameLen
;
956 int length2
= trialKey
->attrNameLen
;
960 if (length1
< length2
) {
963 } else if (length1
> length2
) {
986 * Names are equal; compare startBlock
988 if (searchKey
->startBlock
== trialKey
->startBlock
)
991 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
999 * buildkey - build an Attribute b-tree key
1002 buildkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
1005 size_t unicodeBytes
= 0;
1007 if (attrname
!= NULL
) {
1009 * Convert filename from UTF-8 into Unicode
1011 result
= utf8_decodestr(attrname
, strlen(attrname
), key
->attrName
,
1012 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
1014 if (result
!= ENAMETOOLONG
)
1015 result
= EINVAL
; /* name has invalid characters */
1018 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
1019 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
1021 key
->attrNameLen
= 0;
1022 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
1025 key
->fileID
= fileID
;
1026 key
->startBlock
= 0;
1032 * getnodecount - calculate starting node count for attributes b-tree.
1035 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
1041 avedatasize
= sizeof(u_int16_t
); /* index slot */
1042 avedatasize
+= kHFSPlusAttrKeyMinimumLength
+ HFS_AVERAGE_NAME_SIZE
* sizeof(u_int16_t
);
1043 avedatasize
+= sizeof(HFSPlusAttrData
) + 32;
1045 recpernode
= (nodesize
- sizeof(BTNodeDescriptor
)) / avedatasize
;
1047 count
= (hfsmp
->hfs_filecount
+ hfsmp
->hfs_dircount
) / 8;
1048 count
/= recpernode
;
1050 /* XXX should also consider volume size XXX */
1052 return (MAX(count
, (int)(1024 * 1024) / (int)nodesize
));
1057 * getmaxinlineattrsize - calculate maximum inline attribute size.
1059 * This yields 3,802 bytes for an 8K node size.
1062 getmaxinlineattrsize(struct vnode
* attrvp
)
1064 struct BTreeInfoRec btinfo
;
1065 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
1068 if (attrvp
!= NULL
) {
1069 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
);
1070 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
1071 nodesize
= btinfo
.nodeSize
;
1072 hfs_unlock(VTOC(attrvp
));
1075 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
1076 maxsize
-= 3 * sizeof(UInt16
); /* minus 3 index slots */
1077 maxsize
/= 2; /* 2 key/rec pairs minumum */
1078 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
1079 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
1080 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */