2 * Copyright (c) 2004-2012 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>
62 static int shadow_sequence
;
65 * We use %p to prevent loss of precision for pointers on varying architectures.
68 #define SHADOW_NAME_FMT ".vfs_rsrc_stream_%p%08x%p"
69 #define SHADOW_DIR_FMT ".vfs_rsrc_streams_%p%x"
70 #define SHADOW_DIR_CONTAINER "/var/run"
72 #define MAKE_SHADOW_NAME(VP, NAME) \
73 snprintf((NAME), sizeof((NAME)), (SHADOW_NAME_FMT), \
74 ((void*)(VM_KERNEL_ADDRPERM(VP))), \
76 ((void*)(VM_KERNEL_ADDRPERM((VP)->v_data))))
78 /* The full path to the shadow directory */
79 #define MAKE_SHADOW_DIRNAME(VP, NAME) \
80 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_CONTAINER "/" SHADOW_DIR_FMT), \
81 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
83 /* The shadow directory as a 'leaf' entry */
84 #define MAKE_SHADOW_DIR_LEAF(VP, NAME) \
85 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_FMT), \
86 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
88 static int default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
);
90 static int default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
);
92 static int default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
);
94 static int getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
, int *creator
, vfs_context_t context
);
96 static int get_shadow_dir(vnode_t
*sdvpp
);
98 #endif /* NAMEDSTREAMS */
101 * Default xattr support routines.
104 static int default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
, int options
,
105 vfs_context_t context
);
106 static int default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
,
107 vfs_context_t context
);
108 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
109 vfs_context_t context
);
110 static int default_removexattr(vnode_t vp
, const char *name
, int options
,
111 vfs_context_t context
);
114 * Retrieve the data of an extended attribute.
117 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
118 int options
, vfs_context_t context
)
122 if (!XATTR_VNODE_SUPPORTED(vp
)) {
126 /* getxattr calls are not allowed for streams. */
127 if (vp
->v_flag
& VISNAMEDSTREAM
) {
133 * Non-kernel request need extra checks performed.
135 * The XATTR_NOSECURITY flag implies a kernel request.
137 if (!(options
& XATTR_NOSECURITY
)) {
139 error
= mac_vnode_check_getextattr(context
, vp
, name
, uio
);
143 if ((error
= xattr_validatename(name
))) {
146 if ((error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
))) {
149 /* The offset can only be non-zero for resource forks. */
150 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
151 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
157 /* The offset can only be non-zero for resource forks. */
158 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
159 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
164 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
165 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
167 * A filesystem may keep some EAs natively and return ENOTSUP for others.
169 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
176 * Set the data of an extended attribute.
179 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
183 if (!XATTR_VNODE_SUPPORTED(vp
)) {
187 /* setxattr calls are not allowed for streams. */
188 if (vp
->v_flag
& VISNAMEDSTREAM
) {
193 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
196 if ((error
= xattr_validatename(name
))) {
199 if (!(options
& XATTR_NOSECURITY
)) {
201 error
= mac_vnode_check_setextattr(context
, vp
, name
, uio
);
205 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
209 /* The offset can only be non-zero for resource forks. */
210 if (uio_offset(uio
) != 0 &&
211 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0 ) {
216 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
219 * An EJUSTRETURN is from a filesystem which keeps this xattr
220 * natively as well as in a dot-underscore file. In this case the
221 * EJUSTRETURN means the filesytem has done nothing, but identifies the
222 * EA as one which may be represented natively and/or in a DU, and
223 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
224 * in vn_setxattr can we do the getxattrs needed to ascertain whether
225 * the XATTR_{CREATE,REPLACE} should yield an error.
227 if (error
== EJUSTRETURN
) {
228 int native
= 0, dufile
= 0;
229 size_t sz
; /* not used */
231 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
232 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
233 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
237 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
242 * Having determined no CREATE/REPLACE error should result, we
243 * zero those bits, so both backing stores get written to.
245 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
246 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
247 /* the mainline path here is to have error==ENOTSUP ... */
249 #endif /* DUAL_EAS */
250 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
252 * A filesystem may keep some EAs natively and return ENOTSUP for others.
254 error
= default_setxattr(vp
, name
, uio
, options
, context
);
257 if ((error
== 0) && !(options
& XATTR_NOSECURITY
)) {
258 mac_vnode_notify_setextattr(context
, vp
, name
, uio
);
259 if (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
)
260 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
268 * Remove an extended attribute.
271 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
275 if (!XATTR_VNODE_SUPPORTED(vp
)) {
279 /* removexattr calls are not allowed for streams. */
280 if (vp
->v_flag
& VISNAMEDSTREAM
) {
285 if ((error
= xattr_validatename(name
))) {
288 if (!(options
& XATTR_NOSECURITY
)) {
290 error
= mac_vnode_check_deleteextattr(context
, vp
, name
);
294 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
298 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
299 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
301 * A filesystem may keep some EAs natively and return ENOTSUP for others.
303 error
= default_removexattr(vp
, name
, options
, context
);
305 } else if (error
== EJUSTRETURN
) {
307 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
308 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
309 * a native xattr, so failure to find it in a DU file during
310 * default_removexattr should not be considered an error.
312 error
= default_removexattr(vp
, name
, options
, context
);
313 if (error
== ENOATTR
)
315 #endif /* DUAL_EAS */
318 if ((error
== 0) && !(options
& XATTR_NOSECURITY
)) {
319 mac_vnode_notify_deleteextattr(context
, vp
, name
);
320 if (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
)
321 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
329 * Retrieve the list of extended attribute names.
332 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
336 if (!XATTR_VNODE_SUPPORTED(vp
)) {
340 /* listxattr calls are not allowed for streams. */
341 if (vp
->v_flag
& VISNAMEDSTREAM
) {
346 if (!(options
& XATTR_NOSECURITY
)) {
348 error
= mac_vnode_check_listextattr(context
, vp
);
353 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
);
358 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
359 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
361 * A filesystem may keep some but not all EAs natively, in which case
362 * the native EA names will have been uiomove-d out (or *size updated)
363 * and the default_listxattr here will finish the job.
365 error
= default_listxattr(vp
, uio
, size
, options
, context
);
372 xattr_validatename(const char *name
)
376 if (name
== NULL
|| name
[0] == '\0') {
379 namelen
= strlen(name
);
380 if (name
[namelen
] != '\0')
381 return (ENAMETOOLONG
);
383 if (utf8_validatestr((const unsigned char *)name
, namelen
) != 0)
391 * Determine whether an EA is a protected system attribute.
394 xattr_protected(const char *attrname
)
396 return(!strncmp(attrname
, "com.apple.system.", 17));
403 * Obtain a named stream from vnode vp.
406 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
410 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) {
411 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
416 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
420 uint32_t streamflags
= VISNAMEDSTREAM
;
423 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
424 streamflags
|= VISSHADOW
;
428 vnode_lock_spin(svp
);
429 svp
->v_flag
|= streamflags
;
432 /* Tag the parent so we know to flush credentials for streams on setattr */
434 vp
->v_lflag
|= VL_HASSTREAMS
;
437 /* Make the file it's parent.
438 * Note: This parent link helps us distinguish vnodes for
439 * shadow stream files from vnodes for resource fork on file
440 * systems that support namedstream natively (both have
441 * VISNAMEDSTREAM set) by allowing access to mount structure
442 * for checking MNTK_NAMED_STREAMS bit at many places in the
445 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
452 * Make a named stream for vnode vp.
455 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
459 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
460 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
462 error
= default_makenamedstream(vp
, svpp
, name
, context
);
465 uint32_t streamflags
= VISNAMEDSTREAM
;
469 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
470 streamflags
|= VISSHADOW
;
474 vnode_lock_spin(svp
);
475 svp
->v_flag
|= streamflags
;
478 /* Tag the parent so we know to flush credentials for streams on setattr */
480 vp
->v_lflag
|= VL_HASSTREAMS
;
483 /* Make the file it's parent.
484 * Note: This parent link helps us distinguish vnodes for
485 * shadow stream files from vnodes for resource fork on file
486 * systems that support namedstream natively (both have
487 * VISNAMEDSTREAM set) by allowing access to mount structure
488 * for checking MNTK_NAMED_STREAMS bit at many places in the
491 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
497 * Remove a named stream from vnode vp.
500 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
504 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
505 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
507 error
= default_removenamedstream(vp
, name
, context
);
512 #define NS_IOBUFSIZE (128 * 1024)
515 * Release a named stream shadow file.
517 * Note: This function is called from two places where we do not need
518 * to check if the vnode has any references held before deleting the
519 * shadow file. Once from vclean() when the vnode is being reclaimed
520 * and we do not hold any references on the vnode. Second time from
521 * default_getnamedstream() when we get an error during shadow stream
522 * file initialization so that other processes who are waiting for the
523 * shadow stream file initialization by the creator will get opportunity
524 * to create and initialize the file again.
527 vnode_relenamedstream(vnode_t vp
, vnode_t svp
) {
529 struct componentname cn
;
534 * We need to use the kernel context here. If we used the supplied
535 * VFS context we have no clue whether or not it originated from userland
536 * where it could be subject to a chroot jail. We need to ensure that all
537 * filesystem access to shadow files is done on the same FS regardless of
538 * userland process restrictions.
540 vfs_context_t kernelctx
= vfs_context_kernel();
545 MAKE_SHADOW_NAME(vp
, tmpname
);
548 cn
.cn_nameiop
= DELETE
;
549 cn
.cn_flags
= ISLASTCN
;
550 cn
.cn_context
= kernelctx
;
551 cn
.cn_pnbuf
= tmpname
;
552 cn
.cn_pnlen
= sizeof(tmpname
);
553 cn
.cn_nameptr
= cn
.cn_pnbuf
;
554 cn
.cn_namelen
= strlen(tmpname
);
557 * Obtain the vnode for the shadow files directory. Make sure to
558 * use the kernel ctx as described above.
560 err
= get_shadow_dir(&dvp
);
565 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, kernelctx
);
572 * Flush a named stream shadow file.
574 * 'vp' represents the AppleDouble file.
575 * 'svp' represents the shadow file.
578 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
580 struct vnode_attr va
;
582 caddr_t bufptr
= NULL
;
589 * The kernel context must be used for all I/O to the shadow file
590 * and its namespace operations
592 vfs_context_t kernelctx
= vfs_context_kernel();
594 /* The supplied context is used for access to the AD file itself */
597 VATTR_WANTED(&va
, va_data_size
);
598 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
599 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
602 datasize
= va
.va_data_size
;
604 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
608 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
609 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
612 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
616 * Copy the shadow stream file data into the resource fork.
618 error
= VNOP_OPEN(svp
, 0, kernelctx
);
620 printf("vnode_flushnamedstream: err %d opening file\n", error
);
623 while (offset
< datasize
) {
624 iosize
= MIN(datasize
- offset
, iosize
);
626 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
627 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
628 error
= VNOP_READ(svp
, auio
, 0, kernelctx
);
632 /* Since there's no truncate xattr we must remove the resource fork. */
634 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
635 if ((error
!= 0) && (error
!= ENOATTR
)) {
639 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
640 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
641 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
648 /* close shadowfile */
649 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
652 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
662 * Verify that the vnode 'vp' is a vnode that lives in the shadow
663 * directory. We can't just query the parent pointer directly since
664 * the shadowfile is hooked up to the actual file it's a stream for.
666 errno_t
vnode_verifynamedstream(vnode_t vp
) {
668 struct vnode
*shadow_dvp
= NULL
;
669 struct vnode
*shadowfile
= NULL
;
670 struct componentname cn
;
673 * We need to use the kernel context here. If we used the supplied
674 * VFS context we have no clue whether or not it originated from userland
675 * where it could be subject to a chroot jail. We need to ensure that all
676 * filesystem access to shadow files is done on the same FS regardless of
677 * userland process restrictions.
679 vfs_context_t kernelctx
= vfs_context_kernel();
683 /* Get the shadow directory vnode */
684 error
= get_shadow_dir(&shadow_dvp
);
689 /* Re-generate the shadow name in the buffer */
690 MAKE_SHADOW_NAME (vp
, tmpname
);
692 /* Look up item in shadow dir */
693 bzero(&cn
, sizeof(cn
));
694 cn
.cn_nameiop
= LOOKUP
;
695 cn
.cn_flags
= ISLASTCN
| CN_ALLOWRSRCFORK
;
696 cn
.cn_context
= kernelctx
;
697 cn
.cn_pnbuf
= tmpname
;
698 cn
.cn_pnlen
= sizeof(tmpname
);
699 cn
.cn_nameptr
= cn
.cn_pnbuf
;
700 cn
.cn_namelen
= strlen(tmpname
);
702 if (VNOP_LOOKUP (shadow_dvp
, &shadowfile
, &cn
, kernelctx
) == 0) {
703 /* is the pointer the same? */
704 if (shadowfile
== vp
) {
710 /* drop the iocount acquired */
711 vnode_put (shadowfile
);
714 /* Drop iocount on shadow dir */
715 vnode_put (shadow_dvp
);
720 * Access or create the shadow file as needed.
722 * 'makestream' with non-zero value means that we need to guarantee we were the
723 * creator of the shadow file.
725 * 'context' is the user supplied context for the original VFS operation that
726 * caused us to need a shadow file.
728 * int pointed to by 'creator' is nonzero if we created the shadowfile.
731 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
732 int *creator
, vfs_context_t context
)
734 vnode_t dvp
= NULLVP
;
735 vnode_t svp
= NULLVP
;
736 struct componentname cn
;
737 struct vnode_attr va
;
742 vfs_context_t kernelctx
= vfs_context_kernel();
746 /* Establish a unique file name. */
747 MAKE_SHADOW_NAME(vp
, tmpname
);
748 bzero(&cn
, sizeof(cn
));
749 cn
.cn_nameiop
= LOOKUP
;
750 cn
.cn_flags
= ISLASTCN
;
751 cn
.cn_context
= context
;
752 cn
.cn_pnbuf
= tmpname
;
753 cn
.cn_pnlen
= sizeof(tmpname
);
754 cn
.cn_nameptr
= cn
.cn_pnbuf
;
755 cn
.cn_namelen
= strlen(tmpname
);
757 /* Pick up uid, gid, mode and date from original file. */
759 VATTR_WANTED(&va
, va_uid
);
760 VATTR_WANTED(&va
, va_gid
);
761 VATTR_WANTED(&va
, va_mode
);
762 VATTR_WANTED(&va
, va_create_time
);
763 VATTR_WANTED(&va
, va_modify_time
);
764 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
765 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
766 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
767 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
768 va
.va_uid
= KAUTH_UID_NONE
;
769 va
.va_gid
= KAUTH_GID_NONE
;
770 va
.va_mode
= S_IRUSR
| S_IWUSR
;
772 va
.va_vaflags
= VA_EXCLUSIVE
;
773 VATTR_SET(&va
, va_type
, VREG
);
774 /* We no longer change the access, but we still hide it. */
775 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
777 /* Obtain the vnode for the shadow files directory. */
778 if (get_shadow_dir(&dvp
) != 0) {
783 /* See if someone else already has it open. */
784 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
) == 0) {
785 /* Double check existence by asking for size. */
787 VATTR_WANTED(&va
, va_data_size
);
788 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
789 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
790 goto out
; /* OK to use. */
795 * Otherwise make sure the resource fork data exists.
796 * Use the supplied context for accessing the AD file.
798 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
799 XATTR_NOSECURITY
, context
);
801 * To maintain binary compatibility with legacy Carbon
802 * emulated resource fork support, if the resource fork
803 * doesn't exist but the Finder Info does, then act as
804 * if an empty resource fork is present (see 4724359).
806 if ((error
== ENOATTR
) &&
807 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
808 XATTR_NOSECURITY
, context
) == 0)) {
816 /* If the resource fork exists, its size is expected to be non-zero. */
823 /* Create the shadow stream file. */
824 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, kernelctx
);
829 else if ((error
== EEXIST
) && !makestream
) {
830 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
);
832 else if ((error
== ENOENT
) && !makestream
) {
834 * We could have raced with a rmdir on the shadow directory
835 * post-lookup. Retry from the beginning, 1x only, to
836 * try and see if we need to re-create the shadow directory
851 /* Otherwise, just error out normally below */
859 /* On errors, clean up shadow stream file. */
867 *rsrcsize
= datasize
;
874 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
876 vnode_t svp
= NULLVP
;
878 caddr_t bufptr
= NULL
;
884 /* need the kernel context for accessing the shadowfile */
885 vfs_context_t kernelctx
= vfs_context_kernel();
888 * Only the "com.apple.ResourceFork" stream is supported here.
890 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
896 * Obtain a shadow file for the resource fork I/O.
898 * Need to pass along the supplied context so that getshadowfile
899 * can access the AD file as needed, using it.
901 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
908 * The creator of the shadow file provides its file data,
909 * all other threads should wait until its ready. In order to
910 * prevent a deadlock during error codepaths, we need to check if the
911 * vnode is being created, or if it has failed out. Regardless of success or
912 * failure, we set the VISSHADOW bit on the vnode, so we check that
913 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
914 * then we can infer the creator isn't done yet. If it's there, but
915 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
920 if (svp
->v_flag
& VISNAMEDSTREAM
) {
921 /* data is ready, go use it */
925 /* It's not ready, wait for it (sleep using v_parent as channel) */
926 if ((svp
->v_flag
& VISSHADOW
)) {
928 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
929 * thread is done with this vnode. Just unlock the vnode and try again
934 /* Otherwise, sleep if the shadow file is not created yet */
935 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
936 "getnamedstream", NULL
);
945 * Copy the real resource fork data into shadow stream file.
947 if (op
== NS_OPEN
&& datasize
!= 0) {
951 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
952 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
957 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
960 /* open the shadow file */
961 error
= VNOP_OPEN(svp
, 0, kernelctx
);
965 while (offset
< datasize
) {
968 iosize
= MIN(datasize
- offset
, iosize
);
970 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
971 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
972 /* use supplied ctx for AD file */
973 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
974 XATTR_NOSECURITY
, context
);
979 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
980 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
981 /* kernel context for writing shadowfile */
982 error
= VNOP_WRITE(svp
, auio
, 0, kernelctx
);
989 /* close shadow file */
990 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
993 /* Wake up anyone waiting for svp file content */
997 /* VISSHADOW would be set later on anyway, so we set it now */
998 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
999 wakeup((caddr_t
)&svp
->v_parent
);
1002 /* On post create errors, get rid of the shadow file. This
1003 * way if there is another process waiting for initialization
1004 * of the shadowfile by the current process will wake up and
1005 * retry by creating and initializing the shadow file again.
1006 * Also add the VISSHADOW bit here to indicate we're done operating
1009 (void)vnode_relenamedstream(vp
, svp
);
1011 svp
->v_flag
|= VISSHADOW
;
1012 wakeup((caddr_t
)&svp
->v_parent
);
1018 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
1024 /* On errors, clean up shadow stream file. */
1035 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
1041 * Only the "com.apple.ResourceFork" stream is supported here.
1043 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1048 /* Supply the context to getshadowfile so it can manipulate the AD file */
1049 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
1052 * Wake up any waiters over in default_getnamedstream().
1054 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
1055 vnode_t svp
= *svpp
;
1058 /* If we're the creator, mark it as a named stream */
1059 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
1060 /* Wakeup any waiters on the v_parent channel */
1061 wakeup((caddr_t
)&svp
->v_parent
);
1070 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
1073 * Only the "com.apple.ResourceFork" stream is supported here.
1075 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1079 * XXX - what about other opened instances?
1081 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
1085 get_shadow_dir(vnode_t
*sdvpp
) {
1086 vnode_t dvp
= NULLVP
;
1087 vnode_t sdvp
= NULLVP
;
1088 struct componentname cn
;
1089 struct vnode_attr va
;
1093 vfs_context_t kernelctx
= vfs_context_kernel();
1095 bzero(tmpname
, sizeof(tmpname
));
1096 MAKE_SHADOW_DIRNAME(rootvnode
, tmpname
);
1098 * Look up the shadow directory to ensure that it still exists.
1099 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1100 * in caching it when multiple threads may be trying to manipulate the pointers.
1102 * Make sure to use the kernel context. We want a singular view of
1103 * the shadow dir regardless of chrooted processes.
1105 error
= vnode_lookup(tmpname
, 0, &sdvp
, kernelctx
);
1108 * If we get here, then we have successfully looked up the shadow dir,
1109 * and it has an iocount from the lookup. Return the vp in the output argument.
1114 /* In the failure case, no iocount is acquired */
1116 bzero (tmpname
, sizeof(tmpname
));
1119 * Obtain the vnode for "/var/run" directory using the kernel
1122 * This is defined in the SHADOW_DIR_CONTAINER macro
1124 if (vnode_lookup(SHADOW_DIR_CONTAINER
, 0, &dvp
, kernelctx
) != 0) {
1130 * Create the shadow stream directory.
1131 * 'dvp' below suggests the parent directory so
1132 * we only need to provide the leaf entry name
1134 MAKE_SHADOW_DIR_LEAF(rootvnode
, tmpname
);
1135 bzero(&cn
, sizeof(cn
));
1136 cn
.cn_nameiop
= LOOKUP
;
1137 cn
.cn_flags
= ISLASTCN
;
1138 cn
.cn_context
= kernelctx
;
1139 cn
.cn_pnbuf
= tmpname
;
1140 cn
.cn_pnlen
= sizeof(tmpname
);
1141 cn
.cn_nameptr
= cn
.cn_pnbuf
;
1142 cn
.cn_namelen
= strlen(tmpname
);
1145 * owned by root, only readable by root, hidden
1148 VATTR_SET(&va
, va_uid
, 0);
1149 VATTR_SET(&va
, va_gid
, 0);
1150 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
1151 VATTR_SET(&va
, va_type
, VDIR
);
1152 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
1153 va
.va_vaflags
= VA_EXCLUSIVE
;
1155 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, kernelctx
);
1158 * There can be only one winner for an exclusive create.
1160 if (error
== EEXIST
) {
1161 /* loser has to look up directory */
1162 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, kernelctx
);
1164 /* Make sure its in fact a directory */
1165 if (sdvp
->v_type
!= VDIR
) {
1168 /* Obtain the fsid for /var/run directory */
1170 VATTR_WANTED(&va
, va_fsid
);
1171 if (VNOP_GETATTR(dvp
, &va
, kernelctx
) != 0 ||
1172 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1175 tmp_fsid
= va
.va_fsid
;
1178 VATTR_WANTED(&va
, va_uid
);
1179 VATTR_WANTED(&va
, va_gid
);
1180 VATTR_WANTED(&va
, va_mode
);
1181 VATTR_WANTED(&va
, va_fsid
);
1182 VATTR_WANTED(&va
, va_dirlinkcount
);
1183 VATTR_WANTED(&va
, va_acl
);
1184 /* Provide defaults for attrs that may not be supported */
1185 va
.va_dirlinkcount
= 1;
1186 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1188 if (VNOP_GETATTR(sdvp
, &va
, kernelctx
) != 0 ||
1189 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1190 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1191 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1192 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1196 * Make sure its what we want:
1198 * - not writable by anyone
1199 * - on same file system as /var/run
1200 * - not a hard-linked directory
1201 * - no ACLs (they might grant write access)
1203 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1204 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1205 (va
.va_fsid
!= tmp_fsid
) ||
1206 (va
.va_dirlinkcount
!= 1) ||
1207 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1217 /* On errors, clean up shadow stream directory. */
1227 /* This is not the dir we're looking for, move along */
1228 ++shadow_sequence
; /* try something else next time */
1232 #endif /* NAMEDSTREAMS */
1235 #if CONFIG_APPLEDOUBLE
1237 * Default Implementation (Non-native EA)
1242 Typical "._" AppleDouble Header File layout:
1243 ------------------------------------------------------------
1248 .-- AD ENTRY[0] Finder Info Entry (must be first)
1249 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1251 | ///////////// Fixed Size Data (32 bytes)
1255 | ATTR ENTRY[1] --+--.
1256 | ATTR ENTRY[2] --+--+--.
1258 | ATTR ENTRY[N] --+--+--+--.
1259 | ATTR DATA 0 <-' | | |
1260 | //////////// | | |
1261 | ATTR DATA 1 <----' | |
1263 | ATTR DATA 2 <-------' |
1266 | ATTR DATA N <----------'
1268 | Attribute Free Space
1270 '----> RESOURCE FORK
1271 ///////////// Variable Sized Data
1280 ------------------------------------------------------------
1282 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1283 stored as part of the Finder Info. The length in the Finder
1284 Info AppleDouble entry includes the length of the extended
1285 attribute header, attribute entries, and attribute data.
1290 * On Disk Data Structures
1292 * Note: Motorola 68K alignment and big-endian.
1294 * See RFC 1740 for additional information about the AppleDouble file format.
1298 #define ADH_MAGIC 0x00051607
1299 #define ADH_VERSION 0x00020000
1300 #define ADH_MACOSX "Mac OS X "
1303 * AppleDouble Entry ID's
1305 #define AD_DATA 1 /* Data fork */
1306 #define AD_RESOURCE 2 /* Resource fork */
1307 #define AD_REALNAME 3 /* FileÕs name on home file system */
1308 #define AD_COMMENT 4 /* Standard Mac comment */
1309 #define AD_ICONBW 5 /* Mac black & white icon */
1310 #define AD_ICONCOLOR 6 /* Mac color icon */
1311 #define AD_UNUSED 7 /* Not used */
1312 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1313 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1314 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1315 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1316 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1317 #define AD_AFPNAME 13 /* Short name on AFP server */
1318 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1319 #define AD_AFPDIRID 15 /* AFP directory ID */
1320 #define AD_ATTRIBUTES AD_FINDERINFO
1323 #define ATTR_FILE_PREFIX "._"
1324 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1326 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1328 /* Implementation Limits */
1329 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1330 #define ATTR_MAX_HDR_SIZE 65536
1332 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1333 * size supported (including the attribute entries). All of
1334 * the attribute entries must reside within this limit. If
1335 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1336 * boundry, then all of the attribute data I/O is performed
1337 * separately from the attribute header I/O.
1339 * In particular, all of the attr_entry structures must lie
1340 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1341 * AppleDouble file. However, the attribute data (i.e. the
1342 * contents of the extended attributes) may extend beyond the
1343 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1344 * limit is to allow the implementation to optimize by reading
1345 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1349 #define FINDERINFOSIZE 32
1351 typedef struct apple_double_entry
{
1352 u_int32_t type
; /* entry type: see list, 0 invalid */
1353 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1354 u_int32_t length
; /* entry data length in bytes. */
1355 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1358 typedef struct apple_double_header
{
1359 u_int32_t magic
; /* == ADH_MAGIC */
1360 u_int32_t version
; /* format version: 2 = 0x00020000 */
1361 u_int32_t filler
[4];
1362 u_int16_t numEntries
; /* number of entries which follow */
1363 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1364 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1365 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1366 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1368 #define ADHDRSIZE (4+4+16+2)
1370 /* Entries are aligned on 4 byte boundaries */
1371 typedef struct attr_entry
{
1372 u_int32_t offset
; /* file offset to data */
1373 u_int32_t length
; /* size of attribute data */
1376 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1377 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1380 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1381 typedef struct attr_header
{
1382 apple_double_header_t appledouble
;
1383 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1384 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1385 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1386 u_int32_t data_start
; /* file offset to attribute data area */
1387 u_int32_t data_length
; /* length of attribute data area */
1388 u_int32_t reserved
[3];
1390 u_int16_t num_attrs
;
1391 } __attribute__((aligned(2), packed
)) attr_header_t
;
1394 /* Empty Resource Fork Header */
1395 typedef struct rsrcfork_header
{
1396 u_int32_t fh_DataOffset
;
1397 u_int32_t fh_MapOffset
;
1398 u_int32_t fh_DataLength
;
1399 u_int32_t fh_MapLength
;
1400 u_int8_t systemData
[112];
1401 u_int8_t appData
[128];
1402 u_int32_t mh_DataOffset
;
1403 u_int32_t mh_MapOffset
;
1404 u_int32_t mh_DataLength
;
1405 u_int32_t mh_MapLength
;
1407 u_int16_t mh_RefNum
;
1409 u_int8_t mh_InMemoryAttr
;
1412 u_int16_t typeCount
;
1413 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1415 #define RF_FIRST_RESOURCE 256
1416 #define RF_NULL_MAP_LENGTH 30
1417 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1419 /* Runtime information about the attribute file. */
1420 typedef struct attr_info
{
1421 vfs_context_t context
;
1426 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1427 apple_double_header_t
*filehdr
;
1428 apple_double_entry_t
*finderinfo
;
1429 apple_double_entry_t
*rsrcfork
;
1430 attr_header_t
*attrhdr
;
1431 attr_entry_t
*attr_entry
;
1433 u_int8_t emptyfinderinfo
;
1437 #define ATTR_SETTING 1
1439 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1441 #define ATTR_ENTRY_LENGTH(namelen) \
1442 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1444 #define ATTR_NEXT(ae) \
1445 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1447 #define ATTR_VALID(ae, ai) \
1448 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1450 #define SWAP16(x) OSSwapBigToHostInt16((x))
1451 #define SWAP32(x) OSSwapBigToHostInt32((x))
1452 #define SWAP64(x) OSSwapBigToHostInt64((x))
1455 static u_int32_t emptyfinfo
[8] = {0};
1459 * Local support routines
1461 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1463 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1465 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1467 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1469 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1471 static void rel_xattrinfo(attr_info_t
*ainfop
);
1473 static int write_xattrinfo(attr_info_t
*ainfop
);
1475 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1477 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1479 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1482 #if BYTE_ORDER == LITTLE_ENDIAN
1483 static void swap_adhdr(apple_double_header_t
*adh
);
1484 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1487 #define swap_adhdr(x)
1488 #define swap_attrhdr(x, y)
1491 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1492 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1493 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1497 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1498 * is in big endian (as it would exist on disk). Verifies the following:
1501 * - number of entries
1502 * - that each entry fits within the file size
1504 * If the header is invalid, ENOATTR is returned.
1506 * NOTE: Does not attempt to validate the extended attributes header that
1507 * may be embedded in the Finder Info entry.
1509 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1512 u_int32_t header_end
;
1513 u_int32_t entry_end
;
1515 apple_double_header_t
*header
;
1517 rawsize
= ainfop
->rawsize
;
1518 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1520 /* Is the file big enough to contain an AppleDouble header? */
1521 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1524 /* Swap the AppleDouble header fields to native order */
1525 header
->magic
= SWAP32(header
->magic
);
1526 header
->version
= SWAP32(header
->version
);
1527 header
->numEntries
= SWAP16(header
->numEntries
);
1529 /* Sanity check the AppleDouble header fields */
1530 if (header
->magic
!= ADH_MAGIC
||
1531 header
->version
!= ADH_VERSION
||
1532 header
->numEntries
< 1 ||
1533 header
->numEntries
> 15) {
1537 /* Calculate where the entries[] array ends */
1538 header_end
= offsetof(apple_double_header_t
, entries
) +
1539 header
->numEntries
* sizeof(apple_double_entry_t
);
1541 /* Is the file big enough to contain the AppleDouble entries? */
1542 if (rawsize
< header_end
) {
1546 /* Swap and sanity check each AppleDouble entry */
1547 for (i
=0; i
<header
->numEntries
; i
++) {
1548 /* Swap the per-entry fields to native order */
1549 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1550 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1551 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1553 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1556 * Does the entry's content start within the header itself,
1557 * did the addition overflow, or does the entry's content
1558 * extend past the end of the file?
1560 if (header
->entries
[i
].offset
< header_end
||
1561 entry_end
< header
->entries
[i
].offset
||
1562 entry_end
> ainfop
->filesize
) {
1567 * Does the current entry's content overlap with a previous
1570 * Yes, this is O(N**2), and there are more efficient algorithms
1571 * for testing pairwise overlap of N ranges when N is large.
1572 * But we have already ensured N < 16, and N is almost always 2.
1573 * So there's no point in using a more complex algorithm.
1576 for (j
=0; j
<i
; j
++) {
1577 if (entry_end
> header
->entries
[j
].offset
&&
1578 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1590 * Retrieve the data of an extended attribute.
1593 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1594 __unused
int options
, vfs_context_t context
)
1598 attr_header_t
*header
;
1599 attr_entry_t
*entry
;
1609 if (strcmp(name
, XATTR_RESOURCEFORK_NAME
) == 0) {
1612 * Open the file locked (shared) since the Carbon
1613 * File Manager may have the Apple Double file open
1614 * and could be changing the resource fork.
1616 fileflags
|= O_SHLOCK
;
1621 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1624 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1625 close_xattrfile(xvp
, fileflags
, context
);
1629 /* Get the Finder Info. */
1630 if (strcmp(name
, XATTR_FINDERINFO_NAME
) == 0) {
1632 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1634 } else if (uio
== NULL
) {
1635 *size
= FINDERINFOSIZE
;
1637 } else if (uio_offset(uio
) != 0) {
1639 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1642 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1643 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1648 /* Read the Resource Fork. */
1650 if (!vnode_isreg(vp
)) {
1652 } else if (ainfo
.rsrcfork
== NULL
) {
1654 } else if (uio
== NULL
) {
1655 *size
= (size_t)ainfo
.rsrcfork
->length
;
1657 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1658 error
= VNOP_READ(xvp
, uio
, 0, context
);
1660 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1665 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1669 if (uio_offset(uio
) != 0) {
1674 namelen
= strlen(name
) + 1;
1675 header
= ainfo
.attrhdr
;
1676 entry
= ainfo
.attr_entry
;
1678 * Search for attribute name in the header.
1680 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1681 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1682 datalen
= (size_t)entry
->length
;
1688 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1692 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1693 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1694 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1696 uio_setoffset(uio
, entry
->offset
);
1697 error
= VNOP_READ(xvp
, uio
, 0, context
);
1698 uio_setoffset(uio
, 0);
1702 entry
= ATTR_NEXT(entry
);
1705 rel_xattrinfo(&ainfo
);
1706 close_xattrfile(xvp
, fileflags
, context
);
1712 * Set the data of an extended attribute.
1715 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1719 attr_header_t
*header
;
1720 attr_entry_t
*entry
;
1721 attr_entry_t
*lastentry
;
1725 size_t datafreespace
;
1732 char finfo
[FINDERINFOSIZE
];
1734 datalen
= uio_resid(uio
);
1735 namelen
= strlen(name
) + 1;
1736 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1739 * By convention, Finder Info that is all zeroes is equivalent to not
1740 * having a Finder Info EA. So if we're trying to set the Finder Info
1741 * to all zeroes, then delete it instead. If a file didn't have an
1742 * AppleDouble file before, this prevents creating an AppleDouble file
1743 * with no useful content.
1745 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1746 * for all zeroes Finder Info before opening the AppleDouble file.
1747 * But if either of those options were specified, we need to open the
1748 * AppleDouble file to see whether there was already Finder Info (so we
1749 * can return an error if needed); this case is handled further below.
1751 * NOTE: this copies the Finder Info data into the "finfo" local.
1753 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1755 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1756 * That means we probably have to open_xattrfile and get_xattrinfo.
1758 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1761 error
= uiomove(finfo
, datalen
, uio
);
1764 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1765 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1766 error
= default_removexattr(vp
, name
, 0, context
);
1767 if (error
== ENOATTR
)
1775 * Open the file locked since setting an attribute
1776 * can change the layout of the Apple Double file.
1778 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1779 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1782 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1783 close_xattrfile(xvp
, fileflags
, context
);
1787 /* Set the Finder Info. */
1788 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1789 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1790 /* attr exists and "create" was specified? */
1791 if (options
& XATTR_CREATE
) {
1796 /* attr doesn't exists and "replace" was specified? */
1797 if (options
& XATTR_REPLACE
) {
1802 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1804 * Setting the Finder Info to all zeroes is equivalent to
1805 * removing it. Close the xattr file and let
1806 * default_removexattr do the work (including deleting
1807 * the xattr file if there are no other xattrs).
1809 * Note that we have to handle the case where the
1810 * Finder Info was already all zeroes, and we ignore
1813 * The common case where options == 0 was handled above.
1815 rel_xattrinfo(&ainfo
);
1816 close_xattrfile(xvp
, fileflags
, context
);
1817 error
= default_removexattr(vp
, name
, 0, context
);
1818 if (error
== ENOATTR
)
1822 if (ainfo
.finderinfo
) {
1823 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1824 bcopy(finfo
, attrdata
, datalen
);
1825 ainfo
.iosize
= sizeof(attr_header_t
);
1826 error
= write_xattrinfo(&ainfo
);
1833 /* Write the Resource Fork. */
1834 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1835 u_int32_t endoffset
;
1837 if (!vnode_isreg(vp
)) {
1841 /* Make sure we have a rsrc fork pointer.. */
1842 if (ainfo
.rsrcfork
== NULL
) {
1846 if (ainfo
.rsrcfork
) {
1847 if (ainfo
.rsrcfork
->length
!= 0) {
1848 if (options
& XATTR_CREATE
) {
1849 /* attr exists, and create specified ? */
1855 /* Zero length AD rsrc fork */
1856 if (options
& XATTR_REPLACE
) {
1857 /* attr doesn't exist (0-length), but replace specified ? */
1864 /* We can't do much if we somehow didn't get an AD rsrc pointer */
1869 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1870 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1871 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1874 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1875 if (endoffset
> ainfo
.rsrcfork
->length
) {
1876 ainfo
.rsrcfork
->length
= endoffset
;
1877 ainfo
.iosize
= sizeof(attr_header_t
);
1878 error
= write_xattrinfo(&ainfo
);
1884 if (datalen
> ATTR_MAX_SIZE
) {
1885 return (E2BIG
); /* EINVAL instead ? */
1888 if (ainfo
.attrhdr
== NULL
) {
1892 header
= ainfo
.attrhdr
;
1893 entry
= ainfo
.attr_entry
;
1895 /* Check if data area crosses the maximum header size. */
1896 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1897 splitdata
= 1; /* do data I/O separately */
1902 * See if attribute already exists.
1904 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1905 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1909 entry
= ATTR_NEXT(entry
);
1913 if (options
& XATTR_CREATE
) {
1917 if (datalen
== entry
->length
) {
1919 uio_setoffset(uio
, entry
->offset
);
1920 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1921 uio_setoffset(uio
, 0);
1923 printf("setxattr: VNOP_WRITE error %d\n", error
);
1926 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1927 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1930 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1931 error
= write_xattrinfo(&ainfo
);
1933 printf("setxattr: write_xattrinfo error %d\n", error
);
1939 * Brute force approach - just remove old entry and set new entry.
1942 rel_xattrinfo(&ainfo
);
1943 close_xattrfile(xvp
, fileflags
, context
);
1944 error
= default_removexattr(vp
, name
, options
, context
);
1948 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1949 options
&= ~XATTR_REPLACE
;
1950 goto start
; /* start over */
1955 if (options
& XATTR_REPLACE
) {
1956 error
= ENOATTR
; /* nothing there to replace */
1959 /* Check if header size limit has been reached. */
1960 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1965 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1967 /* Check if we need more space. */
1968 if ((datalen
+ entrylen
) > datafreespace
) {
1971 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1973 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1974 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1975 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1978 ainfo
.filesize
+= growsize
;
1979 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1981 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1987 * Move the resource fork out of the way.
1989 if (ainfo
.rsrcfork
) {
1990 if (ainfo
.rsrcfork
->length
!= 0) {
1991 shift_data_down(xvp
,
1992 ainfo
.rsrcfork
->offset
,
1993 ainfo
.rsrcfork
->length
,
1996 ainfo
.rsrcfork
->offset
+= growsize
;
1998 ainfo
.finderinfo
->length
+= growsize
;
1999 header
->total_size
+= growsize
;
2002 /* Make space for a new entry. */
2004 shift_data_down(xvp
,
2006 header
->data_length
,
2009 bcopy((u_int8_t
*)header
+ header
->data_start
,
2010 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
2011 header
->data_length
);
2013 header
->data_start
+= entrylen
;
2015 /* Fix up entry data offsets. */
2017 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
2018 entry
->offset
+= entrylen
;
2022 * If the attribute data area is entirely within
2023 * the header buffer, then just update the buffer,
2024 * otherwise we'll write it separately to the file.
2029 /* Write new attribute data after the end of existing data. */
2030 offset
= header
->data_start
+ header
->data_length
;
2031 uio_setoffset(uio
, offset
);
2032 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
2033 uio_setoffset(uio
, 0);
2035 printf("setxattr: VNOP_WRITE error %d\n", error
);
2039 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
2041 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
2043 printf("setxattr: uiomove error %d\n", error
);
2048 /* Create the attribute entry. */
2049 lastentry
->length
= datalen
;
2050 lastentry
->offset
= header
->data_start
+ header
->data_length
;
2051 lastentry
->namelen
= namelen
;
2052 lastentry
->flags
= 0;
2053 bcopy(name
, &lastentry
->name
[0], namelen
);
2055 /* Update the attributes header. */
2056 header
->num_attrs
++;
2057 header
->data_length
+= datalen
;
2060 /* Only write the entries, since the data was written separately. */
2061 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
2063 /* The entry and data are both in the header; write them together. */
2064 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2066 error
= write_xattrinfo(&ainfo
);
2068 printf("setxattr: write_xattrinfo error %d\n", error
);
2072 rel_xattrinfo(&ainfo
);
2073 close_xattrfile(xvp
, fileflags
, context
);
2075 /* Touch the change time if we changed an attribute. */
2077 struct vnode_attr va
;
2079 /* Re-write the mtime to cause a ctime change. */
2081 VATTR_WANTED(&va
, va_modify_time
);
2082 if (vnode_getattr(vp
, &va
, context
) == 0) {
2084 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2085 (void) vnode_setattr(vp
, &va
, context
);
2089 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2096 * Remove an extended attribute.
2099 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
2103 attr_header_t
*header
;
2104 attr_entry_t
*entry
;
2105 attr_entry_t
*oldslot
;
2111 int found
= 0, lastone
= 0;
2119 fileflags
= FREAD
| FWRITE
;
2120 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
2123 * Open the file locked (exclusive) since the Carbon
2124 * File Manager may have the Apple Double file open
2125 * and could be changing the resource fork.
2127 fileflags
|= O_EXLOCK
;
2132 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
2135 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2136 close_xattrfile(xvp
, fileflags
, context
);
2140 attrcount
+= ainfo
.attrhdr
->num_attrs
;
2143 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
2146 /* Clear the Finder Info. */
2147 if (strncmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
2148 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
2152 /* On removal of last attribute the ._ file is removed. */
2153 if (--attrcount
== 0)
2155 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
2156 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
2157 ainfo
.iosize
= sizeof(attr_header_t
);
2158 error
= write_xattrinfo(&ainfo
);
2162 /* Clear the Resource Fork. */
2164 if (!vnode_isreg(vp
)) {
2168 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
2172 /* On removal of last attribute the ._ file is removed. */
2173 if (--attrcount
== 0)
2177 * If the resource fork isn't the last AppleDouble
2178 * entry then the space needs to be reclaimed by
2179 * shifting the entries after the resource fork.
2181 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
2182 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
2183 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2186 ainfo
.rsrcfork
->length
= 0;
2187 ainfo
.iosize
= sizeof(attr_header_t
);
2188 error
= write_xattrinfo(&ainfo
);
2193 if (ainfo
.attrhdr
== NULL
) {
2197 namelen
= strlen(name
) + 1;
2198 header
= ainfo
.attrhdr
;
2199 entry
= ainfo
.attr_entry
;
2202 * See if this attribute exists.
2204 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2205 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2207 if ((i
+1) == header
->num_attrs
)
2211 entry
= ATTR_NEXT(entry
);
2217 /* On removal of last attribute the ._ file is removed. */
2218 if (--attrcount
== 0)
2221 datalen
= entry
->length
;
2222 dataoff
= entry
->offset
;
2223 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2224 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
2229 /* Remove the attribute entry. */
2231 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2232 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2235 /* Adjust the attribute data. */
2239 dataoff
- header
->data_start
,
2245 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2249 /* XXX write zeros to freed space ? */
2250 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2254 bcopy((u_int8_t
*)header
+ header
->data_start
,
2255 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2256 dataoff
- header
->data_start
);
2258 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2259 (u_int8_t
*)header
+ dataoff
- entrylen
,
2260 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2262 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2263 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2266 /* Adjust the header values and entry offsets. */
2267 header
->num_attrs
--;
2268 header
->data_start
-= entrylen
;
2269 header
->data_length
-= datalen
;
2272 entry
= ainfo
.attr_entry
;
2273 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2274 entry
->offset
-= entrylen
;
2275 if (entry
>= oldslot
)
2276 entry
->offset
-= datalen
;
2277 entry
= ATTR_NEXT(entry
);
2279 error
= write_xattrinfo(&ainfo
);
2281 printf("removexattr: write_xattrinfo error %d\n", error
);
2284 rel_xattrinfo(&ainfo
);
2286 /* When there are no more attributes remove the ._ file. */
2287 if (attrcount
== 0) {
2288 if (fileflags
& O_EXLOCK
)
2289 (void) unlock_xattrfile(xvp
, context
);
2290 VNOP_CLOSE(xvp
, fileflags
, context
);
2292 error
= remove_xattrfile(xvp
, context
);
2295 close_xattrfile(xvp
, fileflags
, context
);
2297 /* Touch the change time if we changed an attribute. */
2299 struct vnode_attr va
;
2301 /* Re-write the mtime to cause a ctime change. */
2303 VATTR_WANTED(&va
, va_modify_time
);
2304 if (vnode_getattr(vp
, &va
, context
) == 0) {
2306 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2307 (void) vnode_setattr(vp
, &va
, context
);
2311 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2319 * Retrieve the list of extended attribute names.
2322 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2326 attr_entry_t
*entry
;
2331 * We do not zero "*size" here as we don't want to stomp a size set when
2332 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2333 * system call layer, up in listxattr or flistxattr.
2336 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2337 if (error
== ENOATTR
)
2341 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2342 if (error
== ENOATTR
)
2344 close_xattrfile(xvp
, FREAD
, context
);
2348 /* Check for Finder Info. */
2349 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2351 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2352 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2356 error
= uiomove(XATTR_FINDERINFO_NAME
,
2357 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2365 /* Check for Resource Fork. */
2366 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2368 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2369 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2373 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2374 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2382 /* Check for attributes. */
2383 if (ainfo
.attrhdr
) {
2384 count
= ainfo
.attrhdr
->num_attrs
;
2385 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2386 if (xattr_protected((const char *)entry
->name
) ||
2387 ((entry
->namelen
< XATTR_MAXNAMELEN
) &&
2388 (entry
->name
[entry
->namelen
] == '\0') &&
2389 (xattr_validatename((const char *)entry
->name
) != 0))) {
2390 entry
= ATTR_NEXT(entry
);
2394 *size
+= entry
->namelen
;
2395 entry
= ATTR_NEXT(entry
);
2398 if (uio_resid(uio
) < entry
->namelen
) {
2402 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2404 if (error
!= EFAULT
)
2408 entry
= ATTR_NEXT(entry
);
2412 rel_xattrinfo(&ainfo
);
2413 close_xattrfile(xvp
, FREAD
, context
);
2419 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2421 vnode_t xvp
= NULLVP
;
2422 vnode_t dvp
= NULLVP
;
2423 struct vnode_attr va
;
2424 struct nameidata nd
;
2426 char *filename
= NULL
;
2427 const char *basename
= NULL
;
2433 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2435 * For the root directory use "._." to hold the attributes.
2437 filename
= &smallname
[0];
2438 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2439 dvp
= vp
; /* the "._." file resides in the root dir */
2442 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2446 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2451 /* "._" Attribute files cannot have attributes */
2452 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2453 basename
[0] == '.' && basename
[1] == '_') {
2457 filename
= &smallname
[0];
2458 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2459 if (len
>= sizeof(smallname
)) {
2460 len
++; /* snprintf result doesn't include '\0' */
2461 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2462 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2465 * Note that the lookup here does not authorize. Since we are looking
2466 * up in the same directory that we already have the file vnode in,
2467 * we must have been given the file vnode legitimately. Read/write
2468 * access has already been authorized in layers above for calls from
2469 * userspace, and the authorization code using this path to read
2470 * file security from the EA must always get access
2473 NDINIT(&nd
, LOOKUP
, OP_OPEN
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
,
2474 UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), context
);
2477 if (fileflags
& O_CREAT
) {
2478 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2483 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2485 if ( (error
= namei(&nd
))) {
2490 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2496 * Pick up uid/gid/mode from target file.
2499 VATTR_WANTED(&va
, va_uid
);
2500 VATTR_WANTED(&va
, va_gid
);
2501 VATTR_WANTED(&va
, va_mode
);
2502 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2503 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2504 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2505 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2508 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2509 } else /* fallback values */ {
2510 uid
= KAUTH_UID_NONE
;
2511 gid
= KAUTH_GID_NONE
;
2512 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2516 VATTR_SET(&va
, va_type
, VREG
);
2517 VATTR_SET(&va
, va_mode
, umode
);
2518 if (uid
!= KAUTH_UID_NONE
)
2519 VATTR_SET(&va
, va_uid
, uid
);
2520 if (gid
!= KAUTH_GID_NONE
)
2521 VATTR_SET(&va
, va_gid
, gid
);
2523 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
, &va
,
2524 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2534 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2539 if ((error
= namei(&nd
))) {
2549 if (xvp
->v_type
!= VREG
) {
2554 * Owners must match.
2557 VATTR_WANTED(&va
, va_uid
);
2558 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2559 uid_t owner
= va
.va_uid
;
2562 VATTR_WANTED(&va
, va_uid
);
2563 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2564 error
= ENOATTR
; /* don't use this "._" file */
2569 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2575 if ((error
= vnode_ref(xvp
))) {
2580 /* If create was requested, make sure file header exists. */
2581 if (fileflags
& O_CREAT
) {
2583 VATTR_WANTED(&va
, va_data_size
);
2584 VATTR_WANTED(&va
, va_fileid
);
2585 VATTR_WANTED(&va
, va_nlink
);
2586 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2591 /* If the file is empty then add a default header. */
2592 if (va
.va_data_size
== 0) {
2593 /* Don't adopt hard-linked "._" files. */
2594 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2598 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2602 /* Apply file locking if requested. */
2603 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2606 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2607 error
= lock_xattrfile(xvp
, locktype
, context
);
2613 if (xvp
!= NULLVP
) {
2615 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2618 if (fileflags
& O_CREAT
) {
2619 /* Delete the xattr file if we encountered any errors */
2620 (void) remove_xattrfile (xvp
, context
);
2624 (void) vnode_rele(xvp
);
2626 (void) vnode_put(xvp
);
2629 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2633 /* Release resources after error-handling */
2634 if (dvp
&& (dvp
!= vp
)) {
2638 vnode_putname(basename
);
2640 if (filename
&& filename
!= &smallname
[0]) {
2641 FREE(filename
, M_TEMP
);
2644 *xvpp
= xvp
; /* return a referenced vnode */
2649 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2651 // if (fileflags & FWRITE)
2652 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2654 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2655 (void) unlock_xattrfile(xvp
, context
);
2657 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2658 (void) vnode_rele(xvp
);
2659 (void) vnode_put(xvp
);
2663 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2666 struct nameidata nd
;
2671 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2675 pathlen
= MAXPATHLEN
;
2676 error
= vn_getpath(xvp
, path
, &pathlen
);
2678 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2682 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2683 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2685 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2692 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2701 * Read in and parse the AppleDouble header and entries, and the extended
2702 * attribute header and entries if any. Populates the fields of ainfop
2703 * based on the headers and entries found.
2705 * The basic idea is to:
2706 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2707 * AppleDouble entries, the extended attribute header, and extended
2708 * attribute entries must lie within this part of the file; the rest of
2709 * the AppleDouble handling code assumes this. Plus it allows us to
2710 * somewhat optimize by doing a smaller number of larger I/Os.
2711 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2713 * - Find the Finder Info and Resource Fork entries, if any.
2714 * - If we're going to be writing, try to make sure the Finder Info entry has
2715 * room to store the extended attribute header, plus some space for extended
2717 * - Swap and sanity check the extended attribute header and entries (if any).
2720 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2723 void * buffer
= NULL
;
2724 apple_double_header_t
*filehdr
;
2725 struct vnode_attr va
;
2730 bzero(ainfop
, sizeof(attr_info_t
));
2731 ainfop
->filevp
= xvp
;
2732 ainfop
->context
= context
;
2734 VATTR_WANTED(&va
, va_data_size
);
2735 VATTR_WANTED(&va
, va_fileid
);
2736 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2739 ainfop
->filesize
= va
.va_data_size
;
2741 /* When setting attributes, allow room for the header to grow. */
2743 iosize
= ATTR_MAX_HDR_SIZE
;
2745 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2751 ainfop
->iosize
= iosize
;
2752 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2753 if (buffer
== NULL
){
2758 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2759 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2761 /* Read the file header. */
2762 error
= VNOP_READ(xvp
, auio
, 0, context
);
2766 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2767 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2769 filehdr
= (apple_double_header_t
*)buffer
;
2771 error
= check_and_swap_apple_double_header(ainfop
);
2775 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2777 /* rel_xattrinfo is responsible for freeing the header buffer */
2780 /* Find the Finder Info and Resource Fork entries, if any */
2781 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2782 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2783 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2784 /* We found the Finder Info entry. */
2785 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2788 * Is the Finder Info "empty" (all zeroes)? If so,
2789 * we'll pretend like the Finder Info extended attribute
2792 * Note: we have to make sure the Finder Info is
2793 * contained within the buffer we have already read,
2794 * to avoid accidentally accessing a bogus address.
2795 * If it is outside the buffer, we just assume the
2796 * Finder Info is non-empty.
2798 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2799 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2800 ainfop
->emptyfinderinfo
= 1;
2803 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2805 * Ignore zero-length resource forks when getting. If setting,
2806 * we need to remember the resource fork entry so it can be
2807 * updated once the new content has been written.
2809 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2813 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2815 * The "empty" resource headers we created have a system data tag of:
2816 * "This resource fork intentionally left blank "
2818 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2820 u_int8_t systemData
[64];
2824 /* Read the system data which starts at byte 16 */
2825 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2826 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2827 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2828 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2832 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2833 continue; /* skip this resource fork */
2836 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2837 if (i
!= (filehdr
->numEntries
- 1)) {
2838 printf("get_xattrinfo: resource fork not last entry\n");
2839 ainfop
->readonly
= 1;
2846 * See if this file looks like it is laid out correctly to contain
2847 * extended attributes. If so, then do the following:
2849 * - If we're going to be writing, try to make sure the Finder Info
2850 * entry has room to store the extended attribute header, plus some
2851 * space for extended attributes.
2853 * - Swap and sanity check the extended attribute header and entries
2856 if (filehdr
->numEntries
== 2 &&
2857 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2858 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2859 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2860 attr_header_t
*attrhdr
;
2861 attrhdr
= (attr_header_t
*)filehdr
;
2863 * If we're going to be writing, try to make sure the Finder
2864 * Info entry has room to store the extended attribute header,
2865 * plus some space for extended attributes.
2867 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2871 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2872 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2873 /* Make some room before existing resource fork. */
2874 shift_data_down(xvp
,
2875 filehdr
->entries
[1].offset
,
2876 filehdr
->entries
[1].length
,
2878 writesize
= sizeof(attr_header_t
);
2880 /* Create a new, empty resource fork. */
2881 rsrcfork_header_t
*rsrcforkhdr
;
2883 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2885 /* Steal some space for an empty RF header. */
2886 delta
-= sizeof(rsrcfork_header_t
);
2888 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2889 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2891 /* Fill in Empty Resource Fork Header. */
2892 init_empty_resource_fork(rsrcforkhdr
);
2894 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2895 writesize
= ATTR_BUF_SIZE
;
2897 filehdr
->entries
[0].length
+= delta
;
2898 filehdr
->entries
[1].offset
+= delta
;
2900 /* Fill in Attribute Header. */
2901 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2902 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2903 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2904 attrhdr
->data_start
= sizeof(attr_header_t
);
2905 attrhdr
->data_length
= 0;
2906 attrhdr
->reserved
[0] = 0;
2907 attrhdr
->reserved
[1] = 0;
2908 attrhdr
->reserved
[2] = 0;
2910 attrhdr
->num_attrs
= 0;
2912 /* Push out new header */
2913 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2914 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2916 swap_adhdr(filehdr
); /* to big endian */
2917 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2918 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2919 swap_adhdr(filehdr
); /* back to native */
2920 /* The attribute header gets swapped below. */
2924 * Swap and sanity check the extended attribute header and
2925 * entries (if any). The Finder Info content must be big enough
2926 * to include the extended attribute header; if not, we just
2929 * Note that we're passing the offset + length (i.e. the end)
2930 * of the Finder Info instead of rawsize to validate_attrhdr.
2931 * This ensures that all extended attributes lie within the
2932 * Finder Info content according to the AppleDouble entry.
2934 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2937 if (ainfop
->finderinfo
&&
2938 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2939 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2940 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2942 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2943 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2944 /* First attr_entry starts immediately following attribute header */
2945 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2954 FREE(buffer
, M_TEMP
);
2960 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2963 rsrcfork_header_t
*rsrcforkhdr
;
2969 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2970 bzero(buffer
, ATTR_BUF_SIZE
);
2972 xah
= (attr_header_t
*)buffer
;
2973 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2974 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2975 rsrcforksize
= sizeof(rsrcfork_header_t
);
2976 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2978 /* Fill in Apple Double Header. */
2979 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2980 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2981 xah
->appledouble
.numEntries
= SWAP16 (2);
2982 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2983 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2984 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2985 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2986 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2987 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2988 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2990 /* Fill in Attribute Header. */
2991 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2992 xah
->debug_tag
= SWAP32 (fileid
);
2993 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2994 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2996 /* Fill in Empty Resource Fork Header. */
2997 init_empty_resource_fork(rsrcforkhdr
);
3000 error
= VNOP_WRITE(xvp
, auio
, IO_UNIT
, context
);
3002 /* Did we write out the full uio? */
3003 if (uio_resid(auio
) > 0) {
3008 FREE(buffer
, M_TEMP
);
3014 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
3016 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
3017 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3018 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3019 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
3020 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3021 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3022 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
3023 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
3024 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
3025 rsrcforkhdr
->typeCount
= SWAP16 (-1);
3026 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
3030 rel_xattrinfo(attr_info_t
*ainfop
)
3032 FREE(ainfop
->filehdr
, M_TEMP
);
3033 bzero(ainfop
, sizeof(attr_info_t
));
3037 write_xattrinfo(attr_info_t
*ainfop
)
3042 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3043 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
3045 swap_adhdr(ainfop
->filehdr
);
3046 if (ainfop
->attrhdr
!= NULL
) {
3047 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3050 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
3052 swap_adhdr(ainfop
->filehdr
);
3053 if (ainfop
->attrhdr
!= NULL
) {
3054 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3061 #if BYTE_ORDER == LITTLE_ENDIAN
3063 * Endian swap apple double header
3066 swap_adhdr(apple_double_header_t
*adh
)
3071 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
3073 adh
->magic
= SWAP32 (adh
->magic
);
3074 adh
->version
= SWAP32 (adh
->version
);
3075 adh
->numEntries
= SWAP16 (adh
->numEntries
);
3077 for (i
= 0; i
< count
; i
++) {
3078 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
3079 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
3080 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
3085 * Endian swap extended attributes header
3088 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
3094 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
3096 ah
->magic
= SWAP32 (ah
->magic
);
3097 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3098 ah
->total_size
= SWAP32 (ah
->total_size
);
3099 ah
->data_start
= SWAP32 (ah
->data_start
);
3100 ah
->data_length
= SWAP32 (ah
->data_length
);
3101 ah
->flags
= SWAP16 (ah
->flags
);
3102 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3104 ae
= (attr_entry_t
*)(&ah
[1]);
3105 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
3106 ae
->offset
= SWAP32 (ae
->offset
);
3107 ae
->length
= SWAP32 (ae
->length
);
3108 ae
->flags
= SWAP16 (ae
->flags
);
3114 * Validate and swap the attributes header contents, and each attribute's
3117 * Note: Assumes the caller has verified that the Finder Info content is large
3118 * enough to contain the attr_header structure itself. Therefore, we can
3119 * swap the header fields before sanity checking them.
3122 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
3133 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
3136 /* Swap the basic header fields */
3137 ah
->magic
= SWAP32(ah
->magic
);
3138 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3139 ah
->total_size
= SWAP32 (ah
->total_size
);
3140 ah
->data_start
= SWAP32 (ah
->data_start
);
3141 ah
->data_length
= SWAP32 (ah
->data_length
);
3142 ah
->flags
= SWAP16 (ah
->flags
);
3143 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3146 * Make sure the total_size fits within the Finder Info area, and the
3147 * extended attribute data area fits within total_size.
3149 end
= ah
->data_start
+ ah
->data_length
;
3150 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3151 end
< ah
->data_start
||
3152 end
> ah
->total_size
) {
3157 * Make sure each of the attr_entry_t's fits within total_size.
3159 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3160 count
= ah
->num_attrs
;
3161 ae
= (attr_entry_t
*)(&ah
[1]);
3163 for (i
=0; i
<count
; i
++) {
3164 /* Make sure the fixed-size part of this attr_entry_t fits. */
3165 if ((u_int8_t
*) &ae
[1] > buf_end
)
3168 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3169 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3170 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
3173 /* Swap the attribute entry fields */
3174 ae
->offset
= SWAP32(ae
->offset
);
3175 ae
->length
= SWAP32(ae
->length
);
3176 ae
->flags
= SWAP16(ae
->flags
);
3178 /* Make sure the attribute content fits. */
3179 end
= ae
->offset
+ ae
->length
;
3180 if (end
< ae
->offset
|| end
> ah
->total_size
)
3187 * TODO: Make sure the contents of attributes don't overlap the header
3188 * and don't overlap each other. The hard part is that we don't know
3189 * what the actual header size is until we have looped over all of the
3190 * variable-sized attribute entries.
3192 * XXX Is there any guarantee that attribute entries are stored in
3193 * XXX order sorted by the contents' file offset? If so, that would
3194 * XXX make the pairwise overlap check much easier.
3201 // "start" & "end" are byte offsets in the file.
3202 // "to" is the byte offset we want to move the
3203 // data to. "to" should be > "start".
3205 // we do the copy backwards to avoid problems if
3206 // there's an overlap.
3209 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3212 size_t chunk
, orig_chunk
;
3215 kauth_cred_t ucred
= vfs_context_ucred(context
);
3216 proc_t p
= vfs_context_proc(context
);
3218 if (delta
== 0 || len
== 0) {
3228 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3232 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
3233 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3235 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3236 pos
, ret
, chunk
, ret
);
3240 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3242 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3243 pos
+delta
, ret
, chunk
, ret
);
3247 if ((pos
- (off_t
)chunk
) < start
) {
3248 chunk
= pos
- start
;
3250 if (chunk
== 0) { // we're all done
3255 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3262 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3265 size_t chunk
, orig_chunk
;
3269 kauth_cred_t ucred
= vfs_context_ucred(context
);
3270 proc_t p
= vfs_context_proc(context
);
3272 if (delta
== 0 || len
== 0) {
3283 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3287 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3288 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3290 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3291 pos
, ret
, chunk
, ret
);
3295 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3297 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3298 pos
+delta
, ret
, chunk
, ret
);
3302 if ((pos
+ (off_t
)chunk
) > end
) {
3305 if (chunk
== 0) { // we're all done
3310 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3316 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3321 lf
.l_whence
= SEEK_SET
;
3324 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3325 /* Note: id is just a kernel address that's not a proc */
3326 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
, NULL
);
3327 return (error
== ENOTSUP
? 0 : error
);
3331 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3336 lf
.l_whence
= SEEK_SET
;
3339 lf
.l_type
= F_UNLCK
;
3340 /* Note: id is just a kernel address that's not a proc */
3341 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
, NULL
);
3342 return (error
== ENOTSUP
? 0 : error
);
3345 #else /* CONFIG_APPLEDOUBLE */
3349 default_getxattr(__unused vnode_t vp
, __unused
const char *name
,
3350 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3351 __unused vfs_context_t context
)
3357 default_setxattr(__unused vnode_t vp
, __unused
const char *name
,
3358 __unused uio_t uio
, __unused
int options
, __unused vfs_context_t context
)
3364 default_listxattr(__unused vnode_t vp
,
3365 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3366 __unused vfs_context_t context
)
3372 default_removexattr(__unused vnode_t vp
, __unused
const char *name
,
3373 __unused
int options
, __unused vfs_context_t context
)
3378 #endif /* CONFIG_APPLEDOUBLE */