2 * Copyright (c) 2004-2007 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 it's parent. */
404 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
411 * Make a named stream for vnode vp.
414 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
418 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
419 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
421 error
= default_makenamedstream(vp
, svpp
, name
, context
);
428 svp
->v_flag
|= VISNAMEDSTREAM
;
430 /* Make the file it's parent. */
431 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
437 * Remove a named stream from vnode vp.
440 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
444 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
445 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
447 error
= default_removenamedstream(vp
, name
, context
);
452 #define NS_IOBUFSIZE (128 * 1024)
455 * Release a named stream shadow file.
458 vnode_relenamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
461 struct componentname cn
;
465 if (vnode_isinuse(svp
, 1)) {
471 MAKE_SHADOW_NAME(vp
, tmpname
);
474 cn
.cn_nameiop
= DELETE
;
475 cn
.cn_flags
= ISLASTCN
;
476 cn
.cn_context
= context
;
477 cn
.cn_pnbuf
= tmpname
;
478 cn
.cn_pnlen
= sizeof(tmpname
);
479 cn
.cn_nameptr
= cn
.cn_pnbuf
;
480 cn
.cn_namelen
= strlen(tmpname
);
482 /* Obtain the vnode for the shadow files directory. */
483 err
= get_shadow_dir(&dvp
, context
);
487 /* Check for busy svp one last time. */
488 if (vnode_isinuse(svp
, 1) == 0) {
489 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, context
);
490 (void) vnode_recycle(svp
);
498 * Flush a named stream shadow file.
501 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
503 struct vnode_attr va
;
505 caddr_t bufptr
= NULL
;
513 VATTR_WANTED(&va
, va_data_size
);
514 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
515 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
518 datasize
= va
.va_data_size
;
519 if ((datasize
== 0)) {
520 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
524 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
525 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
528 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
532 * Copy the shadow stream file data into the resource fork.
534 error
= VNOP_OPEN(svp
, 0, context
);
536 printf("vnode_flushnamedstream: err %d opening file\n", error
);
539 while (offset
< datasize
) {
540 iosize
= MIN(datasize
- offset
, iosize
);
542 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
543 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
544 error
= VNOP_READ(svp
, auio
, 0, context
);
548 /* Since there's no truncate xattr we must remove the resource fork. */
550 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
551 if ((error
!= 0) && (error
!= ENOATTR
)) {
555 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_WRITE
);
556 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
557 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
563 (void) VNOP_CLOSE(svp
, 0, context
);
566 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
576 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
577 int *creator
, vfs_context_t context
)
579 vnode_t dvp
= NULLVP
;
580 vnode_t svp
= NULLVP
;
581 struct componentname cn
;
582 struct vnode_attr va
;
589 /* Establish a unique file name. */
590 MAKE_SHADOW_NAME(vp
, tmpname
);
591 bzero(&cn
, sizeof(cn
));
592 cn
.cn_nameiop
= LOOKUP
;
593 cn
.cn_flags
= ISLASTCN
;
594 cn
.cn_context
= context
;
595 cn
.cn_pnbuf
= tmpname
;
596 cn
.cn_pnlen
= sizeof(tmpname
);
597 cn
.cn_nameptr
= cn
.cn_pnbuf
;
598 cn
.cn_namelen
= strlen(tmpname
);
600 /* Pick up uid, gid, mode and date from original file. */
602 VATTR_WANTED(&va
, va_uid
);
603 VATTR_WANTED(&va
, va_gid
);
604 VATTR_WANTED(&va
, va_mode
);
605 VATTR_WANTED(&va
, va_create_time
);
606 VATTR_WANTED(&va
, va_modify_time
);
607 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
608 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
609 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
610 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
611 va
.va_uid
= KAUTH_UID_NONE
;
612 va
.va_gid
= KAUTH_GID_NONE
;
613 va
.va_mode
= S_IRUSR
| S_IWUSR
;
615 va
.va_vaflags
= VA_EXCLUSIVE
;
616 VATTR_SET(&va
, va_type
, VREG
);
617 /* We no longer change the access, but we still hide it. */
618 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
620 /* Obtain the vnode for the shadow files directory. */
621 if (get_shadow_dir(&dvp
, context
) != 0) {
626 /* See if someone else already has it open. */
627 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, context
) == 0) {
628 /* Double check existence by asking for size. */
630 VATTR_WANTED(&va
, va_data_size
);
631 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
632 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
633 goto out
; /* OK to use. */
637 /* Otherwise make sure the resource fork data exists. */
638 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
639 XATTR_NOSECURITY
, context
);
641 * To maintain binary compatibility with legacy Carbon
642 * emulated resource fork support, if the resource fork
643 * doesn't exist but the Finder Info does, then act as
644 * if an empty resource fork is present (see 4724359).
646 if ((error
== ENOATTR
) &&
647 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
648 XATTR_NOSECURITY
, context
) == 0)) {
656 /* If the resource fork exists, its size is expected to be non-zero. */
663 /* Create the shadow stream file. */
664 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, context
);
667 } else if ((error
== EEXIST
) && !makestream
) {
668 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, context
);
675 /* On errors, clean up shadow stream file. */
683 *rsrcsize
= datasize
;
690 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
692 vnode_t svp
= NULLVP
;
694 caddr_t bufptr
= NULL
;
701 * Only the "com.apple.ResourceFork" stream is supported here.
703 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
709 * Obtain a shadow file for the resource fork I/O.
711 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
718 * The creator of the shadow file provides its file data,
719 * all other threads should wait until its ready.
723 if (svp
->v_flag
& VISNAMEDSTREAM
) {
724 /* data is ready, go use it */
728 /* its not ready, wait for it (sleep using v_parent as channel) */
729 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
730 "getnamedstream", NULL
);
738 * Copy the real resource fork data into shadow stream file.
740 if (op
== NS_OPEN
&& datasize
!= 0) {
744 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
745 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
750 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
753 error
= VNOP_OPEN(svp
, 0, context
);
757 while (offset
< datasize
) {
760 iosize
= MIN(datasize
- offset
, iosize
);
762 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
763 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
764 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
765 XATTR_NOSECURITY
, context
);
770 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_WRITE
);
771 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
772 error
= VNOP_WRITE(svp
, auio
, 0, context
);
778 (void) VNOP_CLOSE(svp
, 0, context
);
781 /* Wake up anyone waiting for svp file content */
785 svp
->v_flag
|= VISNAMEDSTREAM
;
786 wakeup((caddr_t
)&svp
->v_parent
);
789 /* On post create errors, get rid of shadow file. */
790 (void)vnode_relenamedstream(vp
, svp
, context
);
792 wakeup((caddr_t
)&svp
->v_parent
);
797 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
803 /* On errors, clean up shadow stream file. */
814 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
820 * Only the "com.apple.ResourceFork" stream is supported here.
822 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
826 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
829 * Wake up any waiters over in default_getnamedstream().
831 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
835 svp
->v_flag
|= VISNAMEDSTREAM
;
836 /* Wakeup any waiters on the v_parent channel */
837 wakeup((caddr_t
)&svp
->v_parent
);
844 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
847 * Only the "com.apple.ResourceFork" stream is supported here.
849 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
853 * XXX - what about other opened instances?
855 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
859 get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
)
861 vnode_t dvp
= NULLVP
;
862 vnode_t sdvp
= NULLVP
;
863 struct componentname cn
;
864 struct vnode_attr va
;
869 /* Check if we've already created it. */
870 if (shadow_dvp
!= NULLVP
) {
871 if ((error
= vnode_getwithvid(shadow_dvp
, shadow_vid
))) {
879 /* Obtain the vnode for "/tmp" directory. */
880 if (vnode_lookup("/tmp", 0, &dvp
, context
) != 0) {
885 /* Create the shadow stream directory. */
886 snprintf(tmpname
, sizeof(tmpname
), ".vfs_rsrc_streams_%x%x",
887 (unsigned int)rootvnode
, shadow_sequence
);
888 bzero(&cn
, sizeof(cn
));
889 cn
.cn_nameiop
= LOOKUP
;
890 cn
.cn_flags
= ISLASTCN
;
891 cn
.cn_context
= context
;
892 cn
.cn_pnbuf
= tmpname
;
893 cn
.cn_pnlen
= sizeof(tmpname
);
894 cn
.cn_nameptr
= cn
.cn_pnbuf
;
895 cn
.cn_namelen
= strlen(tmpname
);
898 * owned by root, only readable by root, hidden
901 VATTR_SET(&va
, va_uid
, 0);
902 VATTR_SET(&va
, va_gid
, 0);
903 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
904 VATTR_SET(&va
, va_type
, VDIR
);
905 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
906 va
.va_vaflags
= VA_EXCLUSIVE
;
908 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, context
);
911 * There can be only one winner for an exclusive create.
914 /* Take a long term ref to keep this dir around. */
915 error
= vnode_ref(sdvp
);
918 shadow_vid
= sdvp
->v_id
;
920 } else if (error
== EEXIST
) {
921 /* loser has to look up directory */
922 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, context
);
924 /* Make sure its in fact a directory */
925 if (sdvp
->v_type
!= VDIR
) {
928 /* Obtain the fsid for /tmp directory */
930 VATTR_WANTED(&va
, va_fsid
);
931 if (VNOP_GETATTR(dvp
, &va
, context
) != 0 ||
932 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
935 tmp_fsid
= va
.va_fsid
;
938 VATTR_WANTED(&va
, va_uid
);
939 VATTR_WANTED(&va
, va_gid
);
940 VATTR_WANTED(&va
, va_mode
);
941 VATTR_WANTED(&va
, va_fsid
);
942 VATTR_WANTED(&va
, va_dirlinkcount
);
943 VATTR_WANTED(&va
, va_acl
);
944 /* Provide defaults for attrs that may not be supported */
945 va
.va_dirlinkcount
= 1;
946 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
948 if (VNOP_GETATTR(sdvp
, &va
, context
) != 0 ||
949 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
950 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
951 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
952 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
956 * Make sure its what we want:
958 * - not writable by anyone
959 * - on same file system as /tmp
960 * - not a hard-linked directory
961 * - no ACLs (they might grant write access)
963 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
964 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
965 (va
.va_fsid
!= tmp_fsid
) ||
966 (va
.va_dirlinkcount
!= 1) ||
967 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
977 /* On errors, clean up shadow stream directory. */
987 /* This is not the dir we're looking for, move along */
988 ++shadow_sequence
; /* try something else next time */
997 * Default Implementation (Non-native EA)
1002 Typical "._" AppleDouble Header File layout:
1003 ------------------------------------------------------------
1008 .-- AD ENTRY[0] Finder Info Entry (must be first)
1009 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1011 | ///////////// Fixed Size Data (32 bytes)
1015 | ATTR ENTRY[1] --+--.
1016 | ATTR ENTRY[2] --+--+--.
1018 | ATTR ENTRY[N] --+--+--+--.
1019 | ATTR DATA 0 <-' | | |
1020 | //////////// | | |
1021 | ATTR DATA 1 <----' | |
1023 | ATTR DATA 2 <-------' |
1026 | ATTR DATA N <----------'
1028 | Attribute Free Space
1030 '----> RESOURCE FORK
1031 ///////////// Variable Sized Data
1040 ------------------------------------------------------------
1042 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1043 stored as part of the Finder Info. The length in the Finder
1044 Info AppleDouble entry includes the length of the extended
1045 attribute header, attribute entries, and attribute data.
1050 * On Disk Data Structures
1052 * Note: Motorola 68K alignment and big-endian.
1054 * See RFC 1740 for additional information about the AppleDouble file format.
1058 #define ADH_MAGIC 0x00051607
1059 #define ADH_VERSION 0x00020000
1060 #define ADH_MACOSX "Mac OS X "
1063 * AppleDouble Entry ID's
1065 #define AD_DATA 1 /* Data fork */
1066 #define AD_RESOURCE 2 /* Resource fork */
1067 #define AD_REALNAME 3 /* FileÕs name on home file system */
1068 #define AD_COMMENT 4 /* Standard Mac comment */
1069 #define AD_ICONBW 5 /* Mac black & white icon */
1070 #define AD_ICONCOLOR 6 /* Mac color icon */
1071 #define AD_UNUSED 7 /* Not used */
1072 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1073 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1074 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1075 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1076 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1077 #define AD_AFPNAME 13 /* Short name on AFP server */
1078 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1079 #define AD_AFPDIRID 15 /* AFP directory ID */
1080 #define AD_ATTRIBUTES AD_FINDERINFO
1083 #define ATTR_FILE_PREFIX "._"
1084 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1086 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1088 /* Implementation Limits */
1089 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
1090 #define ATTR_MAX_HDR_SIZE 65536
1092 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1093 * size supported (including the attribute entries). All of
1094 * the attribute entries must reside within this limit. If
1095 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1096 * boundry, then all of the attribute data I/O is performed
1097 * separately from the attribute header I/O.
1099 * In particular, all of the attr_entry structures must lie
1100 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1101 * AppleDouble file. However, the attribute data (i.e. the
1102 * contents of the extended attributes) may extend beyond the
1103 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1104 * limit is to allow the implementation to optimize by reading
1105 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1109 #pragma options align=mac68k
1111 #define FINDERINFOSIZE 32
1113 typedef struct apple_double_entry
{
1114 u_int32_t type
; /* entry type: see list, 0 invalid */
1115 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1116 u_int32_t length
; /* entry data length in bytes. */
1117 } apple_double_entry_t
;
1120 typedef struct apple_double_header
{
1121 u_int32_t magic
; /* == ADH_MAGIC */
1122 u_int32_t version
; /* format version: 2 = 0x00020000 */
1123 u_int32_t filler
[4];
1124 u_int16_t numEntries
; /* number of entries which follow */
1125 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1126 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1127 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1128 } apple_double_header_t
;
1130 #define ADHDRSIZE (4+4+16+2)
1132 /* Entries are aligned on 4 byte boundaries */
1133 typedef struct attr_entry
{
1134 u_int32_t offset
; /* file offset to data */
1135 u_int32_t length
; /* size of attribute data */
1138 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1142 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1143 typedef struct attr_header
{
1144 apple_double_header_t appledouble
;
1145 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1146 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1147 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1148 u_int32_t data_start
; /* file offset to attribute data area */
1149 u_int32_t data_length
; /* length of attribute data area */
1150 u_int32_t reserved
[3];
1152 u_int16_t num_attrs
;
1156 /* Empty Resource Fork Header */
1157 typedef struct rsrcfork_header
{
1158 u_int32_t fh_DataOffset
;
1159 u_int32_t fh_MapOffset
;
1160 u_int32_t fh_DataLength
;
1161 u_int32_t fh_MapLength
;
1162 u_int8_t systemData
[112];
1163 u_int8_t appData
[128];
1164 u_int32_t mh_DataOffset
;
1165 u_int32_t mh_MapOffset
;
1166 u_int32_t mh_DataLength
;
1167 u_int32_t mh_MapLength
;
1169 u_int16_t mh_RefNum
;
1171 u_int8_t mh_InMemoryAttr
;
1174 u_int16_t typeCount
;
1175 } rsrcfork_header_t
;
1177 #define RF_FIRST_RESOURCE 256
1178 #define RF_NULL_MAP_LENGTH 30
1179 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1181 #pragma options align=reset
1183 /* Runtime information about the attribute file. */
1184 typedef struct attr_info
{
1185 vfs_context_t context
;
1190 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1191 apple_double_header_t
*filehdr
;
1192 apple_double_entry_t
*finderinfo
;
1193 apple_double_entry_t
*rsrcfork
;
1194 attr_header_t
*attrhdr
;
1195 attr_entry_t
*attr_entry
;
1197 u_int8_t emptyfinderinfo
;
1201 #define ATTR_SETTING 1
1203 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1205 #define ATTR_ENTRY_LENGTH(namelen) \
1206 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1208 #define ATTR_NEXT(ae) \
1209 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1211 #define ATTR_VALID(ae, ai) \
1212 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1214 #define SWAP16(x) OSSwapBigToHostInt16((x))
1215 #define SWAP32(x) OSSwapBigToHostInt32((x))
1216 #define SWAP64(x) OSSwapBigToHostInt64((x))
1219 static u_int32_t emptyfinfo
[8] = {0};
1223 * Local support routines
1225 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1227 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1229 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1231 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1233 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1235 static void rel_xattrinfo(attr_info_t
*ainfop
);
1237 static int write_xattrinfo(attr_info_t
*ainfop
);
1239 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1241 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1243 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1246 #if BYTE_ORDER == LITTLE_ENDIAN
1247 static void swap_adhdr(apple_double_header_t
*adh
);
1248 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1251 #define swap_adhdr(x)
1252 #define swap_attrhdr(x, y)
1255 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1256 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1257 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1261 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1262 * is in big endian (as it would exist on disk). Verifies the following:
1265 * - number of entries
1266 * - that each entry fits within the file size
1268 * If the header is invalid, ENOATTR is returned.
1270 * NOTE: Does not attempt to validate the extended attributes header that
1271 * may be embedded in the Finder Info entry.
1273 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1276 u_int32_t header_end
;
1277 u_int32_t entry_end
;
1279 apple_double_header_t
*header
;
1281 rawsize
= ainfop
->rawsize
;
1282 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1284 /* Is the file big enough to contain an AppleDouble header? */
1285 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1288 /* Swap the AppleDouble header fields to native order */
1289 header
->magic
= SWAP32(header
->magic
);
1290 header
->version
= SWAP32(header
->version
);
1291 header
->numEntries
= SWAP16(header
->numEntries
);
1293 /* Sanity check the AppleDouble header fields */
1294 if (header
->magic
!= ADH_MAGIC
||
1295 header
->version
!= ADH_VERSION
||
1296 header
->numEntries
< 1 ||
1297 header
->numEntries
> 15) {
1301 /* Calculate where the entries[] array ends */
1302 header_end
= offsetof(apple_double_header_t
, entries
) +
1303 header
->numEntries
* sizeof(apple_double_entry_t
);
1305 /* Is the file big enough to contain the AppleDouble entries? */
1306 if (rawsize
< header_end
) {
1310 /* Swap and sanity check each AppleDouble entry */
1311 for (i
=0; i
<header
->numEntries
; i
++) {
1312 /* Swap the per-entry fields to native order */
1313 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1314 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1315 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1317 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1320 * Does the entry's content start within the header itself,
1321 * did the addition overflow, or does the entry's content
1322 * extend past the end of the file?
1324 if (header
->entries
[i
].offset
< header_end
||
1325 entry_end
< header
->entries
[i
].offset
||
1326 entry_end
> ainfop
->filesize
) {
1331 * Does the current entry's content overlap with a previous
1334 * Yes, this is O(N**2), and there are more efficient algorithms
1335 * for testing pairwise overlap of N ranges when N is large.
1336 * But we have already ensured N < 16, and N is almost always 2.
1337 * So there's no point in using a more complex algorithm.
1340 for (j
=0; j
<i
; j
++) {
1341 if (entry_end
> header
->entries
[j
].offset
&&
1342 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1354 * Retrieve the data of an extended attribute.
1357 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1358 __unused
int options
, vfs_context_t context
)
1362 attr_header_t
*header
;
1363 attr_entry_t
*entry
;
1373 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1376 * Open the file locked (shared) since the Carbon
1377 * File Manager may have the Apple Double file open
1378 * and could be changing the resource fork.
1380 fileflags
|= O_SHLOCK
;
1385 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1388 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1389 close_xattrfile(xvp
, fileflags
, context
);
1393 /* Get the Finder Info. */
1394 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1396 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1398 } else if (uio
== NULL
) {
1399 *size
= FINDERINFOSIZE
;
1401 } else if (uio_offset(uio
) != 0) {
1403 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1406 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1407 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1412 /* Read the Resource Fork. */
1414 if (!vnode_isreg(vp
)) {
1416 } else if (ainfo
.rsrcfork
== NULL
) {
1418 } else if (uio
== NULL
) {
1419 *size
= (size_t)ainfo
.rsrcfork
->length
;
1421 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1422 error
= VNOP_READ(xvp
, uio
, 0, context
);
1424 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1429 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1433 if (uio_offset(uio
) != 0) {
1438 namelen
= strlen(name
) + 1;
1439 header
= ainfo
.attrhdr
;
1440 entry
= ainfo
.attr_entry
;
1442 * Search for attribute name in the header.
1444 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1445 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1446 datalen
= (size_t)entry
->length
;
1452 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1456 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1457 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1458 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1460 uio_setoffset(uio
, entry
->offset
);
1461 error
= VNOP_READ(xvp
, uio
, 0, context
);
1462 uio_setoffset(uio
, 0);
1466 entry
= ATTR_NEXT(entry
);
1469 rel_xattrinfo(&ainfo
);
1470 close_xattrfile(xvp
, fileflags
, context
);
1476 * Set the data of an extended attribute.
1479 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1483 attr_header_t
*header
;
1484 attr_entry_t
*entry
;
1485 attr_entry_t
*lastentry
;
1489 size_t datafreespace
;
1496 char finfo
[FINDERINFOSIZE
];
1498 datalen
= uio_resid(uio
);
1499 namelen
= strlen(name
) + 1;
1500 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1503 * By convention, Finder Info that is all zeroes is equivalent to not
1504 * having a Finder Info EA. So if we're trying to set the Finder Info
1505 * to all zeroes, then delete it instead. If a file didn't have an
1506 * AppleDouble file before, this prevents creating an AppleDouble file
1507 * with no useful content.
1509 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1510 * for all zeroes Finder Info before opening the AppleDouble file.
1511 * But if either of those options were specified, we need to open the
1512 * AppleDouble file to see whether there was already Finder Info (so we
1513 * can return an error if needed); this case is handled further below.
1515 * NOTE: this copies the Finder Info data into the "finfo" local.
1517 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1519 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1520 * That means we probably have to open_xattrfile and get_xattrinfo.
1522 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1525 error
= uiomove(finfo
, datalen
, uio
);
1528 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1529 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1530 error
= default_removexattr(vp
, name
, 0, context
);
1531 if (error
== ENOATTR
)
1539 * Open the file locked since setting an attribute
1540 * can change the layout of the Apple Double file.
1542 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1543 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1546 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1547 close_xattrfile(xvp
, fileflags
, context
);
1551 /* Set the Finder Info. */
1552 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1553 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1554 /* attr exists and "create" was specified? */
1555 if (options
& XATTR_CREATE
) {
1560 /* attr doesn't exists and "replace" was specified? */
1561 if (options
& XATTR_REPLACE
) {
1566 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1568 * Setting the Finder Info to all zeroes is equivalent to
1569 * removing it. Close the xattr file and let
1570 * default_removexattr do the work (including deleting
1571 * the xattr file if there are no other xattrs).
1573 * Note that we have to handle the case where the
1574 * Finder Info was already all zeroes, and we ignore
1577 * The common case where options == 0 was handled above.
1579 rel_xattrinfo(&ainfo
);
1580 close_xattrfile(xvp
, fileflags
, context
);
1581 error
= default_removexattr(vp
, name
, 0, context
);
1582 if (error
== ENOATTR
)
1586 if (ainfo
.finderinfo
) {
1587 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1588 bcopy(finfo
, attrdata
, datalen
);
1589 ainfo
.iosize
= sizeof(attr_header_t
);
1590 error
= write_xattrinfo(&ainfo
);
1597 /* Write the Resource Fork. */
1598 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1599 u_int32_t endoffset
;
1601 if (!vnode_isreg(vp
)) {
1605 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
1606 /* attr exists and "create" was specified? */
1607 if (options
& XATTR_CREATE
) {
1612 /* attr doesn't exists and "replace" was specified? */
1613 if (options
& XATTR_REPLACE
) {
1618 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1619 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1620 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1623 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1624 if (endoffset
> ainfo
.rsrcfork
->length
) {
1625 ainfo
.rsrcfork
->length
= endoffset
;
1626 ainfo
.iosize
= sizeof(attr_header_t
);
1627 error
= write_xattrinfo(&ainfo
);
1633 if (datalen
> ATTR_MAX_SIZE
) {
1634 return (E2BIG
); /* EINVAL instead ? */
1637 if (ainfo
.attrhdr
== NULL
) {
1641 header
= ainfo
.attrhdr
;
1642 entry
= ainfo
.attr_entry
;
1644 /* Check if data area crosses the maximum header size. */
1645 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1646 splitdata
= 1; /* do data I/O separately */
1651 * See if attribute already exists.
1653 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1654 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1658 entry
= ATTR_NEXT(entry
);
1662 if (options
& XATTR_CREATE
) {
1666 if (datalen
== entry
->length
) {
1668 uio_setoffset(uio
, entry
->offset
);
1669 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1670 uio_setoffset(uio
, 0);
1672 printf("setxattr: VNOP_WRITE error %d\n", error
);
1675 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1676 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1679 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1680 error
= write_xattrinfo(&ainfo
);
1682 printf("setxattr: write_xattrinfo error %d\n", error
);
1688 * Brute force approach - just remove old entry and set new entry.
1691 rel_xattrinfo(&ainfo
);
1692 close_xattrfile(xvp
, fileflags
, context
);
1693 error
= default_removexattr(vp
, name
, options
, context
);
1697 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1698 options
&= ~XATTR_REPLACE
;
1699 goto start
; /* start over */
1704 if (options
& XATTR_REPLACE
) {
1705 error
= ENOATTR
; /* nothing there to replace */
1708 /* Check if header size limit has been reached. */
1709 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1714 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1716 /* Check if we need more space. */
1717 if ((datalen
+ entrylen
) > datafreespace
) {
1720 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1722 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1723 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1724 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1727 ainfo
.filesize
+= growsize
;
1728 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1730 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1736 * Move the resource fork out of the way.
1738 if (ainfo
.rsrcfork
) {
1739 if (ainfo
.rsrcfork
->length
!= 0) {
1740 shift_data_down(xvp
,
1741 ainfo
.rsrcfork
->offset
,
1742 ainfo
.rsrcfork
->length
,
1745 ainfo
.rsrcfork
->offset
+= growsize
;
1747 ainfo
.finderinfo
->length
+= growsize
;
1748 header
->total_size
+= growsize
;
1751 /* Make space for a new entry. */
1753 shift_data_down(xvp
,
1755 header
->data_length
,
1758 bcopy((u_int8_t
*)header
+ header
->data_start
,
1759 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
1760 header
->data_length
);
1762 header
->data_start
+= entrylen
;
1764 /* Fix up entry data offsets. */
1766 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
1767 entry
->offset
+= entrylen
;
1771 * If the attribute data area is entirely within
1772 * the header buffer, then just update the buffer,
1773 * otherwise we'll write it separately to the file.
1778 /* Write new attribute data after the end of existing data. */
1779 offset
= header
->data_start
+ header
->data_length
;
1780 uio_setoffset(uio
, offset
);
1781 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1782 uio_setoffset(uio
, 0);
1784 printf("setxattr: VNOP_WRITE error %d\n", error
);
1788 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
1790 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1792 printf("setxattr: uiomove error %d\n", error
);
1797 /* Create the attribute entry. */
1798 lastentry
->length
= datalen
;
1799 lastentry
->offset
= header
->data_start
+ header
->data_length
;
1800 lastentry
->namelen
= namelen
;
1801 lastentry
->flags
= 0;
1802 bcopy(name
, &lastentry
->name
[0], namelen
);
1804 /* Update the attributes header. */
1805 header
->num_attrs
++;
1806 header
->data_length
+= datalen
;
1809 /* Only write the entries, since the data was written separately. */
1810 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
1812 /* The entry and data are both in the header; write them together. */
1813 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1815 error
= write_xattrinfo(&ainfo
);
1817 printf("setxattr: write_xattrinfo error %d\n", error
);
1821 rel_xattrinfo(&ainfo
);
1822 close_xattrfile(xvp
, fileflags
, context
);
1824 /* Touch the change time if we changed an attribute. */
1826 struct vnode_attr va
;
1828 /* Re-write the mtime to cause a ctime change. */
1830 VATTR_WANTED(&va
, va_modify_time
);
1831 if (vnode_getattr(vp
, &va
, context
) == 0) {
1833 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1834 (void) vnode_setattr(vp
, &va
, context
);
1842 * Remove an extended attribute.
1845 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
1849 attr_header_t
*header
;
1850 attr_entry_t
*entry
;
1851 attr_entry_t
*oldslot
;
1857 int found
= 0, lastone
= 0;
1865 fileflags
= FREAD
| FWRITE
;
1866 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1869 * Open the file locked (exclusive) since the Carbon
1870 * File Manager may have the Apple Double file open
1871 * and could be changing the resource fork.
1873 fileflags
|= O_EXLOCK
;
1878 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1881 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1882 close_xattrfile(xvp
, fileflags
, context
);
1886 attrcount
+= ainfo
.attrhdr
->num_attrs
;
1889 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
1892 /* Clear the Finder Info. */
1893 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1894 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1898 /* On removal of last attribute the ._ file is removed. */
1899 if (--attrcount
== 0)
1901 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1902 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
1903 ainfo
.iosize
= sizeof(attr_header_t
);
1904 error
= write_xattrinfo(&ainfo
);
1908 /* Clear the Resource Fork. */
1910 if (!vnode_isreg(vp
)) {
1914 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
1918 /* On removal of last attribute the ._ file is removed. */
1919 if (--attrcount
== 0)
1923 * If the resource fork isn't the last AppleDouble
1924 * entry then the space needs to be reclaimed by
1925 * shifting the entries after the resource fork.
1927 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
1928 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
1929 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1932 ainfo
.rsrcfork
->length
= 0;
1933 ainfo
.iosize
= sizeof(attr_header_t
);
1934 error
= write_xattrinfo(&ainfo
);
1939 if (ainfo
.attrhdr
== NULL
) {
1943 namelen
= strlen(name
) + 1;
1944 header
= ainfo
.attrhdr
;
1945 entry
= ainfo
.attr_entry
;
1948 * See if this attribute exists.
1950 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1951 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1953 if ((i
+1) == header
->num_attrs
)
1957 entry
= ATTR_NEXT(entry
);
1963 /* On removal of last attribute the ._ file is removed. */
1964 if (--attrcount
== 0)
1967 datalen
= entry
->length
;
1968 dataoff
= entry
->offset
;
1969 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1970 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
1975 /* Remove the attribute entry. */
1977 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
1978 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
1981 /* Adjust the attribute data. */
1985 dataoff
- header
->data_start
,
1991 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
1995 /* XXX write zeros to freed space ? */
1996 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2000 bcopy((u_int8_t
*)header
+ header
->data_start
,
2001 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2002 dataoff
- header
->data_start
);
2004 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2005 (u_int8_t
*)header
+ dataoff
- entrylen
,
2006 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2008 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2009 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2012 /* Adjust the header values and entry offsets. */
2013 header
->num_attrs
--;
2014 header
->data_start
-= entrylen
;
2015 header
->data_length
-= datalen
;
2018 entry
= ainfo
.attr_entry
;
2019 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2020 entry
->offset
-= entrylen
;
2021 if (entry
>= oldslot
)
2022 entry
->offset
-= datalen
;
2023 entry
= ATTR_NEXT(entry
);
2025 error
= write_xattrinfo(&ainfo
);
2027 printf("removexattr: write_xattrinfo error %d\n", error
);
2030 rel_xattrinfo(&ainfo
);
2032 /* When there are no more attributes remove the ._ file. */
2033 if (attrcount
== 0) {
2034 if (fileflags
& O_EXLOCK
)
2035 (void) unlock_xattrfile(xvp
, context
);
2036 VNOP_CLOSE(xvp
, fileflags
, context
);
2038 error
= remove_xattrfile(xvp
, context
);
2041 close_xattrfile(xvp
, fileflags
, context
);
2043 /* Touch the change time if we changed an attribute. */
2045 struct vnode_attr va
;
2047 /* Re-write the mtime to cause a ctime change. */
2049 VATTR_WANTED(&va
, va_modify_time
);
2050 if (vnode_getattr(vp
, &va
, context
) == 0) {
2052 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2053 (void) vnode_setattr(vp
, &va
, context
);
2062 * Retrieve the list of extended attribute names.
2065 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2069 attr_entry_t
*entry
;
2074 * We do not zero "*size" here as we don't want to stomp a size set when
2075 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2076 * system call layer, up in listxattr or flistxattr.
2079 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2080 if (error
== ENOATTR
)
2084 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2085 close_xattrfile(xvp
, FREAD
, context
);
2089 /* Check for Finder Info. */
2090 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2092 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2093 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2097 error
= uiomove(XATTR_FINDERINFO_NAME
,
2098 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2106 /* Check for Resource Fork. */
2107 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2109 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2110 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2114 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2115 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2123 /* Check for attributes. */
2124 if (ainfo
.attrhdr
) {
2125 count
= ainfo
.attrhdr
->num_attrs
;
2126 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2127 if (xattr_protected((const char *)entry
->name
) ||
2128 xattr_validatename((const char *)entry
->name
) != 0) {
2129 entry
= ATTR_NEXT(entry
);
2133 *size
+= entry
->namelen
;
2134 entry
= ATTR_NEXT(entry
);
2137 if (uio_resid(uio
) < entry
->namelen
) {
2141 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2143 if (error
!= EFAULT
)
2147 entry
= ATTR_NEXT(entry
);
2151 rel_xattrinfo(&ainfo
);
2152 close_xattrfile(xvp
, FREAD
, context
);
2158 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2160 vnode_t xvp
= NULLVP
;
2161 vnode_t dvp
= NULLVP
;
2162 struct vnode_attr va
;
2163 struct nameidata nd
;
2165 char *filename
= NULL
;
2166 const char *basename
= NULL
;
2172 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2174 * For the root directory use "._." to hold the attributes.
2176 filename
= &smallname
[0];
2177 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2178 dvp
= vp
; /* the "._." file resides in the root dir */
2181 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2185 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2190 /* "._" Attribute files cannot have attributes */
2191 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2192 basename
[0] == '.' && basename
[1] == '_') {
2196 filename
= &smallname
[0];
2197 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2198 if (len
>= sizeof(smallname
)) {
2199 len
++; /* snprintf result doesn't include '\0' */
2200 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2201 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2204 * Note that the lookup here does not authorize. Since we are looking
2205 * up in the same directory that we already have the file vnode in,
2206 * we must have been given the file vnode legitimately. Read/write
2207 * access has already been authorized in layers above for calls from
2208 * userspace, and the authorization code using this path to read
2209 * file security from the EA must always get access
2212 NDINIT(&nd
, LOOKUP
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
, UIO_SYSSPACE
,
2213 CAST_USER_ADDR_T(filename
), context
);
2216 if (fileflags
& O_CREAT
) {
2217 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2219 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2221 if ( (error
= namei(&nd
))) {
2226 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2232 * Pick up uid/gid/mode from target file.
2235 VATTR_WANTED(&va
, va_uid
);
2236 VATTR_WANTED(&va
, va_gid
);
2237 VATTR_WANTED(&va
, va_mode
);
2238 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2239 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2240 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2241 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2244 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2245 } else /* fallback values */ {
2246 uid
= KAUTH_UID_NONE
;
2247 gid
= KAUTH_GID_NONE
;
2248 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2252 VATTR_SET(&va
, va_type
, VREG
);
2253 VATTR_SET(&va
, va_mode
, umode
);
2254 if (uid
!= KAUTH_UID_NONE
)
2255 VATTR_SET(&va
, va_uid
, uid
);
2256 if (gid
!= KAUTH_GID_NONE
)
2257 VATTR_SET(&va
, va_gid
, gid
);
2259 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &va
,
2260 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2267 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2272 if ((error
= namei(&nd
))) {
2282 if (xvp
->v_type
!= VREG
) {
2287 * Owners must match.
2290 VATTR_WANTED(&va
, va_uid
);
2291 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2292 uid_t owner
= va
.va_uid
;
2295 VATTR_WANTED(&va
, va_uid
);
2296 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2297 error
= ENOATTR
; /* don't use this "._" file */
2302 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2308 if ((error
= vnode_ref(xvp
))) {
2313 /* If create was requested, make sure file header exists. */
2314 if (fileflags
& O_CREAT
) {
2316 VATTR_WANTED(&va
, va_data_size
);
2317 VATTR_WANTED(&va
, va_fileid
);
2318 VATTR_WANTED(&va
, va_nlink
);
2319 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2324 /* If the file is empty then add a default header. */
2325 if (va
.va_data_size
== 0) {
2326 /* Don't adopt hard-linked "._" files. */
2327 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2331 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2335 /* Apply file locking if requested. */
2336 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2339 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2340 error
= lock_xattrfile(xvp
, locktype
, context
);
2343 if (dvp
&& (dvp
!= vp
)) {
2347 vnode_putname(basename
);
2349 if (filename
&& filename
!= &smallname
[0]) {
2350 FREE(filename
, M_TEMP
);
2353 if (xvp
!= NULLVP
) {
2355 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2358 (void) vnode_rele(xvp
);
2360 (void) vnode_put(xvp
);
2363 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2367 *xvpp
= xvp
; /* return a referenced vnode */
2372 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2374 // if (fileflags & FWRITE)
2375 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2377 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2378 (void) unlock_xattrfile(xvp
, context
);
2380 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2381 (void) vnode_rele(xvp
);
2382 (void) vnode_put(xvp
);
2386 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2389 struct nameidata nd
;
2394 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2398 pathlen
= MAXPATHLEN
;
2399 error
= vn_getpath(xvp
, path
, &pathlen
);
2401 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2405 NDINIT(&nd
, DELETE
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2406 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2408 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2415 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2424 * Read in and parse the AppleDouble header and entries, and the extended
2425 * attribute header and entries if any. Populates the fields of ainfop
2426 * based on the headers and entries found.
2428 * The basic idea is to:
2429 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2430 * AppleDouble entries, the extended attribute header, and extended
2431 * attribute entries must lie within this part of the file; the rest of
2432 * the AppleDouble handling code assumes this. Plus it allows us to
2433 * somewhat optimize by doing a smaller number of larger I/Os.
2434 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2436 * - Find the Finder Info and Resource Fork entries, if any.
2437 * - If we're going to be writing, try to make sure the Finder Info entry has
2438 * room to store the extended attribute header, plus some space for extended
2440 * - Swap and sanity check the extended attribute header and entries (if any).
2443 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2446 void * buffer
= NULL
;
2447 apple_double_header_t
*filehdr
;
2448 struct vnode_attr va
;
2453 bzero(ainfop
, sizeof(attr_info_t
));
2454 ainfop
->filevp
= xvp
;
2455 ainfop
->context
= context
;
2457 VATTR_WANTED(&va
, va_data_size
);
2458 VATTR_WANTED(&va
, va_fileid
);
2459 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2462 ainfop
->filesize
= va
.va_data_size
;
2464 /* When setting attributes, allow room for the header to grow. */
2466 iosize
= ATTR_MAX_HDR_SIZE
;
2468 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2474 ainfop
->iosize
= iosize
;
2475 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2476 if (buffer
== NULL
){
2481 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2482 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2484 /* Read the file header. */
2485 error
= VNOP_READ(xvp
, auio
, 0, context
);
2489 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2490 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2492 filehdr
= (apple_double_header_t
*)buffer
;
2494 error
= check_and_swap_apple_double_header(ainfop
);
2498 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2500 /* rel_xattrinfo is responsible for freeing the header buffer */
2503 /* Find the Finder Info and Resource Fork entries, if any */
2504 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2505 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2506 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2507 /* We found the Finder Info entry. */
2508 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2511 * Is the Finder Info "empty" (all zeroes)? If so,
2512 * we'll pretend like the Finder Info extended attribute
2515 * Note: we have to make sure the Finder Info is
2516 * contained within the buffer we have already read,
2517 * to avoid accidentally accessing a bogus address.
2518 * If it is outside the buffer, we just assume the
2519 * Finder Info is non-empty.
2521 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2522 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2523 ainfop
->emptyfinderinfo
= 1;
2526 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2528 * Ignore zero-length resource forks when getting. If setting,
2529 * we need to remember the resource fork entry so it can be
2530 * updated once the new content has been written.
2532 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2536 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2538 * The "empty" resource headers we created have a system data tag of:
2539 * "This resource fork intentionally left blank "
2541 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2543 u_int8_t systemData
[64];
2547 /* Read the system data which starts at byte 16 */
2548 rf_uio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2549 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2550 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2551 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2555 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2556 continue; /* skip this resource fork */
2559 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2560 if (i
!= (filehdr
->numEntries
- 1)) {
2561 printf("get_xattrinfo: resource fork not last entry\n");
2562 ainfop
->readonly
= 1;
2569 * See if this file looks like it is laid out correctly to contain
2570 * extended attributes. If so, then do the following:
2572 * - If we're going to be writing, try to make sure the Finder Info
2573 * entry has room to store the extended attribute header, plus some
2574 * space for extended attributes.
2576 * - Swap and sanity check the extended attribute header and entries
2579 if (filehdr
->numEntries
== 2 &&
2580 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2581 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2582 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2583 attr_header_t
*attrhdr
;
2584 attrhdr
= (attr_header_t
*)filehdr
;
2586 * If we're going to be writing, try to make sure the Finder
2587 * Info entry has room to store the extended attribute header,
2588 * plus some space for extended attributes.
2590 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2594 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2595 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2596 /* Make some room before existing resource fork. */
2597 shift_data_down(xvp
,
2598 filehdr
->entries
[1].offset
,
2599 filehdr
->entries
[1].length
,
2601 writesize
= sizeof(attr_header_t
);
2603 /* Create a new, empty resource fork. */
2604 rsrcfork_header_t
*rsrcforkhdr
;
2606 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2608 /* Steal some space for an empty RF header. */
2609 delta
-= sizeof(rsrcfork_header_t
);
2611 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2612 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2614 /* Fill in Empty Resource Fork Header. */
2615 init_empty_resource_fork(rsrcforkhdr
);
2617 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2618 writesize
= ATTR_BUF_SIZE
;
2620 filehdr
->entries
[0].length
+= delta
;
2621 filehdr
->entries
[1].offset
+= delta
;
2623 /* Fill in Attribute Header. */
2624 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2625 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2626 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2627 attrhdr
->data_start
= sizeof(attr_header_t
);
2628 attrhdr
->data_length
= 0;
2629 attrhdr
->reserved
[0] = 0;
2630 attrhdr
->reserved
[1] = 0;
2631 attrhdr
->reserved
[2] = 0;
2633 attrhdr
->num_attrs
= 0;
2635 /* Push out new header */
2636 uio_reset(auio
, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2637 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2639 swap_adhdr(filehdr
); /* to big endian */
2640 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2641 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2642 swap_adhdr(filehdr
); /* back to native */
2643 /* The attribute header gets swapped below. */
2647 * Swap and sanity check the extended attribute header and
2648 * entries (if any). The Finder Info content must be big enough
2649 * to include the extended attribute header; if not, we just
2652 * Note that we're passing the offset + length (i.e. the end)
2653 * of the Finder Info instead of rawsize to validate_attrhdr.
2654 * This ensures that all extended attributes lie within the
2655 * Finder Info content according to the AppleDouble entry.
2657 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2660 if (ainfop
->finderinfo
&&
2661 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2662 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2663 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2665 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2666 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2667 /* First attr_entry starts immediately following attribute header */
2668 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2677 FREE(buffer
, M_TEMP
);
2683 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2686 rsrcfork_header_t
*rsrcforkhdr
;
2692 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2693 bzero(buffer
, ATTR_BUF_SIZE
);
2695 xah
= (attr_header_t
*)buffer
;
2696 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2697 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2698 rsrcforksize
= sizeof(rsrcfork_header_t
);
2699 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2701 /* Fill in Apple Double Header. */
2702 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2703 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2704 xah
->appledouble
.numEntries
= SWAP16 (2);
2705 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2706 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2707 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2708 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2709 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2710 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2711 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2713 /* Fill in Attribute Header. */
2714 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2715 xah
->debug_tag
= SWAP32 (fileid
);
2716 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2717 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2719 /* Fill in Empty Resource Fork Header. */
2720 init_empty_resource_fork(rsrcforkhdr
);
2723 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2726 FREE(buffer
, M_TEMP
);
2732 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
2734 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
2735 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2736 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2737 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2738 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2739 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2740 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2741 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
2742 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
2743 rsrcforkhdr
->typeCount
= SWAP16 (-1);
2744 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
2748 rel_xattrinfo(attr_info_t
*ainfop
)
2750 FREE(ainfop
->filehdr
, M_TEMP
);
2751 bzero(ainfop
, sizeof(attr_info_t
));
2755 write_xattrinfo(attr_info_t
*ainfop
)
2760 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2761 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
2763 swap_adhdr(ainfop
->filehdr
);
2764 if (ainfop
->attrhdr
!= NULL
) {
2765 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2768 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
2770 swap_adhdr(ainfop
->filehdr
);
2771 if (ainfop
->attrhdr
!= NULL
) {
2772 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2779 #if BYTE_ORDER == LITTLE_ENDIAN
2781 * Endian swap apple double header
2784 swap_adhdr(apple_double_header_t
*adh
)
2789 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
2791 adh
->magic
= SWAP32 (adh
->magic
);
2792 adh
->version
= SWAP32 (adh
->version
);
2793 adh
->numEntries
= SWAP16 (adh
->numEntries
);
2795 for (i
= 0; i
< count
; i
++) {
2796 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
2797 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
2798 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
2803 * Endian swap extended attributes header
2806 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
2812 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
2814 ah
->magic
= SWAP32 (ah
->magic
);
2815 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
2816 ah
->total_size
= SWAP32 (ah
->total_size
);
2817 ah
->data_start
= SWAP32 (ah
->data_start
);
2818 ah
->data_length
= SWAP32 (ah
->data_length
);
2819 ah
->flags
= SWAP16 (ah
->flags
);
2820 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
2822 ae
= (attr_entry_t
*)(&ah
[1]);
2823 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
2824 ae
->offset
= SWAP32 (ae
->offset
);
2825 ae
->length
= SWAP32 (ae
->length
);
2826 ae
->flags
= SWAP16 (ae
->flags
);
2832 * Validate and swap the attributes header contents, and each attribute's
2835 * Note: Assumes the caller has verified that the Finder Info content is large
2836 * enough to contain the attr_header structure itself. Therefore, we can
2837 * swap the header fields before sanity checking them.
2840 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
2851 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
2854 /* Swap the basic header fields */
2855 ah
->magic
= SWAP32(ah
->magic
);
2856 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
2857 ah
->total_size
= SWAP32 (ah
->total_size
);
2858 ah
->data_start
= SWAP32 (ah
->data_start
);
2859 ah
->data_length
= SWAP32 (ah
->data_length
);
2860 ah
->flags
= SWAP16 (ah
->flags
);
2861 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
2864 * Make sure the total_size fits within the Finder Info area, and the
2865 * extended attribute data area fits within total_size.
2867 end
= ah
->data_start
+ ah
->data_length
;
2868 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
2869 end
< ah
->data_start
||
2870 end
> ah
->total_size
) {
2875 * Make sure each of the attr_entry_t's fits within total_size.
2877 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
2878 count
= ah
->num_attrs
;
2879 ae
= (attr_entry_t
*)(&ah
[1]);
2881 for (i
=0; i
<count
; i
++) {
2882 /* Make sure the fixed-size part of this attr_entry_t fits. */
2883 if ((u_int8_t
*) &ae
[1] > buf_end
)
2886 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
2887 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
2888 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
2891 /* Swap the attribute entry fields */
2892 ae
->offset
= SWAP32(ae
->offset
);
2893 ae
->length
= SWAP32(ae
->length
);
2894 ae
->flags
= SWAP16(ae
->flags
);
2896 /* Make sure the attribute content fits. */
2897 end
= ae
->offset
+ ae
->length
;
2898 if (end
< ae
->offset
|| end
> ah
->total_size
)
2905 * TODO: Make sure the contents of attributes don't overlap the header
2906 * and don't overlap each other. The hard part is that we don't know
2907 * what the actual header size is until we have looped over all of the
2908 * variable-sized attribute entries.
2910 * XXX Is there any guarantee that attribute entries are stored in
2911 * XXX order sorted by the contents' file offset? If so, that would
2912 * XXX make the pairwise overlap check much easier.
2919 // "start" & "end" are byte offsets in the file.
2920 // "to" is the byte offset we want to move the
2921 // data to. "to" should be > "start".
2923 // we do the copy backwards to avoid problems if
2924 // there's an overlap.
2927 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
2930 size_t chunk
, orig_chunk
;
2933 ucred_t ucred
= vfs_context_ucred(context
);
2934 proc_t p
= vfs_context_proc(context
);
2936 if (delta
== 0 || len
== 0) {
2946 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
2950 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
2951 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
2953 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
2954 pos
, ret
, chunk
, ret
);
2958 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
2960 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
2961 pos
+delta
, ret
, chunk
, ret
);
2965 if ((pos
- chunk
) < start
) {
2966 chunk
= pos
- start
;
2968 if (chunk
== 0) { // we're all done
2973 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
2980 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
2983 size_t chunk
, orig_chunk
;
2987 ucred_t ucred
= vfs_context_ucred(context
);
2988 proc_t p
= vfs_context_proc(context
);
2990 if (delta
== 0 || len
== 0) {
3001 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3005 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3006 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3008 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3009 pos
, ret
, chunk
, ret
);
3013 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3015 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3016 pos
+delta
, ret
, chunk
, ret
);
3020 if ((pos
+ chunk
) > end
) {
3023 if (chunk
== 0) { // we're all done
3028 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3034 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3039 lf
.l_whence
= SEEK_SET
;
3042 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3043 /* Note: id is just a kernel address that's not a proc */
3044 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
);
3045 return (error
== ENOTSUP
? 0 : error
);
3049 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3054 lf
.l_whence
= SEEK_SET
;
3057 lf
.l_type
= F_UNLCK
;
3058 /* Note: id is just a kernel address that's not a proc */
3059 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);
3060 return (error
== ENOTSUP
? 0 : error
);