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_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
151 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, vfs_context_proc(ap
->a_context
));
152 hfs_unlock(VTOC(vp
));
157 *ap
->a_size
= (size_t)VTOF(rvp
)->ff_size
;
159 result
= VNOP_READ(rvp
, uio
, 0, ap
->a_context
);
166 * Standard HFS only supports native FinderInfo and Resource Forks.
168 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
171 /* Bail if we don't have any extended attributes. */
172 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
173 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
176 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
178 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
179 bzero(iterator
, sizeof(*iterator
));
181 bufsize
= sizeof(HFSPlusAttrData
) - 2;
183 bufsize
+= uio_resid(uio
);
184 MALLOC(datap
, HFSPlusAttrData
*, bufsize
, M_TEMP
, M_WAITOK
);
185 btdata
.bufferAddress
= datap
;
186 btdata
.itemSize
= bufsize
;
187 btdata
.itemCount
= 1;
189 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
193 /* Lookup the attribute. */
194 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
195 result
= BTSearchRecord(btfile
, iterator
, &btdata
, &datasize
, NULL
);
196 hfs_systemfile_unlock(hfsmp
, lockflags
);
199 if (result
== btNotFound
)
204 *ap
->a_size
= datap
->attrSize
;
206 /* Copy out the attribute data. */
208 if (datap
->attrSize
> uio_resid(uio
))
211 result
= uiomove((caddr_t
) &datap
->attrData
, datap
->attrSize
, uio
);
215 FREE(iterator
, M_TEMP
);
217 return MacToVFSError(result
);
221 * Set the data of an extended attribute.
225 hfs_vnop_setxattr(struct vnop_setxattr_args
*ap
)
227 struct vnop_setxattr_args {
228 struct vnodeop_desc *a_desc;
233 vfs_context_t a_context;
237 struct vnode
*vp
= ap
->a_vp
;
238 struct hfsmount
*hfsmp
;
239 uio_t uio
= ap
->a_uio
;
240 struct BTreeIterator
* iterator
= NULL
;
241 struct filefork
*btfile
;
243 FSBufferDescriptor btdata
;
244 HFSPlusAttrData
* datap
= NULL
;
249 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
250 return (EINVAL
); /* invalid name */
253 if (VNODE_IS_RSRC(vp
)) {
256 /* Set the Finder Info. */
257 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
260 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
))) {
261 /* attr exists and "create" was specified. */
262 if (ap
->a_options
& XATTR_CREATE
) {
266 /* attr doesn't exists and "replace" was specified. */
267 if (ap
->a_options
& XATTR_REPLACE
) {
271 if (uio_resid(uio
) != attrsize
)
274 result
= uiomove((caddr_t
) &VTOC(vp
)->c_finderinfo
, attrsize
, uio
);
276 VTOC(vp
)->c_touch_chgtime
= TRUE
;
277 VTOC(vp
)->c_flag
|= C_MODIFIED
;
278 result
= hfs_update(vp
, FALSE
);
282 /* Write the Resource Fork. */
283 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
284 struct vnode
*rvp
= NULL
;
286 if (!vnode_isreg(vp
)) {
289 if (RESOURCE_FORK_EXISTS(vp
)) {
290 /* attr exists and "create" was specified. */
291 if (ap
->a_options
& XATTR_CREATE
) {
295 /* attr doesn't exists and "replace" was specified. */
296 if (ap
->a_options
& XATTR_REPLACE
) {
300 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
303 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, vfs_context_proc(ap
->a_context
));
304 hfs_unlock(VTOC(vp
));
308 result
= VNOP_WRITE(rvp
, uio
, 0, ap
->a_context
);
313 * Standard HFS only supports native FinderInfo and Resource Forks.
315 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
318 if (hfsmp
->hfs_max_inline_attrsize
== 0) {
319 hfsmp
->hfs_max_inline_attrsize
= getmaxinlineattrsize(hfsmp
->hfs_attribute_vp
);
321 attrsize
= uio_resid(uio
);
322 if (attrsize
> hfsmp
->hfs_max_inline_attrsize
) {
324 * XXX Need to support extent-based attributes XXX
328 /* Calculate size of record rounded up to multiple of 2 bytes. */
329 datasize
= sizeof(HFSPlusAttrData
) - 2 + attrsize
+ ((attrsize
& 1) ? 1 : 0);
331 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
332 bzero(iterator
, sizeof(*iterator
));
334 MALLOC(datap
, HFSPlusAttrData
*, datasize
, M_TEMP
, M_WAITOK
);
335 btdata
.bufferAddress
= datap
;
336 btdata
.itemSize
= datasize
;
337 btdata
.itemCount
= 1;
338 datap
->recordType
= kHFSPlusAttrInlineData
;
339 datap
->reserved
[0] = 0;
340 datap
->reserved
[1] = 0;
341 datap
->attrSize
= attrsize
;
343 /* Copy in the attribute data. */
344 result
= uiomove((caddr_t
) &datap
->attrData
, attrsize
, uio
);
348 /* Build a b-tree key. */
349 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
353 /* Start a transaction for our changes. */
354 if (hfs_start_transaction(hfsmp
) != 0) {
359 /* once we started the transaction, nobody can compete with us, so make sure this file is still there */
362 if (cp
->c_flag
& C_NOEXISTS
) { /* this file has already been removed */
368 * If there isn't an attributes b-tree then create one.
370 if (hfsmp
->hfs_attribute_vp
== NULL
) {
371 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
372 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
373 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
374 hfs_systemfile_unlock(hfsmp
, lockflags
);
379 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
381 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
383 if (ap
->a_options
& XATTR_REPLACE
) {
384 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
391 /* Insert the attribute. */
392 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
394 if (result
!= btExists
) {
398 // if it exists and XATTR_CREATE was specified,
399 // the spec says to return EEXIST
400 if (ap
->a_options
& XATTR_CREATE
) {
404 /* XXX need to account for old size in c_attrblks */
405 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
408 (void) BTFlushPath(btfile
);
410 hfs_systemfile_unlock(hfsmp
, lockflags
);
415 cp
->c_touch_chgtime
= TRUE
;
416 if ((cp
->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
417 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
418 (void) hfs_update(vp
, 0);
420 HFS_KNOTE(vp
, NOTE_ATTRIB
);
423 /* Finish the transaction of our changes. */
424 hfs_end_transaction(hfsmp
);
427 FREE(iterator
, M_TEMP
);
429 if (result
== btNotFound
)
432 result
= MacToVFSError(result
);
438 * Remove an extended attribute.
442 hfs_vnop_removexattr(struct vnop_removexattr_args
*ap
)
444 struct vnop_removexattr_args {
445 struct vnodeop_desc *a_desc;
449 vfs_context_t a_context;
453 struct vnode
*vp
= ap
->a_vp
;
454 struct hfsmount
*hfsmp
;
455 struct BTreeIterator
* iterator
= NULL
;
456 struct filefork
*btfile
;
457 struct proc
*p
= vfs_context_proc(ap
->a_context
);
458 FSBufferDescriptor btdata
;
459 HFSPlusAttrData attrdata
;
463 if (ap
->a_name
== NULL
|| ap
->a_name
[0] == '\0') {
464 return (EINVAL
); /* invalid name */
467 if (VNODE_IS_RSRC(vp
)) {
471 /* If Resource Fork is non-empty then truncate it. */
472 if (bcmp(ap
->a_name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
473 struct vnode
*rvp
= NULL
;
475 if ( !vnode_isreg(vp
) ) {
478 if ( !RESOURCE_FORK_EXISTS(vp
) ) {
481 if ((result
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
484 result
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, p
);
485 hfs_unlock(VTOC(vp
));
489 hfs_lock_truncate(VTOC(rvp
), TRUE
);
490 if ((result
= hfs_lock(VTOC(rvp
), HFS_EXCLUSIVE_LOCK
))) {
491 hfs_unlock_truncate(VTOC(vp
));
495 result
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 0, ap
->a_context
);
497 hfs_unlock_truncate(VTOC(rvp
));
498 hfs_unlock(VTOC(rvp
));
503 /* Clear out the Finder Info. */
504 if (bcmp(ap
->a_name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
505 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
508 bzero(VTOC(vp
)->c_finderinfo
, sizeof(emptyfinfo
));
512 * Standard HFS only supports native FinderInfo and Resource Forks.
514 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
517 if (hfsmp
->hfs_attribute_vp
== NULL
) {
520 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
522 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
523 bzero(iterator
, sizeof(*iterator
));
525 if (hfs_start_transaction(hfsmp
) != 0) {
530 result
= buildkey(VTOC(vp
)->c_fileid
, ap
->a_name
, (HFSPlusAttrKey
*)&iterator
->key
);
534 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
536 btdata
.bufferAddress
= &attrdata
;
537 btdata
.itemSize
= sizeof(attrdata
);
538 btdata
.itemCount
= 1;
539 result
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
);
543 result
= BTDeleteRecord(btfile
, iterator
);
544 (void) BTFlushPath(btfile
);
546 hfs_systemfile_unlock(hfsmp
, lockflags
);
548 VTOC(vp
)->c_touch_chgtime
= TRUE
;
549 HFS_KNOTE(vp
, NOTE_ATTRIB
);
552 if (result
== btNotFound
) {
555 hfs_end_transaction(hfsmp
);
557 FREE(iterator
, M_TEMP
);
559 return MacToVFSError(result
);
564 * Retrieve the list of extended attribute names.
568 hfs_vnop_listxattr(struct vnop_listxattr_args
*ap
)
570 struct vnop_listxattr_args {
571 struct vnodeop_desc *a_desc;
576 vfs_context_t a_context;
579 struct vnode
*vp
= ap
->a_vp
;
580 struct hfsmount
*hfsmp
;
581 uio_t uio
= ap
->a_uio
;
582 struct BTreeIterator
* iterator
= NULL
;
583 struct filefork
*btfile
;
584 struct listattr_callback_state state
;
588 if (VNODE_IS_RSRC(vp
)) {
594 /* If Finder Info is non-empty then export it. */
595 if (bcmp(VTOC(vp
)->c_finderinfo
, emptyfinfo
, sizeof(emptyfinfo
)) != 0) {
597 *ap
->a_size
+= sizeof(XATTR_FINDERINFO_NAME
);
598 } else if (uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
601 result
= uiomove((caddr_t
)XATTR_FINDERINFO_NAME
,
602 sizeof(XATTR_FINDERINFO_NAME
), uio
);
607 /* If Resource Fork is non-empty then export it. */
608 if (vnode_isreg(vp
) && RESOURCE_FORK_EXISTS(vp
)) {
610 *ap
->a_size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
611 } else if (uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
614 result
= uiomove((caddr_t
)XATTR_RESOURCEFORK_NAME
,
615 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
621 * Standard HFS only supports native FinderInfo and Resource Forks.
622 * Return at this point.
624 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
627 /* Bail if we don't have any extended attributes. */
628 if ((hfsmp
->hfs_attribute_vp
== NULL
) ||
629 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSHasAttributesMask
) == 0) {
632 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
634 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
635 bzero(iterator
, sizeof(*iterator
));
636 result
= buildkey(VTOC(vp
)->c_fileid
, NULL
, (HFSPlusAttrKey
*)&iterator
->key
);
640 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
642 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
643 if (result
&& result
!= btNotFound
) {
644 hfs_systemfile_unlock(hfsmp
, lockflags
);
648 state
.fileID
= VTOC(vp
)->c_fileid
;
654 * Process entries starting just after iterator->key.
656 result
= BTIterateRecords(btfile
, kBTreeNextRecord
, iterator
,
657 (IterateCallBackProcPtr
)listattr_callback
, &state
);
658 hfs_systemfile_unlock(hfsmp
, lockflags
);
660 *ap
->a_size
+= state
.size
;
663 FREE(iterator
, M_TEMP
);
665 if (state
.result
|| result
== btNotFound
)
666 result
= state
.result
;
668 return MacToVFSError(result
);
673 * Callback - called for each attribute
676 listattr_callback(const HFSPlusAttrKey
*key
, __unused
const HFSPlusAttrData
*data
, struct listattr_callback_state
*state
)
678 char attrname
[XATTR_MAXNAMELEN
+ 1];
682 if (state
->fileID
!= key
->fileID
) {
684 return (0); /* stop */
687 * Skip over non-primary keys
689 if (key
->startBlock
!= 0) {
690 return (1); /* continue */
693 result
= utf8_encodestr(key
->attrName
, key
->attrNameLen
* sizeof(UniChar
),
694 attrname
, &bytecount
, sizeof(attrname
), 0, 0);
696 state
->result
= result
;
697 return (0); /* stop */
699 bytecount
++; /* account for null termination char */
701 if (xattr_protected(attrname
))
702 return (1); /* continue */
704 if (state
->uio
== NULL
) {
705 state
->size
+= bytecount
;
707 if (bytecount
> uio_resid(state
->uio
)) {
708 state
->result
= ERANGE
;
709 return (0); /* stop */
711 result
= uiomove((caddr_t
) attrname
, bytecount
, state
->uio
);
713 state
->result
= result
;
714 return (0); /* stop */
717 return (1); /* continue */
722 * Remove all the attributes from a cnode.
724 * A jornal transaction must be already started.
725 * Attributes b-Tree must have exclusive lock held.
729 hfs_removeallattr(struct hfsmount
*hfsmp
, u_int32_t fileid
)
731 BTreeIterator
*next_iterator
, *del_iterator
;
732 HFSPlusAttrKey
*next_key
;
733 struct filefork
*btfile
;
734 int result
, iter_result
;
736 if (hfsmp
->hfs_attribute_vp
== NULL
) {
739 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
741 MALLOC(next_iterator
, BTreeIterator
*, sizeof(BTreeIterator
) * 2, M_TEMP
, M_WAITOK
);
742 bzero(next_iterator
, sizeof(BTreeIterator
) * 2);
743 del_iterator
= &next_iterator
[1];
744 next_key
= (HFSPlusAttrKey
*)&next_iterator
->key
;
747 * Go to first possible attribute key/record pair
749 (void) buildkey(fileid
, NULL
, next_key
);
750 result
= BTIterateRecord(btfile
, kBTreeNextRecord
, next_iterator
, NULL
, NULL
);
751 if (result
|| next_key
->fileID
!= fileid
) {
754 /* Remember iterator of attribute to delete */
755 bcopy(next_iterator
, del_iterator
, sizeof(BTreeIterator
));
757 /* Loop until there are no more attributes for this file id */
759 iter_result
= BTIterateRecord(btfile
, kBTreeNextRecord
, next_iterator
, NULL
, NULL
);
761 /* XXX need to free and extents for record types 0x20 and 0x30 */
762 result
= BTDeleteRecord(btfile
, del_iterator
);
767 result
= iter_result
;
770 if (iter_result
|| next_key
->fileID
!= fileid
) {
771 break; /* end of attributes for this file id */
773 bcopy(next_iterator
, del_iterator
, sizeof(BTreeIterator
));
776 (void) BTFlushPath(btfile
);
778 if (result
== btNotFound
) {
781 FREE(next_iterator
, M_TEMP
);
786 * Enable/Disable extended security (ACLs).
790 hfs_setextendedsecurity(struct hfsmount
*hfsmp
, int state
)
792 struct BTreeIterator
* iterator
= NULL
;
793 struct filefork
*btfile
;
797 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
801 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
802 bzero(iterator
, sizeof(*iterator
));
805 * Build a b-tree key.
806 * We use the root's parent id (1) to hold this volume attribute.
808 (void) buildkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
809 (HFSPlusAttrKey
*)&iterator
->key
);
811 /* Start a transaction for our changes. */
812 if (hfs_start_transaction(hfsmp
) != 0) {
817 * If there isn't an attributes b-tree then create one.
819 if (hfsmp
->hfs_attribute_vp
== NULL
) {
820 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
821 result
= hfs_create_attr_btree(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
,
822 getnodecount(hfsmp
, ATTRIBUTE_FILE_NODE_SIZE
));
823 hfs_systemfile_unlock(hfsmp
, lockflags
);
828 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
830 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
833 /* Remove the attribute. */
834 result
= BTDeleteRecord(btfile
, iterator
);
835 if (result
== btNotFound
)
838 FSBufferDescriptor btdata
;
839 HFSPlusAttrData attrdata
;
842 datasize
= sizeof(attrdata
);
843 btdata
.bufferAddress
= &attrdata
;
844 btdata
.itemSize
= datasize
;
845 btdata
.itemCount
= 1;
846 attrdata
.recordType
= kHFSPlusAttrInlineData
;
847 attrdata
.reserved
[0] = 0;
848 attrdata
.reserved
[1] = 0;
849 attrdata
.attrSize
= 2;
850 attrdata
.attrData
[0] = 0;
851 attrdata
.attrData
[1] = 0;
853 /* Insert the attribute. */
854 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
855 if (result
== btExists
)
858 (void) BTFlushPath(btfile
);
860 hfs_systemfile_unlock(hfsmp
, lockflags
);
862 /* Finish the transaction of our changes. */
863 hfs_end_transaction(hfsmp
);
865 FREE(iterator
, M_TEMP
);
869 vfs_clearextendedsecurity(HFSTOVFS(hfsmp
));
871 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
872 printf("hfs: %s extended security on %s\n",
873 state
== 0 ? "disabling" : "enabling", hfsmp
->vcbVN
);
876 return MacToVFSError(result
);
880 * Check for extended security (ACLs).
884 hfs_checkextendedsecurity(struct hfsmount
*hfsmp
)
886 struct BTreeIterator
* iterator
;
887 struct filefork
*btfile
;
891 if (hfsmp
->hfs_flags
& HFS_STANDARD
||
892 hfsmp
->hfs_attribute_vp
== NULL
) {
896 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
897 bzero(iterator
, sizeof(*iterator
));
900 * Build a b-tree key.
901 * We use the root's parent id (1) to hold this volume attribute.
903 (void) buildkey(kHFSRootParentID
, XATTR_EXTENDEDSECURITY_NAME
,
904 (HFSPlusAttrKey
*)&iterator
->key
);
906 btfile
= VTOF(hfsmp
->hfs_attribute_vp
);
908 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
910 /* Check for our attribute. */
911 result
= BTSearchRecord(btfile
, iterator
, NULL
, NULL
, NULL
);
913 hfs_systemfile_unlock(hfsmp
, lockflags
);
914 FREE(iterator
, M_TEMP
);
917 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
918 printf("hfs mount: enabling extended security on %s\n", hfsmp
->vcbVN
);
924 * hfs_attrkeycompare - compare two attribute b-tree keys.
926 * The name portion of the key is compared using a 16-bit binary comparison.
927 * This is called from the b-tree code.
931 hfs_attrkeycompare(HFSPlusAttrKey
*searchKey
, HFSPlusAttrKey
*trialKey
)
933 u_int32_t searchFileID
, trialFileID
;
936 searchFileID
= searchKey
->fileID
;
937 trialFileID
= trialKey
->fileID
;
940 if (searchFileID
> trialFileID
) {
942 } else if (searchFileID
< trialFileID
) {
945 u_int16_t
* str1
= &searchKey
->attrName
[0];
946 u_int16_t
* str2
= &trialKey
->attrName
[0];
947 int length1
= searchKey
->attrNameLen
;
948 int length2
= trialKey
->attrNameLen
;
952 if (length1
< length2
) {
955 } else if (length1
> length2
) {
978 * Names are equal; compare startBlock
980 if (searchKey
->startBlock
== trialKey
->startBlock
)
983 return (searchKey
->startBlock
< trialKey
->startBlock
? -1 : 1);
991 * buildkey - build an Attribute b-tree key
994 buildkey(u_int32_t fileID
, const char *attrname
, HFSPlusAttrKey
*key
)
997 size_t unicodeBytes
= 0;
999 if (attrname
!= NULL
) {
1001 * Convert filename from UTF-8 into Unicode
1003 result
= utf8_decodestr(attrname
, strlen(attrname
), key
->attrName
,
1004 &unicodeBytes
, sizeof(key
->attrName
), 0, 0);
1006 if (result
!= ENAMETOOLONG
)
1007 result
= EINVAL
; /* name has invalid characters */
1010 key
->attrNameLen
= unicodeBytes
/ sizeof(UniChar
);
1011 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ unicodeBytes
;
1013 key
->attrNameLen
= 0;
1014 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
1017 key
->fileID
= fileID
;
1018 key
->startBlock
= 0;
1024 * getnodecount - calculate starting node count for attributes b-tree.
1027 getnodecount(struct hfsmount
*hfsmp
, size_t nodesize
)
1033 avedatasize
= sizeof(u_int16_t
); /* index slot */
1034 avedatasize
+= kHFSPlusAttrKeyMinimumLength
+ HFS_AVERAGE_NAME_SIZE
* sizeof(u_int16_t
);
1035 avedatasize
+= sizeof(HFSPlusAttrData
) + 32;
1037 recpernode
= (nodesize
- sizeof(BTNodeDescriptor
)) / avedatasize
;
1039 count
= (hfsmp
->hfs_filecount
+ hfsmp
->hfs_dircount
) / 8;
1040 count
/= recpernode
;
1042 /* XXX should also consider volume size XXX */
1044 return (MAX(count
, (int)(1024 * 1024) / (int)nodesize
));
1049 * getmaxinlineattrsize - calculate maximum inline attribute size.
1051 * This yields 3,802 bytes for an 8K node size.
1054 getmaxinlineattrsize(struct vnode
* attrvp
)
1056 struct BTreeInfoRec btinfo
;
1057 size_t nodesize
= ATTRIBUTE_FILE_NODE_SIZE
;
1060 if (attrvp
!= NULL
) {
1061 (void) hfs_lock(VTOC(attrvp
), HFS_SHARED_LOCK
);
1062 if (BTGetInformation(VTOF(attrvp
), 0, &btinfo
) == 0)
1063 nodesize
= btinfo
.nodeSize
;
1064 hfs_unlock(VTOC(attrvp
));
1067 maxsize
-= sizeof(BTNodeDescriptor
); /* minus node descriptor */
1068 maxsize
-= 3 * sizeof(UInt16
); /* minus 3 index slots */
1069 maxsize
/= 2; /* 2 key/rec pairs minumum */
1070 maxsize
-= sizeof(HFSPlusAttrKey
); /* minus maximum key size */
1071 maxsize
-= sizeof(HFSPlusAttrData
) - 2; /* minus data header */
1072 maxsize
&= 0xFFFFFFFE; /* multiple of 2 bytes */