2 * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
30 * support for mandatory and extensible security protections. This notice
31 * is included in support of clause 2.2 (b) of the Apple Public License,
35 #include <sys/param.h>
37 #include <sys/fcntl.h>
38 #include <sys/fsevents.h>
39 #include <sys/kernel.h>
40 #include <sys/kauth.h>
41 #include <sys/malloc.h>
42 #include <sys/mount_internal.h>
43 #include <sys/namei.h>
44 #include <sys/proc_internal.h>
47 #include <sys/utfconv.h>
48 #include <sys/vnode.h>
49 #include <sys/vnode_internal.h>
50 #include <sys/xattr.h>
52 #include <libkern/OSByteOrder.h>
53 #include <vm/vm_kern.h>
56 #include <security/mac_framework.h>
63 * Cast to 'unsigned int' loses precision - hope that's OK...
65 #define MAKE_SHADOW_NAME(VP, NAME) \
66 snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%x%08x%x", (unsigned int)(VP), (VP)->v_id, (unsigned int)(VP)->v_data);
68 static vnode_t shadow_dvp
; /* tmp directory to hold stream shadow files */
69 static int shadow_vid
;
70 static int shadow_sequence
;
73 static int default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
);
75 static int default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
);
77 static int default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
);
79 static int getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
, int *creator
, vfs_context_t context
);
81 static int get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
);
87 * Default xattr support routines.
90 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
91 vfs_context_t context
);
96 * Retrieve the data of an extended attribute.
99 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
100 int options
, vfs_context_t context
)
104 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
108 /* getxattr calls are not allowed for streams. */
109 if (vp
->v_flag
& VISNAMEDSTREAM
) {
115 * Non-kernel request need extra checks performed.
117 * The XATTR_NOSECURITY flag implies a kernel request.
119 if (!(options
& XATTR_NOSECURITY
)) {
121 error
= mac_vnode_check_getextattr(context
, vp
, name
, uio
);
125 if ((error
= xattr_validatename(name
))) {
128 if ((error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
))) {
131 /* The offset can only be non-zero for resource forks. */
132 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
133 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
139 /* The offset can only be non-zero for resource forks. */
140 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
141 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
146 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
147 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
149 * A filesystem may keep some EAs natively and return ENOTSUP for others.
150 * SMB returns ENOTSUP for finderinfo and resource forks.
152 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
159 * Set the data of an extended attribute.
162 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
166 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
170 /* setxattr calls are not allowed for streams. */
171 if (vp
->v_flag
& VISNAMEDSTREAM
) {
176 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
179 if ((error
= xattr_validatename(name
))) {
182 if (!(options
& XATTR_NOSECURITY
)) {
184 error
= mac_vnode_check_setextattr(context
, vp
, name
, uio
);
188 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
192 /* The offset can only be non-zero for resource forks. */
193 if (uio_offset(uio
) != 0 &&
194 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0 ) {
199 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
202 * An EJUSTRETURN is from a filesystem which keeps this xattr
203 * natively as well as in a dot-underscore file. In this case the
204 * EJUSTRETURN means the filesytem has done nothing, but identifies the
205 * EA as one which may be represented natively and/or in a DU, and
206 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
207 * in vn_setxattr can we do the getxattrs needed to ascertain whether
208 * the XATTR_{CREATE,REPLACE} should yield an error.
210 if (error
== EJUSTRETURN
) {
211 int native
= 0, dufile
= 0;
212 size_t sz
; /* not used */
214 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
215 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
216 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
220 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
225 * Having determined no CREATE/REPLACE error should result, we
226 * zero those bits, so both backing stores get written to.
228 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
229 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
230 /* the mainline path here is to have error==ENOTSUP ... */
232 #endif /* DUAL_EAS */
233 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
235 * A filesystem may keep some EAs natively and return ENOTSUP for others.
236 * SMB returns ENOTSUP for finderinfo and resource forks.
238 error
= default_setxattr(vp
, name
, uio
, options
, context
);
241 if ((error
== 0) && !(options
& XATTR_NOSECURITY
) &&
242 (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
))
243 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
250 * Remove an extended attribute.
253 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
257 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
261 /* removexattr calls are not allowed for streams. */
262 if (vp
->v_flag
& VISNAMEDSTREAM
) {
267 if ((error
= xattr_validatename(name
))) {
270 if (!(options
& XATTR_NOSECURITY
)) {
272 error
= mac_vnode_check_deleteextattr(context
, vp
, name
);
276 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
280 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
281 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
283 * A filesystem may keep some EAs natively and return ENOTSUP for others.
284 * SMB returns ENOTSUP for finderinfo and resource forks.
286 error
= default_removexattr(vp
, name
, options
, context
);
288 } else if (error
== EJUSTRETURN
) {
290 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
291 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
292 * a native xattr, so failure to find it in a DU file during
293 * default_removexattr should not be considered an error.
295 error
= default_removexattr(vp
, name
, options
, context
);
296 if (error
== ENOATTR
)
298 #endif /* DUAL_EAS */
301 if ((error
== 0) && !(options
& XATTR_NOSECURITY
) &&
302 (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
))
303 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
310 * Retrieve the list of extended attribute names.
313 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
317 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
321 /* listxattr calls are not allowed for streams. */
322 if (vp
->v_flag
& VISNAMEDSTREAM
) {
327 if (!(options
& XATTR_NOSECURITY
)) {
329 error
= mac_vnode_check_listextattr(context
, vp
);
334 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
);
339 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
340 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
342 * A filesystem may keep some but not all EAs natively, in which case
343 * the native EA names will have been uiomove-d out (or *size updated)
344 * and the default_listxattr here will finish the job. Note SMB takes
345 * advantage of this for its finder-info and resource forks.
347 error
= default_listxattr(vp
, uio
, size
, options
, context
);
354 xattr_validatename(const char *name
)
358 if (name
== NULL
|| name
[0] == '\0') {
361 namelen
= strnlen(name
, XATTR_MAXNAMELEN
);
362 if (name
[namelen
] != '\0')
363 return (ENAMETOOLONG
);
365 if (utf8_validatestr((const unsigned char *)name
, namelen
) != 0)
373 * Determine whether an EA is a protected system attribute.
376 xattr_protected(const char *attrname
)
378 return(!strncmp(attrname
, "com.apple.system.", 17));
384 * Obtain a named stream from vnode vp.
387 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
391 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
392 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
394 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
401 svp
->v_flag
|= VISNAMEDSTREAM
;
403 /* Make the file its parent.
404 * Note: This parent link helps us distinguish vnodes for
405 * shadow stream files from vnodes for resource fork on file
406 * systems that support named streams natively (both have
407 * VISNAMEDSTREAM set) by allowing access to mount structure
408 * for checking MNTK_NAMED_STREAMS bit at many places in the code
410 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
417 * Make a named stream for vnode vp.
420 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
424 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
425 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
427 error
= default_makenamedstream(vp
, svpp
, name
, context
);
434 svp
->v_flag
|= VISNAMEDSTREAM
;
436 /* Make the file its parent.
437 * Note: This parent link helps us distinguish vnodes for
438 * shadow stream files from vnodes for resource fork on file
439 * systems that support named streams natively (both have
440 * VISNAMEDSTREAM set) by allowing access to mount structure
441 * for checking MNTK_NAMED_STREAMS bit at many places in the code
443 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
449 * Remove a named stream from vnode vp.
452 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
456 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
457 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
459 error
= default_removenamedstream(vp
, name
, context
);
464 #define NS_IOBUFSIZE (128 * 1024)
467 * Release a named stream shadow file.
469 * Note: This function is called from two places where we do not need
470 * to check if the vnode has any references held before deleting the
471 * shadow file. Once from vclean() when the vnode is being reclaimed
472 * and we do not hold any references on the vnode. Second time from
473 * default_getnamedstream() when we get an error during shadow stream
474 * file initialization so that other processes who are waiting for the
475 * shadow stream file initialization by the creator will get opportunity
476 * to create and initialize the file again.
479 vnode_relenamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
482 struct componentname cn
;
489 MAKE_SHADOW_NAME(vp
, tmpname
);
492 cn
.cn_nameiop
= DELETE
;
493 cn
.cn_flags
= ISLASTCN
;
494 cn
.cn_context
= context
;
495 cn
.cn_pnbuf
= tmpname
;
496 cn
.cn_pnlen
= sizeof(tmpname
);
497 cn
.cn_nameptr
= cn
.cn_pnbuf
;
498 cn
.cn_namelen
= strlen(tmpname
);
500 /* Obtain the vnode for the shadow files directory. */
501 err
= get_shadow_dir(&dvp
, context
);
506 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, context
);
513 * Flush a named stream shadow file.
516 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
518 struct vnode_attr va
;
520 caddr_t bufptr
= NULL
;
528 VATTR_WANTED(&va
, va_data_size
);
529 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
530 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
533 datasize
= va
.va_data_size
;
534 if ((datasize
== 0)) {
535 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
539 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
540 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
543 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
547 * Copy the shadow stream file data into the resource fork.
549 error
= VNOP_OPEN(svp
, 0, context
);
551 printf("vnode_flushnamedstream: err %d opening file\n", error
);
554 while (offset
< datasize
) {
555 iosize
= MIN(datasize
- offset
, iosize
);
557 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
558 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
559 error
= VNOP_READ(svp
, auio
, 0, context
);
563 /* Since there's no truncate xattr we must remove the resource fork. */
565 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
566 if ((error
!= 0) && (error
!= ENOATTR
)) {
570 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_WRITE
);
571 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
572 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
578 (void) VNOP_CLOSE(svp
, 0, context
);
581 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
591 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
592 int *creator
, vfs_context_t context
)
594 vnode_t dvp
= NULLVP
;
595 vnode_t svp
= NULLVP
;
596 struct componentname cn
;
597 struct vnode_attr va
;
604 /* Establish a unique file name. */
605 MAKE_SHADOW_NAME(vp
, tmpname
);
606 bzero(&cn
, sizeof(cn
));
607 cn
.cn_nameiop
= LOOKUP
;
608 cn
.cn_flags
= ISLASTCN
;
609 cn
.cn_context
= context
;
610 cn
.cn_pnbuf
= tmpname
;
611 cn
.cn_pnlen
= sizeof(tmpname
);
612 cn
.cn_nameptr
= cn
.cn_pnbuf
;
613 cn
.cn_namelen
= strlen(tmpname
);
615 /* Pick up uid, gid, mode and date from original file. */
617 VATTR_WANTED(&va
, va_uid
);
618 VATTR_WANTED(&va
, va_gid
);
619 VATTR_WANTED(&va
, va_mode
);
620 VATTR_WANTED(&va
, va_create_time
);
621 VATTR_WANTED(&va
, va_modify_time
);
622 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
623 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
624 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
625 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
626 va
.va_uid
= KAUTH_UID_NONE
;
627 va
.va_gid
= KAUTH_GID_NONE
;
628 va
.va_mode
= S_IRUSR
| S_IWUSR
;
630 va
.va_vaflags
= VA_EXCLUSIVE
;
631 VATTR_SET(&va
, va_type
, VREG
);
632 /* We no longer change the access, but we still hide it. */
633 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
635 /* Obtain the vnode for the shadow files directory. */
636 if (get_shadow_dir(&dvp
, context
) != 0) {
641 /* See if someone else already has it open. */
642 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, context
) == 0) {
643 /* Double check existence by asking for size. */
645 VATTR_WANTED(&va
, va_data_size
);
646 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
647 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
648 goto out
; /* OK to use. */
652 /* Otherwise make sure the resource fork data exists. */
653 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
654 XATTR_NOSECURITY
, context
);
656 * To maintain binary compatibility with legacy Carbon
657 * emulated resource fork support, if the resource fork
658 * doesn't exist but the Finder Info does, then act as
659 * if an empty resource fork is present (see 4724359).
661 if ((error
== ENOATTR
) &&
662 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
663 XATTR_NOSECURITY
, context
) == 0)) {
671 /* If the resource fork exists, its size is expected to be non-zero. */
678 /* Create the shadow stream file. */
679 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, context
);
682 } else if ((error
== EEXIST
) && !makestream
) {
683 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, context
);
690 /* On errors, clean up shadow stream file. */
698 *rsrcsize
= datasize
;
705 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
707 vnode_t svp
= NULLVP
;
709 caddr_t bufptr
= NULL
;
716 * Only the "com.apple.ResourceFork" stream is supported here.
718 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
724 * Obtain a shadow file for the resource fork I/O.
726 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
733 * The creator of the shadow file provides its file data,
734 * all other threads should wait until its ready.
738 if (svp
->v_flag
& VISNAMEDSTREAM
) {
739 /* data is ready, go use it */
743 /* its not ready, wait for it (sleep using v_parent as channel) */
744 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
745 "getnamedstream", NULL
);
753 * Copy the real resource fork data into shadow stream file.
755 if (op
== NS_OPEN
&& datasize
!= 0) {
759 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
760 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
765 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
768 error
= VNOP_OPEN(svp
, 0, context
);
772 while (offset
< datasize
) {
775 iosize
= MIN(datasize
- offset
, iosize
);
777 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
778 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
779 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
780 XATTR_NOSECURITY
, context
);
785 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_WRITE
);
786 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
787 error
= VNOP_WRITE(svp
, auio
, 0, context
);
793 (void) VNOP_CLOSE(svp
, 0, context
);
796 /* Wake up anyone waiting for svp file content */
800 svp
->v_flag
|= VISNAMEDSTREAM
;
801 wakeup((caddr_t
)&svp
->v_parent
);
804 /* On post create errors, get rid of the shadow file. This
805 * way, if there is another process waiting for initialization
806 * of the shadow file by the current process, it will wake up
807 * and retry by creating and initializing the shadow file again.
809 (void) vnode_relenamedstream(vp
, svp
, context
);
810 wakeup((caddr_t
)&svp
->v_parent
);
815 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
821 /* On errors, clean up shadow stream file. */
832 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
838 * Only the "com.apple.ResourceFork" stream is supported here.
840 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
844 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
847 * Wake up any waiters over in default_getnamedstream().
849 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
853 svp
->v_flag
|= VISNAMEDSTREAM
;
854 /* Wakeup any waiters on the v_parent channel */
855 wakeup((caddr_t
)&svp
->v_parent
);
862 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
865 * Only the "com.apple.ResourceFork" stream is supported here.
867 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
871 * XXX - what about other opened instances?
873 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
877 get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
)
879 vnode_t dvp
= NULLVP
;
880 vnode_t sdvp
= NULLVP
;
881 struct componentname cn
;
882 struct vnode_attr va
;
887 /* Check if we've already created it. */
888 if (shadow_dvp
!= NULLVP
) {
889 if ((error
= vnode_getwithvid(shadow_dvp
, shadow_vid
))) {
897 /* Obtain the vnode for "/tmp" directory. */
898 if (vnode_lookup("/tmp", 0, &dvp
, context
) != 0) {
903 /* Create the shadow stream directory. */
904 snprintf(tmpname
, sizeof(tmpname
), ".vfs_rsrc_streams_%x%x",
905 (unsigned int)rootvnode
, shadow_sequence
);
906 bzero(&cn
, sizeof(cn
));
907 cn
.cn_nameiop
= LOOKUP
;
908 cn
.cn_flags
= ISLASTCN
;
909 cn
.cn_context
= context
;
910 cn
.cn_pnbuf
= tmpname
;
911 cn
.cn_pnlen
= sizeof(tmpname
);
912 cn
.cn_nameptr
= cn
.cn_pnbuf
;
913 cn
.cn_namelen
= strlen(tmpname
);
916 * owned by root, only readable by root, hidden
919 VATTR_SET(&va
, va_uid
, 0);
920 VATTR_SET(&va
, va_gid
, 0);
921 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
922 VATTR_SET(&va
, va_type
, VDIR
);
923 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
924 va
.va_vaflags
= VA_EXCLUSIVE
;
926 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, context
);
929 * There can be only one winner for an exclusive create.
932 /* Take a long term ref to keep this dir around. */
933 error
= vnode_ref(sdvp
);
936 shadow_vid
= sdvp
->v_id
;
938 } else if (error
== EEXIST
) {
939 /* loser has to look up directory */
940 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, context
);
942 /* Make sure its in fact a directory */
943 if (sdvp
->v_type
!= VDIR
) {
946 /* Obtain the fsid for /tmp directory */
948 VATTR_WANTED(&va
, va_fsid
);
949 if (VNOP_GETATTR(dvp
, &va
, context
) != 0 ||
950 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
953 tmp_fsid
= va
.va_fsid
;
956 VATTR_WANTED(&va
, va_uid
);
957 VATTR_WANTED(&va
, va_gid
);
958 VATTR_WANTED(&va
, va_mode
);
959 VATTR_WANTED(&va
, va_fsid
);
960 VATTR_WANTED(&va
, va_dirlinkcount
);
961 VATTR_WANTED(&va
, va_acl
);
962 /* Provide defaults for attrs that may not be supported */
963 va
.va_dirlinkcount
= 1;
964 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
966 if (VNOP_GETATTR(sdvp
, &va
, context
) != 0 ||
967 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
968 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
969 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
970 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
974 * Make sure its what we want:
976 * - not writable by anyone
977 * - on same file system as /tmp
978 * - not a hard-linked directory
979 * - no ACLs (they might grant write access)
981 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
982 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
983 (va
.va_fsid
!= tmp_fsid
) ||
984 (va
.va_dirlinkcount
!= 1) ||
985 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
995 /* On errors, clean up shadow stream directory. */
1005 /* This is not the dir we're looking for, move along */
1006 ++shadow_sequence
; /* try something else next time */
1015 * Default Implementation (Non-native EA)
1020 Typical "._" AppleDouble Header File layout:
1021 ------------------------------------------------------------
1026 .-- AD ENTRY[0] Finder Info Entry (must be first)
1027 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1029 | ///////////// Fixed Size Data (32 bytes)
1033 | ATTR ENTRY[1] --+--.
1034 | ATTR ENTRY[2] --+--+--.
1036 | ATTR ENTRY[N] --+--+--+--.
1037 | ATTR DATA 0 <-' | | |
1038 | //////////// | | |
1039 | ATTR DATA 1 <----' | |
1041 | ATTR DATA 2 <-------' |
1044 | ATTR DATA N <----------'
1046 | Attribute Free Space
1048 '----> RESOURCE FORK
1049 ///////////// Variable Sized Data
1058 ------------------------------------------------------------
1060 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1061 stored as part of the Finder Info. The length in the Finder
1062 Info AppleDouble entry includes the length of the extended
1063 attribute header, attribute entries, and attribute data.
1068 * On Disk Data Structures
1070 * Note: Motorola 68K alignment and big-endian.
1072 * See RFC 1740 for additional information about the AppleDouble file format.
1076 #define ADH_MAGIC 0x00051607
1077 #define ADH_VERSION 0x00020000
1078 #define ADH_MACOSX "Mac OS X "
1081 * AppleDouble Entry ID's
1083 #define AD_DATA 1 /* Data fork */
1084 #define AD_RESOURCE 2 /* Resource fork */
1085 #define AD_REALNAME 3 /* FileÕs name on home file system */
1086 #define AD_COMMENT 4 /* Standard Mac comment */
1087 #define AD_ICONBW 5 /* Mac black & white icon */
1088 #define AD_ICONCOLOR 6 /* Mac color icon */
1089 #define AD_UNUSED 7 /* Not used */
1090 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1091 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1092 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1093 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1094 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1095 #define AD_AFPNAME 13 /* Short name on AFP server */
1096 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1097 #define AD_AFPDIRID 15 /* AFP directory ID */
1098 #define AD_ATTRIBUTES AD_FINDERINFO
1101 #define ATTR_FILE_PREFIX "._"
1102 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1104 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1106 /* Implementation Limits */
1107 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
1108 #define ATTR_MAX_HDR_SIZE 65536
1110 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1111 * size supported (including the attribute entries). All of
1112 * the attribute entries must reside within this limit. If
1113 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1114 * boundry, then all of the attribute data I/O is performed
1115 * separately from the attribute header I/O.
1117 * In particular, all of the attr_entry structures must lie
1118 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1119 * AppleDouble file. However, the attribute data (i.e. the
1120 * contents of the extended attributes) may extend beyond the
1121 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1122 * limit is to allow the implementation to optimize by reading
1123 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1127 #pragma options align=mac68k
1129 #define FINDERINFOSIZE 32
1131 typedef struct apple_double_entry
{
1132 u_int32_t type
; /* entry type: see list, 0 invalid */
1133 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1134 u_int32_t length
; /* entry data length in bytes. */
1135 } apple_double_entry_t
;
1138 typedef struct apple_double_header
{
1139 u_int32_t magic
; /* == ADH_MAGIC */
1140 u_int32_t version
; /* format version: 2 = 0x00020000 */
1141 u_int32_t filler
[4];
1142 u_int16_t numEntries
; /* number of entries which follow */
1143 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1144 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1145 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1146 } apple_double_header_t
;
1148 #define ADHDRSIZE (4+4+16+2)
1150 /* Entries are aligned on 4 byte boundaries */
1151 typedef struct attr_entry
{
1152 u_int32_t offset
; /* file offset to data */
1153 u_int32_t length
; /* size of attribute data */
1156 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1160 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1161 typedef struct attr_header
{
1162 apple_double_header_t appledouble
;
1163 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1164 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1165 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1166 u_int32_t data_start
; /* file offset to attribute data area */
1167 u_int32_t data_length
; /* length of attribute data area */
1168 u_int32_t reserved
[3];
1170 u_int16_t num_attrs
;
1174 /* Empty Resource Fork Header */
1175 typedef struct rsrcfork_header
{
1176 u_int32_t fh_DataOffset
;
1177 u_int32_t fh_MapOffset
;
1178 u_int32_t fh_DataLength
;
1179 u_int32_t fh_MapLength
;
1180 u_int8_t systemData
[112];
1181 u_int8_t appData
[128];
1182 u_int32_t mh_DataOffset
;
1183 u_int32_t mh_MapOffset
;
1184 u_int32_t mh_DataLength
;
1185 u_int32_t mh_MapLength
;
1187 u_int16_t mh_RefNum
;
1189 u_int8_t mh_InMemoryAttr
;
1192 u_int16_t typeCount
;
1193 } rsrcfork_header_t
;
1195 #define RF_FIRST_RESOURCE 256
1196 #define RF_NULL_MAP_LENGTH 30
1197 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1199 #pragma options align=reset
1201 /* Runtime information about the attribute file. */
1202 typedef struct attr_info
{
1203 vfs_context_t context
;
1208 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1209 apple_double_header_t
*filehdr
;
1210 apple_double_entry_t
*finderinfo
;
1211 apple_double_entry_t
*rsrcfork
;
1212 attr_header_t
*attrhdr
;
1213 attr_entry_t
*attr_entry
;
1215 u_int8_t emptyfinderinfo
;
1219 #define ATTR_SETTING 1
1221 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1223 #define ATTR_ENTRY_LENGTH(namelen) \
1224 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1226 #define ATTR_NEXT(ae) \
1227 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1229 #define ATTR_VALID(ae, ai) \
1230 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1232 #define SWAP16(x) OSSwapBigToHostInt16((x))
1233 #define SWAP32(x) OSSwapBigToHostInt32((x))
1234 #define SWAP64(x) OSSwapBigToHostInt64((x))
1237 static u_int32_t emptyfinfo
[8] = {0};
1241 * Local support routines
1243 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1245 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1247 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1249 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1251 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1253 static void rel_xattrinfo(attr_info_t
*ainfop
);
1255 static int write_xattrinfo(attr_info_t
*ainfop
);
1257 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1259 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1261 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1264 #if BYTE_ORDER == LITTLE_ENDIAN
1265 static void swap_adhdr(apple_double_header_t
*adh
);
1266 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1269 #define swap_adhdr(x)
1270 #define swap_attrhdr(x, y)
1273 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1274 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1275 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1279 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1280 * is in big endian (as it would exist on disk). Verifies the following:
1283 * - number of entries
1284 * - that each entry fits within the file size
1286 * If the header is invalid, ENOATTR is returned.
1288 * NOTE: Does not attempt to validate the extended attributes header that
1289 * may be embedded in the Finder Info entry.
1291 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1294 u_int32_t header_end
;
1295 u_int32_t entry_end
;
1297 apple_double_header_t
*header
;
1299 rawsize
= ainfop
->rawsize
;
1300 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1302 /* Is the file big enough to contain an AppleDouble header? */
1303 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1306 /* Swap the AppleDouble header fields to native order */
1307 header
->magic
= SWAP32(header
->magic
);
1308 header
->version
= SWAP32(header
->version
);
1309 header
->numEntries
= SWAP16(header
->numEntries
);
1311 /* Sanity check the AppleDouble header fields */
1312 if (header
->magic
!= ADH_MAGIC
||
1313 header
->version
!= ADH_VERSION
||
1314 header
->numEntries
< 1 ||
1315 header
->numEntries
> 15) {
1319 /* Calculate where the entries[] array ends */
1320 header_end
= offsetof(apple_double_header_t
, entries
) +
1321 header
->numEntries
* sizeof(apple_double_entry_t
);
1323 /* Is the file big enough to contain the AppleDouble entries? */
1324 if (rawsize
< header_end
) {
1328 /* Swap and sanity check each AppleDouble entry */
1329 for (i
=0; i
<header
->numEntries
; i
++) {
1330 /* Swap the per-entry fields to native order */
1331 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1332 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1333 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1335 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1338 * Does the entry's content start within the header itself,
1339 * did the addition overflow, or does the entry's content
1340 * extend past the end of the file?
1342 if (header
->entries
[i
].offset
< header_end
||
1343 entry_end
< header
->entries
[i
].offset
||
1344 entry_end
> ainfop
->filesize
) {
1349 * Does the current entry's content overlap with a previous
1352 * Yes, this is O(N**2), and there are more efficient algorithms
1353 * for testing pairwise overlap of N ranges when N is large.
1354 * But we have already ensured N < 16, and N is almost always 2.
1355 * So there's no point in using a more complex algorithm.
1358 for (j
=0; j
<i
; j
++) {
1359 if (entry_end
> header
->entries
[j
].offset
&&
1360 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1372 * Retrieve the data of an extended attribute.
1375 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1376 __unused
int options
, vfs_context_t context
)
1380 attr_header_t
*header
;
1381 attr_entry_t
*entry
;
1391 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1394 * Open the file locked (shared) since the Carbon
1395 * File Manager may have the Apple Double file open
1396 * and could be changing the resource fork.
1398 fileflags
|= O_SHLOCK
;
1403 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1406 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1407 close_xattrfile(xvp
, fileflags
, context
);
1411 /* Get the Finder Info. */
1412 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1414 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1416 } else if (uio
== NULL
) {
1417 *size
= FINDERINFOSIZE
;
1419 } else if (uio_offset(uio
) != 0) {
1421 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1424 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1425 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1430 /* Read the Resource Fork. */
1432 if (!vnode_isreg(vp
)) {
1434 } else if (ainfo
.rsrcfork
== NULL
) {
1436 } else if (uio
== NULL
) {
1437 *size
= (size_t)ainfo
.rsrcfork
->length
;
1439 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1440 error
= VNOP_READ(xvp
, uio
, 0, context
);
1442 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1447 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1451 if (uio_offset(uio
) != 0) {
1456 namelen
= strlen(name
) + 1;
1457 header
= ainfo
.attrhdr
;
1458 entry
= ainfo
.attr_entry
;
1460 * Search for attribute name in the header.
1462 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1463 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1464 datalen
= (size_t)entry
->length
;
1470 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1474 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1475 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1476 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1478 uio_setoffset(uio
, entry
->offset
);
1479 error
= VNOP_READ(xvp
, uio
, 0, context
);
1480 uio_setoffset(uio
, 0);
1484 entry
= ATTR_NEXT(entry
);
1487 rel_xattrinfo(&ainfo
);
1488 close_xattrfile(xvp
, fileflags
, context
);
1494 * Set the data of an extended attribute.
1497 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1501 attr_header_t
*header
;
1502 attr_entry_t
*entry
;
1503 attr_entry_t
*lastentry
;
1507 size_t datafreespace
;
1514 char finfo
[FINDERINFOSIZE
];
1516 datalen
= uio_resid(uio
);
1517 namelen
= strlen(name
) + 1;
1518 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1521 * By convention, Finder Info that is all zeroes is equivalent to not
1522 * having a Finder Info EA. So if we're trying to set the Finder Info
1523 * to all zeroes, then delete it instead. If a file didn't have an
1524 * AppleDouble file before, this prevents creating an AppleDouble file
1525 * with no useful content.
1527 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1528 * for all zeroes Finder Info before opening the AppleDouble file.
1529 * But if either of those options were specified, we need to open the
1530 * AppleDouble file to see whether there was already Finder Info (so we
1531 * can return an error if needed); this case is handled further below.
1533 * NOTE: this copies the Finder Info data into the "finfo" local.
1535 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1537 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1538 * That means we probably have to open_xattrfile and get_xattrinfo.
1540 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1543 error
= uiomove(finfo
, datalen
, uio
);
1546 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1547 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1548 error
= default_removexattr(vp
, name
, 0, context
);
1549 if (error
== ENOATTR
)
1557 * Open the file locked since setting an attribute
1558 * can change the layout of the Apple Double file.
1560 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1561 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1564 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1565 close_xattrfile(xvp
, fileflags
, context
);
1569 /* Set the Finder Info. */
1570 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1571 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1572 /* attr exists and "create" was specified? */
1573 if (options
& XATTR_CREATE
) {
1578 /* attr doesn't exists and "replace" was specified? */
1579 if (options
& XATTR_REPLACE
) {
1584 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1586 * Setting the Finder Info to all zeroes is equivalent to
1587 * removing it. Close the xattr file and let
1588 * default_removexattr do the work (including deleting
1589 * the xattr file if there are no other xattrs).
1591 * Note that we have to handle the case where the
1592 * Finder Info was already all zeroes, and we ignore
1595 * The common case where options == 0 was handled above.
1597 rel_xattrinfo(&ainfo
);
1598 close_xattrfile(xvp
, fileflags
, context
);
1599 error
= default_removexattr(vp
, name
, 0, context
);
1600 if (error
== ENOATTR
)
1604 if (ainfo
.finderinfo
) {
1605 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1606 bcopy(finfo
, attrdata
, datalen
);
1607 ainfo
.iosize
= sizeof(attr_header_t
);
1608 error
= write_xattrinfo(&ainfo
);
1615 /* Write the Resource Fork. */
1616 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1617 u_int32_t endoffset
;
1619 if (!vnode_isreg(vp
)) {
1623 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
1624 /* attr exists and "create" was specified? */
1625 if (options
& XATTR_CREATE
) {
1630 /* attr doesn't exists and "replace" was specified? */
1631 if (options
& XATTR_REPLACE
) {
1636 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1637 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1638 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1641 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1642 if (endoffset
> ainfo
.rsrcfork
->length
) {
1643 ainfo
.rsrcfork
->length
= endoffset
;
1644 ainfo
.iosize
= sizeof(attr_header_t
);
1645 error
= write_xattrinfo(&ainfo
);
1651 if (datalen
> ATTR_MAX_SIZE
) {
1652 return (E2BIG
); /* EINVAL instead ? */
1655 if (ainfo
.attrhdr
== NULL
) {
1659 header
= ainfo
.attrhdr
;
1660 entry
= ainfo
.attr_entry
;
1662 /* Check if data area crosses the maximum header size. */
1663 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1664 splitdata
= 1; /* do data I/O separately */
1669 * See if attribute already exists.
1671 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1672 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1676 entry
= ATTR_NEXT(entry
);
1680 if (options
& XATTR_CREATE
) {
1684 if (datalen
== entry
->length
) {
1686 uio_setoffset(uio
, entry
->offset
);
1687 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1688 uio_setoffset(uio
, 0);
1690 printf("setxattr: VNOP_WRITE error %d\n", error
);
1693 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1694 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1697 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1698 error
= write_xattrinfo(&ainfo
);
1700 printf("setxattr: write_xattrinfo error %d\n", error
);
1706 * Brute force approach - just remove old entry and set new entry.
1709 rel_xattrinfo(&ainfo
);
1710 close_xattrfile(xvp
, fileflags
, context
);
1711 error
= default_removexattr(vp
, name
, options
, context
);
1715 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1716 options
&= ~XATTR_REPLACE
;
1717 goto start
; /* start over */
1722 if (options
& XATTR_REPLACE
) {
1723 error
= ENOATTR
; /* nothing there to replace */
1726 /* Check if header size limit has been reached. */
1727 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1732 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1734 /* Check if we need more space. */
1735 if ((datalen
+ entrylen
) > datafreespace
) {
1738 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1740 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1741 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1742 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1745 ainfo
.filesize
+= growsize
;
1746 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1748 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1754 * Move the resource fork out of the way.
1756 if (ainfo
.rsrcfork
) {
1757 if (ainfo
.rsrcfork
->length
!= 0) {
1758 shift_data_down(xvp
,
1759 ainfo
.rsrcfork
->offset
,
1760 ainfo
.rsrcfork
->length
,
1763 ainfo
.rsrcfork
->offset
+= growsize
;
1765 ainfo
.finderinfo
->length
+= growsize
;
1766 header
->total_size
+= growsize
;
1769 /* Make space for a new entry. */
1771 shift_data_down(xvp
,
1773 header
->data_length
,
1776 bcopy((u_int8_t
*)header
+ header
->data_start
,
1777 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
1778 header
->data_length
);
1780 header
->data_start
+= entrylen
;
1782 /* Fix up entry data offsets. */
1784 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
1785 entry
->offset
+= entrylen
;
1789 * If the attribute data area is entirely within
1790 * the header buffer, then just update the buffer,
1791 * otherwise we'll write it separately to the file.
1796 /* Write new attribute data after the end of existing data. */
1797 offset
= header
->data_start
+ header
->data_length
;
1798 uio_setoffset(uio
, offset
);
1799 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1800 uio_setoffset(uio
, 0);
1802 printf("setxattr: VNOP_WRITE error %d\n", error
);
1806 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
1808 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1810 printf("setxattr: uiomove error %d\n", error
);
1815 /* Create the attribute entry. */
1816 lastentry
->length
= datalen
;
1817 lastentry
->offset
= header
->data_start
+ header
->data_length
;
1818 lastentry
->namelen
= namelen
;
1819 lastentry
->flags
= 0;
1820 bcopy(name
, &lastentry
->name
[0], namelen
);
1822 /* Update the attributes header. */
1823 header
->num_attrs
++;
1824 header
->data_length
+= datalen
;
1827 /* Only write the entries, since the data was written separately. */
1828 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
1830 /* The entry and data are both in the header; write them together. */
1831 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1833 error
= write_xattrinfo(&ainfo
);
1835 printf("setxattr: write_xattrinfo error %d\n", error
);
1839 rel_xattrinfo(&ainfo
);
1840 close_xattrfile(xvp
, fileflags
, context
);
1842 /* Touch the change time if we changed an attribute. */
1844 struct vnode_attr va
;
1846 /* Re-write the mtime to cause a ctime change. */
1848 VATTR_WANTED(&va
, va_modify_time
);
1849 if (vnode_getattr(vp
, &va
, context
) == 0) {
1851 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1852 (void) vnode_setattr(vp
, &va
, context
);
1860 * Remove an extended attribute.
1863 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
1867 attr_header_t
*header
;
1868 attr_entry_t
*entry
;
1869 attr_entry_t
*oldslot
;
1875 int found
= 0, lastone
= 0;
1883 fileflags
= FREAD
| FWRITE
;
1884 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1887 * Open the file locked (exclusive) since the Carbon
1888 * File Manager may have the Apple Double file open
1889 * and could be changing the resource fork.
1891 fileflags
|= O_EXLOCK
;
1896 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1899 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1900 close_xattrfile(xvp
, fileflags
, context
);
1904 attrcount
+= ainfo
.attrhdr
->num_attrs
;
1907 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
1910 /* Clear the Finder Info. */
1911 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1912 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1916 /* On removal of last attribute the ._ file is removed. */
1917 if (--attrcount
== 0)
1919 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1920 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
1921 ainfo
.iosize
= sizeof(attr_header_t
);
1922 error
= write_xattrinfo(&ainfo
);
1926 /* Clear the Resource Fork. */
1928 if (!vnode_isreg(vp
)) {
1932 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
1936 /* On removal of last attribute the ._ file is removed. */
1937 if (--attrcount
== 0)
1941 * If the resource fork isn't the last AppleDouble
1942 * entry then the space needs to be reclaimed by
1943 * shifting the entries after the resource fork.
1945 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
1946 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
1947 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1950 ainfo
.rsrcfork
->length
= 0;
1951 ainfo
.iosize
= sizeof(attr_header_t
);
1952 error
= write_xattrinfo(&ainfo
);
1957 if (ainfo
.attrhdr
== NULL
) {
1961 namelen
= strlen(name
) + 1;
1962 header
= ainfo
.attrhdr
;
1963 entry
= ainfo
.attr_entry
;
1966 * See if this attribute exists.
1968 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1969 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1971 if ((i
+1) == header
->num_attrs
)
1975 entry
= ATTR_NEXT(entry
);
1981 /* On removal of last attribute the ._ file is removed. */
1982 if (--attrcount
== 0)
1985 datalen
= entry
->length
;
1986 dataoff
= entry
->offset
;
1987 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1988 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
1993 /* Remove the attribute entry. */
1995 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
1996 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
1999 /* Adjust the attribute data. */
2003 dataoff
- header
->data_start
,
2009 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2013 /* XXX write zeros to freed space ? */
2014 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2018 bcopy((u_int8_t
*)header
+ header
->data_start
,
2019 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2020 dataoff
- header
->data_start
);
2022 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2023 (u_int8_t
*)header
+ dataoff
- entrylen
,
2024 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2026 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2027 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2030 /* Adjust the header values and entry offsets. */
2031 header
->num_attrs
--;
2032 header
->data_start
-= entrylen
;
2033 header
->data_length
-= datalen
;
2036 entry
= ainfo
.attr_entry
;
2037 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2038 entry
->offset
-= entrylen
;
2039 if (entry
>= oldslot
)
2040 entry
->offset
-= datalen
;
2041 entry
= ATTR_NEXT(entry
);
2043 error
= write_xattrinfo(&ainfo
);
2045 printf("removexattr: write_xattrinfo error %d\n", error
);
2048 rel_xattrinfo(&ainfo
);
2050 /* When there are no more attributes remove the ._ file. */
2051 if (attrcount
== 0) {
2052 if (fileflags
& O_EXLOCK
)
2053 (void) unlock_xattrfile(xvp
, context
);
2054 VNOP_CLOSE(xvp
, fileflags
, context
);
2056 error
= remove_xattrfile(xvp
, context
);
2059 close_xattrfile(xvp
, fileflags
, context
);
2061 /* Touch the change time if we changed an attribute. */
2063 struct vnode_attr va
;
2065 /* Re-write the mtime to cause a ctime change. */
2067 VATTR_WANTED(&va
, va_modify_time
);
2068 if (vnode_getattr(vp
, &va
, context
) == 0) {
2070 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2071 (void) vnode_setattr(vp
, &va
, context
);
2080 * Retrieve the list of extended attribute names.
2083 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2087 attr_entry_t
*entry
;
2092 * We do not zero "*size" here as we don't want to stomp a size set when
2093 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2094 * system call layer, up in listxattr or flistxattr.
2097 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2098 if (error
== ENOATTR
)
2102 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2103 close_xattrfile(xvp
, FREAD
, context
);
2107 /* Check for Finder Info. */
2108 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2110 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2111 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2115 error
= uiomove(XATTR_FINDERINFO_NAME
,
2116 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2124 /* Check for Resource Fork. */
2125 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2127 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2128 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2132 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2133 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2141 /* Check for attributes. */
2142 if (ainfo
.attrhdr
) {
2143 count
= ainfo
.attrhdr
->num_attrs
;
2144 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2145 if (xattr_protected((const char *)entry
->name
) ||
2146 xattr_validatename((const char *)entry
->name
) != 0) {
2147 entry
= ATTR_NEXT(entry
);
2151 *size
+= entry
->namelen
;
2152 entry
= ATTR_NEXT(entry
);
2155 if (uio_resid(uio
) < entry
->namelen
) {
2159 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2161 if (error
!= EFAULT
)
2165 entry
= ATTR_NEXT(entry
);
2169 rel_xattrinfo(&ainfo
);
2170 close_xattrfile(xvp
, FREAD
, context
);
2176 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2178 vnode_t xvp
= NULLVP
;
2179 vnode_t dvp
= NULLVP
;
2180 struct vnode_attr va
;
2181 struct nameidata nd
;
2183 char *filename
= NULL
;
2184 const char *basename
= NULL
;
2190 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2192 * For the root directory use "._." to hold the attributes.
2194 filename
= &smallname
[0];
2195 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2196 dvp
= vp
; /* the "._." file resides in the root dir */
2199 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2203 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2208 /* "._" Attribute files cannot have attributes */
2209 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2210 basename
[0] == '.' && basename
[1] == '_') {
2214 filename
= &smallname
[0];
2215 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2216 if (len
>= sizeof(smallname
)) {
2217 len
++; /* snprintf result doesn't include '\0' */
2218 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2219 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2222 * Note that the lookup here does not authorize. Since we are looking
2223 * up in the same directory that we already have the file vnode in,
2224 * we must have been given the file vnode legitimately. Read/write
2225 * access has already been authorized in layers above for calls from
2226 * userspace, and the authorization code using this path to read
2227 * file security from the EA must always get access
2230 NDINIT(&nd
, LOOKUP
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
, UIO_SYSSPACE
,
2231 CAST_USER_ADDR_T(filename
), context
);
2234 if (fileflags
& O_CREAT
) {
2235 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2237 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2239 if ( (error
= namei(&nd
))) {
2244 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2250 * Pick up uid/gid/mode from target file.
2253 VATTR_WANTED(&va
, va_uid
);
2254 VATTR_WANTED(&va
, va_gid
);
2255 VATTR_WANTED(&va
, va_mode
);
2256 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2257 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2258 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2259 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2262 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2263 } else /* fallback values */ {
2264 uid
= KAUTH_UID_NONE
;
2265 gid
= KAUTH_GID_NONE
;
2266 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2270 VATTR_SET(&va
, va_type
, VREG
);
2271 VATTR_SET(&va
, va_mode
, umode
);
2272 if (uid
!= KAUTH_UID_NONE
)
2273 VATTR_SET(&va
, va_uid
, uid
);
2274 if (gid
!= KAUTH_GID_NONE
)
2275 VATTR_SET(&va
, va_gid
, gid
);
2277 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &va
,
2278 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2285 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2290 if ((error
= namei(&nd
))) {
2300 if (xvp
->v_type
!= VREG
) {
2305 * Owners must match.
2308 VATTR_WANTED(&va
, va_uid
);
2309 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2310 uid_t owner
= va
.va_uid
;
2313 VATTR_WANTED(&va
, va_uid
);
2314 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2315 error
= ENOATTR
; /* don't use this "._" file */
2320 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2326 if ((error
= vnode_ref(xvp
))) {
2331 /* If create was requested, make sure file header exists. */
2332 if (fileflags
& O_CREAT
) {
2334 VATTR_WANTED(&va
, va_data_size
);
2335 VATTR_WANTED(&va
, va_fileid
);
2336 VATTR_WANTED(&va
, va_nlink
);
2337 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2342 /* If the file is empty then add a default header. */
2343 if (va
.va_data_size
== 0) {
2344 /* Don't adopt hard-linked "._" files. */
2345 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2349 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2353 /* Apply file locking if requested. */
2354 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2357 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2358 error
= lock_xattrfile(xvp
, locktype
, context
);
2361 if (dvp
&& (dvp
!= vp
)) {
2365 vnode_putname(basename
);
2367 if (filename
&& filename
!= &smallname
[0]) {
2368 FREE(filename
, M_TEMP
);
2371 if (xvp
!= NULLVP
) {
2373 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2376 (void) vnode_rele(xvp
);
2378 (void) vnode_put(xvp
);
2381 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2385 *xvpp
= xvp
; /* return a referenced vnode */
2390 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2392 // if (fileflags & FWRITE)
2393 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2395 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2396 (void) unlock_xattrfile(xvp
, context
);
2398 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2399 (void) vnode_rele(xvp
);
2400 (void) vnode_put(xvp
);
2404 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2407 struct nameidata nd
;
2412 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2416 pathlen
= MAXPATHLEN
;
2417 error
= vn_getpath(xvp
, path
, &pathlen
);
2419 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2423 NDINIT(&nd
, DELETE
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2424 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2426 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2433 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2442 * Read in and parse the AppleDouble header and entries, and the extended
2443 * attribute header and entries if any. Populates the fields of ainfop
2444 * based on the headers and entries found.
2446 * The basic idea is to:
2447 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2448 * AppleDouble entries, the extended attribute header, and extended
2449 * attribute entries must lie within this part of the file; the rest of
2450 * the AppleDouble handling code assumes this. Plus it allows us to
2451 * somewhat optimize by doing a smaller number of larger I/Os.
2452 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2454 * - Find the Finder Info and Resource Fork entries, if any.
2455 * - If we're going to be writing, try to make sure the Finder Info entry has
2456 * room to store the extended attribute header, plus some space for extended
2458 * - Swap and sanity check the extended attribute header and entries (if any).
2461 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2464 void * buffer
= NULL
;
2465 apple_double_header_t
*filehdr
;
2466 struct vnode_attr va
;
2471 bzero(ainfop
, sizeof(attr_info_t
));
2472 ainfop
->filevp
= xvp
;
2473 ainfop
->context
= context
;
2475 VATTR_WANTED(&va
, va_data_size
);
2476 VATTR_WANTED(&va
, va_fileid
);
2477 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2480 ainfop
->filesize
= va
.va_data_size
;
2482 /* When setting attributes, allow room for the header to grow. */
2484 iosize
= ATTR_MAX_HDR_SIZE
;
2486 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2492 ainfop
->iosize
= iosize
;
2493 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2494 if (buffer
== NULL
){
2499 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2500 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2502 /* Read the file header. */
2503 error
= VNOP_READ(xvp
, auio
, 0, context
);
2507 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2508 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2510 filehdr
= (apple_double_header_t
*)buffer
;
2512 error
= check_and_swap_apple_double_header(ainfop
);
2516 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2518 /* rel_xattrinfo is responsible for freeing the header buffer */
2521 /* Find the Finder Info and Resource Fork entries, if any */
2522 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2523 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2524 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2525 /* We found the Finder Info entry. */
2526 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2529 * Is the Finder Info "empty" (all zeroes)? If so,
2530 * we'll pretend like the Finder Info extended attribute
2533 * Note: we have to make sure the Finder Info is
2534 * contained within the buffer we have already read,
2535 * to avoid accidentally accessing a bogus address.
2536 * If it is outside the buffer, we just assume the
2537 * Finder Info is non-empty.
2539 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2540 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2541 ainfop
->emptyfinderinfo
= 1;
2544 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2546 * Ignore zero-length resource forks when getting. If setting,
2547 * we need to remember the resource fork entry so it can be
2548 * updated once the new content has been written.
2550 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2554 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2556 * The "empty" resource headers we created have a system data tag of:
2557 * "This resource fork intentionally left blank "
2559 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2561 u_int8_t systemData
[64];
2565 /* Read the system data which starts at byte 16 */
2566 rf_uio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2567 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2568 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2569 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2573 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2574 continue; /* skip this resource fork */
2577 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2578 if (i
!= (filehdr
->numEntries
- 1)) {
2579 printf("get_xattrinfo: resource fork not last entry\n");
2580 ainfop
->readonly
= 1;
2587 * See if this file looks like it is laid out correctly to contain
2588 * extended attributes. If so, then do the following:
2590 * - If we're going to be writing, try to make sure the Finder Info
2591 * entry has room to store the extended attribute header, plus some
2592 * space for extended attributes.
2594 * - Swap and sanity check the extended attribute header and entries
2597 if (filehdr
->numEntries
== 2 &&
2598 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2599 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2600 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2601 attr_header_t
*attrhdr
;
2602 attrhdr
= (attr_header_t
*)filehdr
;
2604 * If we're going to be writing, try to make sure the Finder
2605 * Info entry has room to store the extended attribute header,
2606 * plus some space for extended attributes.
2608 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2612 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2613 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2614 /* Make some room before existing resource fork. */
2615 shift_data_down(xvp
,
2616 filehdr
->entries
[1].offset
,
2617 filehdr
->entries
[1].length
,
2619 writesize
= sizeof(attr_header_t
);
2621 /* Create a new, empty resource fork. */
2622 rsrcfork_header_t
*rsrcforkhdr
;
2624 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2626 /* Steal some space for an empty RF header. */
2627 delta
-= sizeof(rsrcfork_header_t
);
2629 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2630 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2632 /* Fill in Empty Resource Fork Header. */
2633 init_empty_resource_fork(rsrcforkhdr
);
2635 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2636 writesize
= ATTR_BUF_SIZE
;
2638 filehdr
->entries
[0].length
+= delta
;
2639 filehdr
->entries
[1].offset
+= delta
;
2641 /* Fill in Attribute Header. */
2642 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2643 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2644 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2645 attrhdr
->data_start
= sizeof(attr_header_t
);
2646 attrhdr
->data_length
= 0;
2647 attrhdr
->reserved
[0] = 0;
2648 attrhdr
->reserved
[1] = 0;
2649 attrhdr
->reserved
[2] = 0;
2651 attrhdr
->num_attrs
= 0;
2653 /* Push out new header */
2654 uio_reset(auio
, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2655 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2657 swap_adhdr(filehdr
); /* to big endian */
2658 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2659 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2660 swap_adhdr(filehdr
); /* back to native */
2661 /* The attribute header gets swapped below. */
2665 * Swap and sanity check the extended attribute header and
2666 * entries (if any). The Finder Info content must be big enough
2667 * to include the extended attribute header; if not, we just
2670 * Note that we're passing the offset + length (i.e. the end)
2671 * of the Finder Info instead of rawsize to validate_attrhdr.
2672 * This ensures that all extended attributes lie within the
2673 * Finder Info content according to the AppleDouble entry.
2675 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2678 if (ainfop
->finderinfo
&&
2679 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2680 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2681 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2683 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2684 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2685 /* First attr_entry starts immediately following attribute header */
2686 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2695 FREE(buffer
, M_TEMP
);
2701 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2704 rsrcfork_header_t
*rsrcforkhdr
;
2710 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2711 bzero(buffer
, ATTR_BUF_SIZE
);
2713 xah
= (attr_header_t
*)buffer
;
2714 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2715 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2716 rsrcforksize
= sizeof(rsrcfork_header_t
);
2717 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2719 /* Fill in Apple Double Header. */
2720 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2721 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2722 xah
->appledouble
.numEntries
= SWAP16 (2);
2723 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2724 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2725 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2726 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2727 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2728 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2729 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2731 /* Fill in Attribute Header. */
2732 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2733 xah
->debug_tag
= SWAP32 (fileid
);
2734 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2735 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2737 /* Fill in Empty Resource Fork Header. */
2738 init_empty_resource_fork(rsrcforkhdr
);
2741 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2744 FREE(buffer
, M_TEMP
);
2750 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
2752 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
2753 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2754 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2755 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2756 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2757 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2758 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2759 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
2760 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
2761 rsrcforkhdr
->typeCount
= SWAP16 (-1);
2762 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
2766 rel_xattrinfo(attr_info_t
*ainfop
)
2768 FREE(ainfop
->filehdr
, M_TEMP
);
2769 bzero(ainfop
, sizeof(attr_info_t
));
2773 write_xattrinfo(attr_info_t
*ainfop
)
2778 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2779 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
2781 swap_adhdr(ainfop
->filehdr
);
2782 if (ainfop
->attrhdr
!= NULL
) {
2783 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2786 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
2788 swap_adhdr(ainfop
->filehdr
);
2789 if (ainfop
->attrhdr
!= NULL
) {
2790 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2797 #if BYTE_ORDER == LITTLE_ENDIAN
2799 * Endian swap apple double header
2802 swap_adhdr(apple_double_header_t
*adh
)
2807 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
2809 adh
->magic
= SWAP32 (adh
->magic
);
2810 adh
->version
= SWAP32 (adh
->version
);
2811 adh
->numEntries
= SWAP16 (adh
->numEntries
);
2813 for (i
= 0; i
< count
; i
++) {
2814 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
2815 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
2816 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
2821 * Endian swap extended attributes header
2824 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
2830 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
2832 ah
->magic
= SWAP32 (ah
->magic
);
2833 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
2834 ah
->total_size
= SWAP32 (ah
->total_size
);
2835 ah
->data_start
= SWAP32 (ah
->data_start
);
2836 ah
->data_length
= SWAP32 (ah
->data_length
);
2837 ah
->flags
= SWAP16 (ah
->flags
);
2838 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
2840 ae
= (attr_entry_t
*)(&ah
[1]);
2841 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
2842 ae
->offset
= SWAP32 (ae
->offset
);
2843 ae
->length
= SWAP32 (ae
->length
);
2844 ae
->flags
= SWAP16 (ae
->flags
);
2850 * Validate and swap the attributes header contents, and each attribute's
2853 * Note: Assumes the caller has verified that the Finder Info content is large
2854 * enough to contain the attr_header structure itself. Therefore, we can
2855 * swap the header fields before sanity checking them.
2858 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
2869 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
2872 /* Swap the basic header fields */
2873 ah
->magic
= SWAP32(ah
->magic
);
2874 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
2875 ah
->total_size
= SWAP32 (ah
->total_size
);
2876 ah
->data_start
= SWAP32 (ah
->data_start
);
2877 ah
->data_length
= SWAP32 (ah
->data_length
);
2878 ah
->flags
= SWAP16 (ah
->flags
);
2879 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
2882 * Make sure the total_size fits within the Finder Info area, and the
2883 * extended attribute data area fits within total_size.
2885 end
= ah
->data_start
+ ah
->data_length
;
2886 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
2887 end
< ah
->data_start
||
2888 end
> ah
->total_size
) {
2893 * Make sure each of the attr_entry_t's fits within total_size.
2895 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
2896 count
= ah
->num_attrs
;
2897 ae
= (attr_entry_t
*)(&ah
[1]);
2899 for (i
=0; i
<count
; i
++) {
2900 /* Make sure the fixed-size part of this attr_entry_t fits. */
2901 if ((u_int8_t
*) &ae
[1] > buf_end
)
2904 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
2905 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
2906 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
2909 /* Swap the attribute entry fields */
2910 ae
->offset
= SWAP32(ae
->offset
);
2911 ae
->length
= SWAP32(ae
->length
);
2912 ae
->flags
= SWAP16(ae
->flags
);
2914 /* Make sure the attribute content fits. */
2915 end
= ae
->offset
+ ae
->length
;
2916 if (end
< ae
->offset
|| end
> ah
->total_size
)
2923 * TODO: Make sure the contents of attributes don't overlap the header
2924 * and don't overlap each other. The hard part is that we don't know
2925 * what the actual header size is until we have looped over all of the
2926 * variable-sized attribute entries.
2928 * XXX Is there any guarantee that attribute entries are stored in
2929 * XXX order sorted by the contents' file offset? If so, that would
2930 * XXX make the pairwise overlap check much easier.
2937 // "start" & "end" are byte offsets in the file.
2938 // "to" is the byte offset we want to move the
2939 // data to. "to" should be > "start".
2941 // we do the copy backwards to avoid problems if
2942 // there's an overlap.
2945 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
2948 size_t chunk
, orig_chunk
;
2951 ucred_t ucred
= vfs_context_ucred(context
);
2952 proc_t p
= vfs_context_proc(context
);
2954 if (delta
== 0 || len
== 0) {
2964 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
2968 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
2969 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
2971 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
2972 pos
, ret
, chunk
, ret
);
2976 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
2978 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
2979 pos
+delta
, ret
, chunk
, ret
);
2983 if ((pos
- chunk
) < start
) {
2984 chunk
= pos
- start
;
2986 if (chunk
== 0) { // we're all done
2991 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
2998 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3001 size_t chunk
, orig_chunk
;
3005 ucred_t ucred
= vfs_context_ucred(context
);
3006 proc_t p
= vfs_context_proc(context
);
3008 if (delta
== 0 || len
== 0) {
3019 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3023 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3024 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3026 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3027 pos
, ret
, chunk
, ret
);
3031 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3033 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3034 pos
+delta
, ret
, chunk
, ret
);
3038 if ((pos
+ chunk
) > end
) {
3041 if (chunk
== 0) { // we're all done
3046 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3052 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3057 lf
.l_whence
= SEEK_SET
;
3060 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3061 /* Note: id is just a kernel address that's not a proc */
3062 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
);
3063 return (error
== ENOTSUP
? 0 : error
);
3067 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3072 lf
.l_whence
= SEEK_SET
;
3075 lf
.l_type
= F_UNLCK
;
3076 /* Note: id is just a kernel address that's not a proc */
3077 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);
3078 return (error
== ENOTSUP
? 0 : error
);