2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/kernel.h>
27 #include <sys/malloc.h>
28 #include <sys/utfconv.h>
29 #include <sys/vnode.h>
30 #include <sys/xattr.h>
33 #include "hfs_cnode.h"
34 #include "hfs_mount.h"
35 #include "hfs_format.h"
36 #include "hfs_endian.h"
38 #include "hfscommon/headers/BTreesInternal.h"
41 #define ATTRIBUTE_FILE_NODE_SIZE 8192
44 /* State information for the listattr_callback callback function. */
45 struct listattr_callback_state
{
52 #define HFS_MAXATTRIBUTESIZE (1024*1024)
54 /* HFS Internal Names */
55 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
58 #define RESOURCE_FORK_EXISTS(VP) \
59 ((VTOC((VP))->c_blocks - VTOF((VP))->ff_blocks) > 0)
61 static u_int32_t emptyfinfo
[8] = {0};
64 extern int hfs_create_attr_btree(struct hfsmount
*hfsmp
, uint32_t nodesize
, uint32_t nodecnt
);
67 int hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
);
68 int hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
);
69 int hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
);
70 int hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
);
71 int hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
);
75 static int listattr_callback(const HFSPlusAttrKey
*key
, const HFSPlusAttrData
*data
,
76 struct listattr_callback_state
*state
);
78 static int buildkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
);
80 static int getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
);
82 static size_t getmaxinlineattrsize(struct vnode
* attrvp
);
85 * Retrieve the data of an extended attribute.
89 hfs_vnop_getxattr(struct vnop_getxattr_args
*ap
)
91 struct vnop_getxattr_args {
92 struct vnodeop_desc *a_desc;
98 vfs_context_t a_context;
102 struct vnode
*vp
= ap
->a_vp
;
103 struct hfsmount
*hfsmp
;
104 uio_t uio
= ap
->a_uio
;
105 struct BTreeIterator
* iterator
= NULL
;
106 struct filefork
*btfile
;
107 FSBufferDescriptor btdata
;
108 HFSPlusAttrData
* datap
= NULL
;
114 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
115 return (EINVAL
); /* invalid name */
119 if (!VNODE_IS_RSRC(vp
)) {
120 /* Get the Finder Info. */
121 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
124 /* If Finder Info is empty then it doesn't exist. */
125 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
129 *ap
->a_size
= bufsize
;
132 if (uio_resid(uio
) < bufsize
)
135 result
= uiomove((caddr_t
) &VTOC(vp
)->c_finderinfo
, bufsize
, uio
);
139 /* Read the Resource Fork. */
140 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
141 struct vnode
*rvp
= NULL
;
143 if ( !vnode_isreg(vp
) ) {
146 if ( !RESOURCE_FORK_EXISTS(vp
)) {
149 if ((result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, vfs_context_proc(ap
->a_context
)))) {
153 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
155 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
162 * Standard HFS only supports native FinderInfo and Resource Forks.
164 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
167 /* Bail if we don't have any extended attributes. */
168 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
169 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
172 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
174 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
175 bzero(iterator
, sizeof(*iterator
));
177 bufsize
= sizeof(HFSPlusAttrData
) - 2;
179 bufsize
+= uio_resid(uio
);
180 MALLOC(datap
, HFSPlusAttrData
*, bufsize
, M_TEMP
, M_WAITOK
);
181 btdata
.bufferAddress
= datap
;
182 btdata
.itemSize
= bufsize
;
183 btdata
.itemCount
= 1;
185 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
189 /* Lookup the attribute. */
190 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
191 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
192 hfs_systemfile_unlock(hfsmp
, lockflags
);
195 if (result
== btNotFound
)
200 *ap
->a_size
= datap
->attrSize
;
202 /* Copy out the attribute data. */
204 if (datap
->attrSize
> uio_resid(uio
))
207 result
= uiomove((caddr_t
) &datap
->attrData
, datap
->attrSize
, uio
);
211 FREE(iterator
, M_TEMP
);
213 return MacToVFSError(result
);
217 * Set the data of an extended attribute.
221 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
223 struct vnop_setxattr_args {
224 struct vnodeop_desc *a_desc;
229 vfs_context_t a_context;
233 struct vnode
*vp
= ap
->a_vp
;
234 struct hfsmount
*hfsmp
;
235 uio_t uio
= ap
->a_uio
;
236 struct BTreeIterator
* iterator
= NULL
;
237 struct filefork
*btfile
;
239 FSBufferDescriptor btdata
;
240 HFSPlusAttrData
* datap
= NULL
;
245 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
246 return (EINVAL
); /* invalid name */
249 if (VNODE_IS_RSRC(vp
)) {
252 /* Set the Finder Info. */
253 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
256 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
))) {
257 /* attr exists and "create" was specified. */
258 if (ap
->a_options
& XATTR_CREATE
) {
262 /* attr doesn't exists and "replace" was specified. */
263 if (ap
->a_options
& XATTR_REPLACE
) {
267 if (uio_resid(uio
) != attrsize
)
270 result
= uiomove((caddr_t
) &VTOC(vp
)->c_finderinfo
, attrsize
, uio
);
272 VTOC(vp
)->c_touch_chgtime
= TRUE
;
273 VTOC(vp
)->c_flag
|= C_MODIFIED
;
274 result
= hfs_update(vp
, FALSE
);
278 /* Write the Resource Fork. */
279 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
280 struct vnode
*rvp
= NULL
;
282 if (!vnode_isreg(vp
)) {
285 if (RESOURCE_FORK_EXISTS(vp
)) {
286 /* attr exists and "create" was specified. */
287 if (ap
->a_options
& XATTR_CREATE
) {
291 /* attr doesn't exists and "replace" was specified. */
292 if (ap
->a_options
& XATTR_REPLACE
) {
296 if ((result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, vfs_context_proc(ap
->a_context
)))) {
299 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
304 * Standard HFS only supports native FinderInfo and Resource Forks.
306 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
309 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
310 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
312 attrsize
= uio_resid(uio
);
313 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
315 * XXX Need to support extent-based attributes XXX
319 /* Calculate size of record rounded up to multiple of 2 bytes. */
320 datasize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
322 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
323 bzero(iterator
, sizeof(*iterator
));
325 MALLOC(datap
, HFSPlusAttrData
*, datasize
, M_TEMP
, M_WAITOK
);
326 btdata
.bufferAddress
= datap
;
327 btdata
.itemSize
= datasize
;
328 btdata
.itemCount
= 1;
329 datap
->recordType
= kHFSPlusAttrInlineData
;
330 datap
->reserved
[0] = 0;
331 datap
->reserved
[1] = 0;
332 datap
->attrSize
= attrsize
;
334 /* Copy in the attribute data. */
335 result
= uiomove((caddr_t
) &datap
->attrData
, attrsize
, uio
);
339 /* Build a b-tree key. */
340 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
344 /* Start a transaction for our changes. */
345 if (hfs_start_transaction(hfsmp
) != 0) {
350 /* once we started the transaction, nobody can compete with us, so make sure this file is still there */
353 if (cp
->c_flag
& C_NOEXISTS
) { /* this file has already been removed */
359 * If there isn't an attributes b-tree then create one.
361 if (hfsmp
->hfs_attribute_vp
== NULL
) {
362 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
363 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
364 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
365 hfs_systemfile_unlock(hfsmp
, lockflags
);
370 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
372 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
374 if (ap
->a_options
& XATTR_REPLACE
) {
375 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
382 /* Insert the attribute. */
383 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
385 if (result
!= btExists
) {
389 // if it exists and XATTR_CREATE was specified,
390 // the spec says to return EEXIST
391 if (ap
->a_options
& XATTR_CREATE
) {
395 /* XXX need to account for old size in c_attrblks */
396 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
399 (void) BTFlushPath(btfile
);
401 hfs_systemfile_unlock(hfsmp
, lockflags
);
406 cp
->c_touch_chgtime
= TRUE
;
407 if ((cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
408 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
409 (void) hfs_update(vp
, 0);
411 HFS_KNOTE(vp
, NOTE_ATTRIB
);
414 /* Finish the transaction of our changes. */
415 hfs_end_transaction(hfsmp
);
418 FREE(iterator
, M_TEMP
);
420 if (result
== btNotFound
)
423 result
= MacToVFSError(result
);
429 * Remove an extended attribute.
433 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
435 struct vnop_removexattr_args {
436 struct vnodeop_desc *a_desc;
440 vfs_context_t a_context;
444 struct vnode
*vp
= ap
->a_vp
;
445 struct hfsmount
*hfsmp
;
446 struct BTreeIterator
* iterator
= NULL
;
447 struct filefork
*btfile
;
448 struct proc
*p
= vfs_context_proc(ap
->a_context
);
449 FSBufferDescriptor btdata
;
450 HFSPlusAttrData attrdata
;
454 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
455 return (EINVAL
); /* invalid name */
458 if (VNODE_IS_RSRC(vp
)) {
462 /* If Resource Fork is non-empty then truncate it. */
463 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
464 struct vnode
*rvp
= NULL
;
466 if ( !vnode_isreg(vp
) ) {
469 if ( !RESOURCE_FORK_EXISTS(vp
) ) {
472 if ((result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, p
))) {
475 hfs_lock_truncate(VTOC(rvp
), TRUE
);
476 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
))) {
477 hfs_unlock_truncate(VTOC(vp
));
481 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, ap
->a_context
);
483 hfs_unlock_truncate(VTOC(rvp
));
484 hfs_unlock(VTOC(rvp
));
489 /* Clear out the Finder Info. */
490 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
491 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
494 bzero(VTOC(vp
)->c_finderinfo
, sizeof(emptyfinfo
));
498 * Standard HFS only supports native FinderInfo and Resource Forks.
500 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
503 if (hfsmp
->hfs_attribute_vp
== NULL
) {
506 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
508 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
509 bzero(iterator
, sizeof(*iterator
));
511 if (hfs_start_transaction(hfsmp
) != 0) {
516 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
520 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
522 btdata
.bufferAddress
= &attrdata
;
523 btdata
.itemSize
= sizeof(attrdata
);
524 btdata
.itemCount
= 1;
525 result
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
);
529 result
= BTDeleteRecord(btfile
, iterator
);
530 (void) BTFlushPath(btfile
);
532 hfs_systemfile_unlock(hfsmp
, lockflags
);
534 VTOC(vp
)->c_touch_chgtime
= TRUE
;
535 HFS_KNOTE(vp
, NOTE_ATTRIB
);
538 if (result
== btNotFound
) {
541 hfs_end_transaction(hfsmp
);
543 FREE(iterator
, M_TEMP
);
545 return MacToVFSError(result
);
550 * Retrieve the list of extended attribute names.
554 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
556 struct vnop_listxattr_args {
557 struct vnodeop_desc *a_desc;
562 vfs_context_t a_context;
565 struct vnode
*vp
= ap
->a_vp
;
566 struct hfsmount
*hfsmp
;
567 uio_t uio
= ap
->a_uio
;
568 struct BTreeIterator
* iterator
= NULL
;
569 struct filefork
*btfile
;
570 struct listattr_callback_state state
;
574 if (VNODE_IS_RSRC(vp
)) {
580 /* If Finder Info is non-empty then export it. */
581 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) != 0) {
583 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
584 } else if (uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
587 result
= uiomove((caddr_t
)XATTR_FINDERINFO_NAME
,
588 sizeof(XATTR_FINDERINFO_NAME
), uio
);
593 /* If Resource Fork is non-empty then export it. */
594 if (vnode_isreg(vp
) && RESOURCE_FORK_EXISTS(vp
)) {
596 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
597 } else if (uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
600 result
= uiomove((caddr_t
)XATTR_RESOURCEFORK_NAME
,
601 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
607 * Standard HFS only supports native FinderInfo and Resource Forks.
608 * Return at this point.
610 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
613 /* Bail if we don't have any extended attributes. */
614 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
615 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
618 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
620 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
621 bzero(iterator
, sizeof(*iterator
));
622 result
= buildkey(VTOC(vp
)->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
626 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
628 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
629 if (result
&& result
!= btNotFound
) {
630 hfs_systemfile_unlock(hfsmp
, lockflags
);
634 state
.fileID
= VTOC(vp
)->c_fileid
;
640 * Process entries starting just after iterator->key.
642 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
643 (IterateCallBackProcPtr
)listattr_callback
, &state
);
644 hfs_systemfile_unlock(hfsmp
, lockflags
);
646 *ap
->a_size
+= state
.size
;
649 FREE(iterator
, M_TEMP
);
651 if (state
.result
|| result
== btNotFound
)
652 result
= state
.result
;
654 return MacToVFSError(result
);
659 * Callback - called for each attribute
662 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
664 char attrname
[XATTR_MAXNAMELEN
+ 1];
668 if (state
->fileID
!= key
->fileID
) {
670 return (0); /* stop */
673 * Skip over non-primary keys
675 if (key
->startBlock
!= 0) {
676 return (1); /* continue */
679 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
680 attrname
, &bytecount
, sizeof(attrname
), 0, 0);
682 state
->result
= result
;
683 return (0); /* stop */
685 bytecount
++; /* account for null termination char */
687 if (xattr_protected(attrname
))
688 return (1); /* continue */
690 if (state
->uio
== NULL
) {
691 state
->size
+= bytecount
;
693 if (bytecount
> uio_resid(state
->uio
)) {
694 state
->result
= ERANGE
;
695 return (0); /* stop */
697 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
699 state
->result
= result
;
700 return (0); /* stop */
703 return (1); /* continue */
708 * Remove all the attributes from a cnode.
710 * A jornal transaction must be already started.
711 * Attributes b-Tree must have exclusive lock held.
715 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
)
717 BTreeIterator
*next_iterator
, *del_iterator
;
718 HFSPlusAttrKey
*next_key
;
719 struct filefork
*btfile
;
720 int result
, iter_result
;
722 if (hfsmp
->hfs_attribute_vp
== NULL
) {
725 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
727 MALLOC(next_iterator
, BTreeIterator
*, sizeof(BTreeIterator
) * 2, M_TEMP
, M_WAITOK
);
728 bzero(next_iterator
, sizeof(BTreeIterator
) * 2);
729 del_iterator
= &next_iterator
[1];
730 next_key
= (HFSPlusAttrKey
*)&next_iterator
->key
;
733 * Go to first possible attribute key/record pair
735 (void) buildkey(fileid
, NULL
, next_key
);
736 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, next_iterator
, NULL
, NULL
);
737 if (result
|| next_key
->fileID
!= fileid
) {
740 /* Remember iterator of attribute to delete */
741 bcopy(next_iterator
, del_iterator
, sizeof(BTreeIterator
));
743 /* Loop until there are no more attributes for this file id */
745 iter_result
= BTIterateRecord(btfile
, kBTreeNextRecord
, next_iterator
, NULL
, NULL
);
747 /* XXX need to free and extents for record types 0x20 and 0x30 */
748 result
= BTDeleteRecord(btfile
, del_iterator
);
753 result
= iter_result
;
756 if (iter_result
|| next_key
->fileID
!= fileid
) {
757 break; /* end of attributes for this file id */
759 bcopy(next_iterator
, del_iterator
, sizeof(BTreeIterator
));
762 (void) BTFlushPath(btfile
);
764 if (result
== btNotFound
) {
767 FREE(next_iterator
, M_TEMP
);
772 * Enable/Disable extended security (ACLs).
776 hfs_setextendedsecurity(struct hfsmount
*hfsmp
, int state
)
778 struct BTreeIterator
* iterator
= NULL
;
779 struct filefork
*btfile
;
783 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
787 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
788 bzero(iterator
, sizeof(*iterator
));
791 * Build a b-tree key.
792 * We use the root's parent id (1) to hold this volume attribute.
794 (void) buildkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
795 (HFSPlusAttrKey
*)&iterator
->key
);
797 /* Start a transaction for our changes. */
798 if (hfs_start_transaction(hfsmp
) != 0) {
803 * If there isn't an attributes b-tree then create one.
805 if (hfsmp
->hfs_attribute_vp
== NULL
) {
806 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
807 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
808 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
809 hfs_systemfile_unlock(hfsmp
, lockflags
);
814 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
816 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
819 /* Remove the attribute. */
820 result
= BTDeleteRecord(btfile
, iterator
);
821 if (result
== btNotFound
)
824 FSBufferDescriptor btdata
;
825 HFSPlusAttrData attrdata
;
828 datasize
= sizeof(attrdata
);
829 btdata
.bufferAddress
= &attrdata
;
830 btdata
.itemSize
= datasize
;
831 btdata
.itemCount
= 1;
832 attrdata
.recordType
= kHFSPlusAttrInlineData
;
833 attrdata
.reserved
[0] = 0;
834 attrdata
.reserved
[1] = 0;
835 attrdata
.attrSize
= 2;
836 attrdata
.attrData
[0] = 0;
837 attrdata
.attrData
[1] = 0;
839 /* Insert the attribute. */
840 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
841 if (result
== btExists
)
844 (void) BTFlushPath(btfile
);
846 hfs_systemfile_unlock(hfsmp
, lockflags
);
848 /* Finish the transaction of our changes. */
849 hfs_end_transaction(hfsmp
);
851 FREE(iterator
, M_TEMP
);
855 vfs_clearextendedsecurity(HFSTOVFS(hfsmp
));
857 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
858 printf("hfs: %s extended security on %s\n",
859 state
== 0 ? "disabling" : "enabling", hfsmp
->vcbVN
);
862 return MacToVFSError(result
);
866 * Check for extended security (ACLs).
870 hfs_checkextendedsecurity(struct hfsmount
*hfsmp
)
872 struct BTreeIterator
* iterator
;
873 struct filefork
*btfile
;
877 if (hfsmp
->hfs_flags
& HFS_STANDARD
||
878 hfsmp
->hfs_attribute_vp
== NULL
) {
882 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
883 bzero(iterator
, sizeof(*iterator
));
886 * Build a b-tree key.
887 * We use the root's parent id (1) to hold this volume attribute.
889 (void) buildkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
890 (HFSPlusAttrKey
*)&iterator
->key
);
892 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
894 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
896 /* Check for our attribute. */
897 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
899 hfs_systemfile_unlock(hfsmp
, lockflags
);
900 FREE(iterator
, M_TEMP
);
903 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
904 printf("hfs mount: enabling extended security on %s\n", hfsmp
->vcbVN
);
910 * hfs_attrkeycompare - compare two attribute b-tree keys.
912 * The name portion of the key is compared using a 16-bit binary comparison.
913 * This is called from the b-tree code.
917 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
919 u_int32_t searchFileID
, trialFileID
;
922 searchFileID
= searchKey
->fileID
;
923 trialFileID
= trialKey
->fileID
;
926 if (searchFileID
> trialFileID
) {
928 } else if (searchFileID
< trialFileID
) {
931 u_int16_t
* str1
= &searchKey
->attrName
[0];
932 u_int16_t
* str2
= &trialKey
->attrName
[0];
933 int length1
= searchKey
->attrNameLen
;
934 int length2
= trialKey
->attrNameLen
;
938 if (length1
< length2
) {
941 } else if (length1
> length2
) {
964 * Names are equal; compare startBlock
966 if (searchKey
->startBlock
== trialKey
->startBlock
)
969 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
977 * buildkey - build an Attribute b-tree key
980 buildkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
983 size_t unicodeBytes
= 0;
985 if (attrname
!= NULL
) {
987 * Convert filename from UTF-8 into Unicode
989 result
= utf8_decodestr(attrname
, strlen(attrname
), key
->attrName
,
990 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
992 if (result
!= ENAMETOOLONG
)
993 result
= EINVAL
; /* name has invalid characters */
996 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
997 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
999 key
->attrNameLen
= 0;
1000 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
1003 key
->fileID
= fileID
;
1004 key
->startBlock
= 0;
1010 * getnodecount - calculate starting node count for attributes b-tree.
1013 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
1019 avedatasize
= sizeof(u_int16_t
); /* index slot */
1020 avedatasize
+= kHFSPlusAttrKeyMinimumLength
+ HFS_AVERAGE_NAME_SIZE
* sizeof(u_int16_t
);
1021 avedatasize
+= sizeof(HFSPlusAttrData
) + 32;
1023 recpernode
= (nodesize
- sizeof(BTNodeDescriptor
)) / avedatasize
;
1025 count
= (hfsmp
->hfs_filecount
+ hfsmp
->hfs_dircount
) / 8;
1026 count
/= recpernode
;
1028 /* XXX should also consider volume size XXX */
1030 return (MAX(count
, (int)(1024 * 1024) / (int)nodesize
));
1035 * getmaxinlineattrsize - calculate maximum inline attribute size.
1037 * This yields 3,802 bytes for an 8K node size.
1040 getmaxinlineattrsize(struct vnode
* attrvp
)
1042 struct BTreeInfoRec btinfo
;
1043 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
1046 if (attrvp
!= NULL
) {
1047 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
);
1048 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
1049 nodesize
= btinfo
.nodeSize
;
1050 hfs_unlock(VTOC(attrvp
));
1053 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
1054 maxsize
-= 3 * sizeof(UInt16
); /* minus 3 index slots */
1055 maxsize
/= 2; /* 2 key/rec pairs minumum */
1056 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
1057 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
1058 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */