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));
401 vnode_setasnamedstream_internal(vnode_t vp
, vnode_t svp
)
403 uint32_t streamflags
= VISNAMEDSTREAM
;
405 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
406 streamflags
|= VISSHADOW
;
410 vnode_lock_spin(svp
);
411 svp
->v_flag
|= streamflags
;
414 /* Tag the parent so we know to flush credentials for streams on setattr */
416 vp
->v_lflag
|= VL_HASSTREAMS
;
419 /* Make the file it's parent.
420 * Note: This parent link helps us distinguish vnodes for
421 * shadow stream files from vnodes for resource fork on file
422 * systems that support namedstream natively (both have
423 * VISNAMEDSTREAM set) by allowing access to mount structure
424 * for checking MNTK_NAMED_STREAMS bit at many places in the
427 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_NAMEDSTREAM_PARENT
);
433 vnode_setasnamedstream(vnode_t vp
, vnode_t svp
)
435 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0)
438 vnode_setasnamedstream_internal(vp
, svp
);
445 * Obtain a named stream from vnode vp.
448 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
452 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) {
453 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
458 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
462 vnode_setasnamedstream_internal(vp
, *svpp
);
469 * Make a named stream for vnode vp.
472 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
476 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
477 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
479 error
= default_makenamedstream(vp
, svpp
, name
, context
);
482 vnode_setasnamedstream_internal(vp
, *svpp
);
489 * Remove a named stream from vnode vp.
492 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
496 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
497 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
499 error
= default_removenamedstream(vp
, name
, context
);
504 #define NS_IOBUFSIZE (128 * 1024)
507 * Release a named stream shadow file.
509 * Note: This function is called from two places where we do not need
510 * to check if the vnode has any references held before deleting the
511 * shadow file. Once from vclean() when the vnode is being reclaimed
512 * and we do not hold any references on the vnode. Second time from
513 * default_getnamedstream() when we get an error during shadow stream
514 * file initialization so that other processes who are waiting for the
515 * shadow stream file initialization by the creator will get opportunity
516 * to create and initialize the file again.
519 vnode_relenamedstream(vnode_t vp
, vnode_t svp
) {
521 struct componentname cn
;
526 * We need to use the kernel context here. If we used the supplied
527 * VFS context we have no clue whether or not it originated from userland
528 * where it could be subject to a chroot jail. We need to ensure that all
529 * filesystem access to shadow files is done on the same FS regardless of
530 * userland process restrictions.
532 vfs_context_t kernelctx
= vfs_context_kernel();
537 MAKE_SHADOW_NAME(vp
, tmpname
);
540 cn
.cn_nameiop
= DELETE
;
541 cn
.cn_flags
= ISLASTCN
;
542 cn
.cn_context
= kernelctx
;
543 cn
.cn_pnbuf
= tmpname
;
544 cn
.cn_pnlen
= sizeof(tmpname
);
545 cn
.cn_nameptr
= cn
.cn_pnbuf
;
546 cn
.cn_namelen
= strlen(tmpname
);
549 * Obtain the vnode for the shadow files directory. Make sure to
550 * use the kernel ctx as described above.
552 err
= get_shadow_dir(&dvp
);
557 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, kernelctx
);
564 * Flush a named stream shadow file.
566 * 'vp' represents the AppleDouble file.
567 * 'svp' represents the shadow file.
570 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
572 struct vnode_attr va
;
574 caddr_t bufptr
= NULL
;
581 * The kernel context must be used for all I/O to the shadow file
582 * and its namespace operations
584 vfs_context_t kernelctx
= vfs_context_kernel();
586 /* The supplied context is used for access to the AD file itself */
589 VATTR_WANTED(&va
, va_data_size
);
590 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
591 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
594 datasize
= va
.va_data_size
;
596 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
600 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
601 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
604 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
608 * Copy the shadow stream file data into the resource fork.
610 error
= VNOP_OPEN(svp
, 0, kernelctx
);
612 printf("vnode_flushnamedstream: err %d opening file\n", error
);
615 while (offset
< datasize
) {
616 iosize
= MIN(datasize
- offset
, iosize
);
618 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
619 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
620 error
= VNOP_READ(svp
, auio
, 0, kernelctx
);
624 /* Since there's no truncate xattr we must remove the resource fork. */
626 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
627 if ((error
!= 0) && (error
!= ENOATTR
)) {
631 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
632 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
633 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
640 /* close shadowfile */
641 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
644 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
654 * Verify that the vnode 'vp' is a vnode that lives in the shadow
655 * directory. We can't just query the parent pointer directly since
656 * the shadowfile is hooked up to the actual file it's a stream for.
658 errno_t
vnode_verifynamedstream(vnode_t vp
) {
660 struct vnode
*shadow_dvp
= NULL
;
661 struct vnode
*shadowfile
= NULL
;
662 struct componentname cn
;
665 * We need to use the kernel context here. If we used the supplied
666 * VFS context we have no clue whether or not it originated from userland
667 * where it could be subject to a chroot jail. We need to ensure that all
668 * filesystem access to shadow files is done on the same FS regardless of
669 * userland process restrictions.
671 vfs_context_t kernelctx
= vfs_context_kernel();
675 /* Get the shadow directory vnode */
676 error
= get_shadow_dir(&shadow_dvp
);
681 /* Re-generate the shadow name in the buffer */
682 MAKE_SHADOW_NAME (vp
, tmpname
);
684 /* Look up item in shadow dir */
685 bzero(&cn
, sizeof(cn
));
686 cn
.cn_nameiop
= LOOKUP
;
687 cn
.cn_flags
= ISLASTCN
| CN_ALLOWRSRCFORK
;
688 cn
.cn_context
= kernelctx
;
689 cn
.cn_pnbuf
= tmpname
;
690 cn
.cn_pnlen
= sizeof(tmpname
);
691 cn
.cn_nameptr
= cn
.cn_pnbuf
;
692 cn
.cn_namelen
= strlen(tmpname
);
694 if (VNOP_LOOKUP (shadow_dvp
, &shadowfile
, &cn
, kernelctx
) == 0) {
695 /* is the pointer the same? */
696 if (shadowfile
== vp
) {
702 /* drop the iocount acquired */
703 vnode_put (shadowfile
);
706 /* Drop iocount on shadow dir */
707 vnode_put (shadow_dvp
);
712 * Access or create the shadow file as needed.
714 * 'makestream' with non-zero value means that we need to guarantee we were the
715 * creator of the shadow file.
717 * 'context' is the user supplied context for the original VFS operation that
718 * caused us to need a shadow file.
720 * int pointed to by 'creator' is nonzero if we created the shadowfile.
723 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
724 int *creator
, vfs_context_t context
)
726 vnode_t dvp
= NULLVP
;
727 vnode_t svp
= NULLVP
;
728 struct componentname cn
;
729 struct vnode_attr va
;
734 vfs_context_t kernelctx
= vfs_context_kernel();
738 /* Establish a unique file name. */
739 MAKE_SHADOW_NAME(vp
, tmpname
);
740 bzero(&cn
, sizeof(cn
));
741 cn
.cn_nameiop
= LOOKUP
;
742 cn
.cn_flags
= ISLASTCN
;
743 cn
.cn_context
= context
;
744 cn
.cn_pnbuf
= tmpname
;
745 cn
.cn_pnlen
= sizeof(tmpname
);
746 cn
.cn_nameptr
= cn
.cn_pnbuf
;
747 cn
.cn_namelen
= strlen(tmpname
);
749 /* Pick up uid, gid, mode and date from original file. */
751 VATTR_WANTED(&va
, va_uid
);
752 VATTR_WANTED(&va
, va_gid
);
753 VATTR_WANTED(&va
, va_mode
);
754 VATTR_WANTED(&va
, va_create_time
);
755 VATTR_WANTED(&va
, va_modify_time
);
756 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
757 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
758 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
759 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
760 va
.va_uid
= KAUTH_UID_NONE
;
761 va
.va_gid
= KAUTH_GID_NONE
;
762 va
.va_mode
= S_IRUSR
| S_IWUSR
;
764 va
.va_vaflags
= VA_EXCLUSIVE
;
765 VATTR_SET(&va
, va_type
, VREG
);
766 /* We no longer change the access, but we still hide it. */
767 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
769 /* Obtain the vnode for the shadow files directory. */
770 if (get_shadow_dir(&dvp
) != 0) {
775 /* See if someone else already has it open. */
776 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
) == 0) {
777 /* Double check existence by asking for size. */
779 VATTR_WANTED(&va
, va_data_size
);
780 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
781 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
782 goto out
; /* OK to use. */
787 * Otherwise make sure the resource fork data exists.
788 * Use the supplied context for accessing the AD file.
790 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
791 XATTR_NOSECURITY
, context
);
793 * To maintain binary compatibility with legacy Carbon
794 * emulated resource fork support, if the resource fork
795 * doesn't exist but the Finder Info does, then act as
796 * if an empty resource fork is present (see 4724359).
798 if ((error
== ENOATTR
) &&
799 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
800 XATTR_NOSECURITY
, context
) == 0)) {
808 /* If the resource fork exists, its size is expected to be non-zero. */
815 /* Create the shadow stream file. */
816 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, kernelctx
);
821 else if ((error
== EEXIST
) && !makestream
) {
822 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
);
824 else if ((error
== ENOENT
) && !makestream
) {
826 * We could have raced with a rmdir on the shadow directory
827 * post-lookup. Retry from the beginning, 1x only, to
828 * try and see if we need to re-create the shadow directory
843 /* Otherwise, just error out normally below */
851 /* On errors, clean up shadow stream file. */
859 *rsrcsize
= datasize
;
866 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
868 vnode_t svp
= NULLVP
;
870 caddr_t bufptr
= NULL
;
876 /* need the kernel context for accessing the shadowfile */
877 vfs_context_t kernelctx
= vfs_context_kernel();
880 * Only the "com.apple.ResourceFork" stream is supported here.
882 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
888 * Obtain a shadow file for the resource fork I/O.
890 * Need to pass along the supplied context so that getshadowfile
891 * can access the AD file as needed, using it.
893 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
900 * The creator of the shadow file provides its file data,
901 * all other threads should wait until its ready. In order to
902 * prevent a deadlock during error codepaths, we need to check if the
903 * vnode is being created, or if it has failed out. Regardless of success or
904 * failure, we set the VISSHADOW bit on the vnode, so we check that
905 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
906 * then we can infer the creator isn't done yet. If it's there, but
907 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
912 if (svp
->v_flag
& VISNAMEDSTREAM
) {
913 /* data is ready, go use it */
917 /* It's not ready, wait for it (sleep using v_parent as channel) */
918 if ((svp
->v_flag
& VISSHADOW
)) {
920 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
921 * thread is done with this vnode. Just unlock the vnode and try again
926 /* Otherwise, sleep if the shadow file is not created yet */
927 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
928 "getnamedstream", NULL
);
937 * Copy the real resource fork data into shadow stream file.
939 if (op
== NS_OPEN
&& datasize
!= 0) {
943 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
944 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
949 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
952 /* open the shadow file */
953 error
= VNOP_OPEN(svp
, 0, kernelctx
);
957 while (offset
< datasize
) {
960 iosize
= MIN(datasize
- offset
, iosize
);
962 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
963 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
964 /* use supplied ctx for AD file */
965 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
966 XATTR_NOSECURITY
, context
);
971 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
972 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
973 /* kernel context for writing shadowfile */
974 error
= VNOP_WRITE(svp
, auio
, 0, kernelctx
);
981 /* close shadow file */
982 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
985 /* Wake up anyone waiting for svp file content */
989 /* VISSHADOW would be set later on anyway, so we set it now */
990 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
991 wakeup((caddr_t
)&svp
->v_parent
);
994 /* On post create errors, get rid of the shadow file. This
995 * way if there is another process waiting for initialization
996 * of the shadowfile by the current process will wake up and
997 * retry by creating and initializing the shadow file again.
998 * Also add the VISSHADOW bit here to indicate we're done operating
1001 (void)vnode_relenamedstream(vp
, svp
);
1003 svp
->v_flag
|= VISSHADOW
;
1004 wakeup((caddr_t
)&svp
->v_parent
);
1010 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
1016 /* On errors, clean up shadow stream file. */
1027 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
1033 * Only the "com.apple.ResourceFork" stream is supported here.
1035 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1040 /* Supply the context to getshadowfile so it can manipulate the AD file */
1041 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
1044 * Wake up any waiters over in default_getnamedstream().
1046 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
1047 vnode_t svp
= *svpp
;
1050 /* If we're the creator, mark it as a named stream */
1051 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
1052 /* Wakeup any waiters on the v_parent channel */
1053 wakeup((caddr_t
)&svp
->v_parent
);
1062 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
1065 * Only the "com.apple.ResourceFork" stream is supported here.
1067 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1071 * XXX - what about other opened instances?
1073 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
1077 get_shadow_dir(vnode_t
*sdvpp
) {
1078 vnode_t dvp
= NULLVP
;
1079 vnode_t sdvp
= NULLVP
;
1080 struct componentname cn
;
1081 struct vnode_attr va
;
1085 vfs_context_t kernelctx
= vfs_context_kernel();
1087 bzero(tmpname
, sizeof(tmpname
));
1088 MAKE_SHADOW_DIRNAME(rootvnode
, tmpname
);
1090 * Look up the shadow directory to ensure that it still exists.
1091 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1092 * in caching it when multiple threads may be trying to manipulate the pointers.
1094 * Make sure to use the kernel context. We want a singular view of
1095 * the shadow dir regardless of chrooted processes.
1097 error
= vnode_lookup(tmpname
, 0, &sdvp
, kernelctx
);
1100 * If we get here, then we have successfully looked up the shadow dir,
1101 * and it has an iocount from the lookup. Return the vp in the output argument.
1106 /* In the failure case, no iocount is acquired */
1108 bzero (tmpname
, sizeof(tmpname
));
1111 * Obtain the vnode for "/var/run" directory using the kernel
1114 * This is defined in the SHADOW_DIR_CONTAINER macro
1116 if (vnode_lookup(SHADOW_DIR_CONTAINER
, 0, &dvp
, kernelctx
) != 0) {
1122 * Create the shadow stream directory.
1123 * 'dvp' below suggests the parent directory so
1124 * we only need to provide the leaf entry name
1126 MAKE_SHADOW_DIR_LEAF(rootvnode
, tmpname
);
1127 bzero(&cn
, sizeof(cn
));
1128 cn
.cn_nameiop
= LOOKUP
;
1129 cn
.cn_flags
= ISLASTCN
;
1130 cn
.cn_context
= kernelctx
;
1131 cn
.cn_pnbuf
= tmpname
;
1132 cn
.cn_pnlen
= sizeof(tmpname
);
1133 cn
.cn_nameptr
= cn
.cn_pnbuf
;
1134 cn
.cn_namelen
= strlen(tmpname
);
1137 * owned by root, only readable by root, hidden
1140 VATTR_SET(&va
, va_uid
, 0);
1141 VATTR_SET(&va
, va_gid
, 0);
1142 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
1143 VATTR_SET(&va
, va_type
, VDIR
);
1144 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
1145 va
.va_vaflags
= VA_EXCLUSIVE
;
1147 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, kernelctx
);
1150 * There can be only one winner for an exclusive create.
1152 if (error
== EEXIST
) {
1153 /* loser has to look up directory */
1154 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, kernelctx
);
1156 /* Make sure its in fact a directory */
1157 if (sdvp
->v_type
!= VDIR
) {
1160 /* Obtain the fsid for /var/run directory */
1162 VATTR_WANTED(&va
, va_fsid
);
1163 if (VNOP_GETATTR(dvp
, &va
, kernelctx
) != 0 ||
1164 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1167 tmp_fsid
= va
.va_fsid
;
1170 VATTR_WANTED(&va
, va_uid
);
1171 VATTR_WANTED(&va
, va_gid
);
1172 VATTR_WANTED(&va
, va_mode
);
1173 VATTR_WANTED(&va
, va_fsid
);
1174 VATTR_WANTED(&va
, va_dirlinkcount
);
1175 VATTR_WANTED(&va
, va_acl
);
1176 /* Provide defaults for attrs that may not be supported */
1177 va
.va_dirlinkcount
= 1;
1178 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1180 if (VNOP_GETATTR(sdvp
, &va
, kernelctx
) != 0 ||
1181 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1182 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1183 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1184 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1188 * Make sure its what we want:
1190 * - not writable by anyone
1191 * - on same file system as /var/run
1192 * - not a hard-linked directory
1193 * - no ACLs (they might grant write access)
1195 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1196 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1197 (va
.va_fsid
!= tmp_fsid
) ||
1198 (va
.va_dirlinkcount
!= 1) ||
1199 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1209 /* On errors, clean up shadow stream directory. */
1219 /* This is not the dir we're looking for, move along */
1220 ++shadow_sequence
; /* try something else next time */
1224 #endif /* NAMEDSTREAMS */
1227 #if CONFIG_APPLEDOUBLE
1229 * Default Implementation (Non-native EA)
1234 Typical "._" AppleDouble Header File layout:
1235 ------------------------------------------------------------
1240 .-- AD ENTRY[0] Finder Info Entry (must be first)
1241 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1243 | ///////////// Fixed Size Data (32 bytes)
1247 | ATTR ENTRY[1] --+--.
1248 | ATTR ENTRY[2] --+--+--.
1250 | ATTR ENTRY[N] --+--+--+--.
1251 | ATTR DATA 0 <-' | | |
1252 | //////////// | | |
1253 | ATTR DATA 1 <----' | |
1255 | ATTR DATA 2 <-------' |
1258 | ATTR DATA N <----------'
1260 | Attribute Free Space
1262 '----> RESOURCE FORK
1263 ///////////// Variable Sized Data
1272 ------------------------------------------------------------
1274 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1275 stored as part of the Finder Info. The length in the Finder
1276 Info AppleDouble entry includes the length of the extended
1277 attribute header, attribute entries, and attribute data.
1282 * On Disk Data Structures
1284 * Note: Motorola 68K alignment and big-endian.
1286 * See RFC 1740 for additional information about the AppleDouble file format.
1290 #define ADH_MAGIC 0x00051607
1291 #define ADH_VERSION 0x00020000
1292 #define ADH_MACOSX "Mac OS X "
1295 * AppleDouble Entry ID's
1297 #define AD_DATA 1 /* Data fork */
1298 #define AD_RESOURCE 2 /* Resource fork */
1299 #define AD_REALNAME 3 /* FileÕs name on home file system */
1300 #define AD_COMMENT 4 /* Standard Mac comment */
1301 #define AD_ICONBW 5 /* Mac black & white icon */
1302 #define AD_ICONCOLOR 6 /* Mac color icon */
1303 #define AD_UNUSED 7 /* Not used */
1304 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1305 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1306 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1307 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1308 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1309 #define AD_AFPNAME 13 /* Short name on AFP server */
1310 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1311 #define AD_AFPDIRID 15 /* AFP directory ID */
1312 #define AD_ATTRIBUTES AD_FINDERINFO
1315 #define ATTR_FILE_PREFIX "._"
1316 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1318 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1320 /* Implementation Limits */
1321 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1322 #define ATTR_MAX_HDR_SIZE 65536
1324 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1325 * size supported (including the attribute entries). All of
1326 * the attribute entries must reside within this limit. If
1327 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1328 * boundry, then all of the attribute data I/O is performed
1329 * separately from the attribute header I/O.
1331 * In particular, all of the attr_entry structures must lie
1332 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1333 * AppleDouble file. However, the attribute data (i.e. the
1334 * contents of the extended attributes) may extend beyond the
1335 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1336 * limit is to allow the implementation to optimize by reading
1337 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1341 #define FINDERINFOSIZE 32
1343 typedef struct apple_double_entry
{
1344 u_int32_t type
; /* entry type: see list, 0 invalid */
1345 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1346 u_int32_t length
; /* entry data length in bytes. */
1347 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1350 typedef struct apple_double_header
{
1351 u_int32_t magic
; /* == ADH_MAGIC */
1352 u_int32_t version
; /* format version: 2 = 0x00020000 */
1353 u_int32_t filler
[4];
1354 u_int16_t numEntries
; /* number of entries which follow */
1355 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1356 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1357 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1358 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1360 #define ADHDRSIZE (4+4+16+2)
1362 /* Entries are aligned on 4 byte boundaries */
1363 typedef struct attr_entry
{
1364 u_int32_t offset
; /* file offset to data */
1365 u_int32_t length
; /* size of attribute data */
1368 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1369 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1372 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1373 typedef struct attr_header
{
1374 apple_double_header_t appledouble
;
1375 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1376 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1377 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1378 u_int32_t data_start
; /* file offset to attribute data area */
1379 u_int32_t data_length
; /* length of attribute data area */
1380 u_int32_t reserved
[3];
1382 u_int16_t num_attrs
;
1383 } __attribute__((aligned(2), packed
)) attr_header_t
;
1386 /* Empty Resource Fork Header */
1387 typedef struct rsrcfork_header
{
1388 u_int32_t fh_DataOffset
;
1389 u_int32_t fh_MapOffset
;
1390 u_int32_t fh_DataLength
;
1391 u_int32_t fh_MapLength
;
1392 u_int8_t systemData
[112];
1393 u_int8_t appData
[128];
1394 u_int32_t mh_DataOffset
;
1395 u_int32_t mh_MapOffset
;
1396 u_int32_t mh_DataLength
;
1397 u_int32_t mh_MapLength
;
1399 u_int16_t mh_RefNum
;
1401 u_int8_t mh_InMemoryAttr
;
1404 u_int16_t typeCount
;
1405 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1407 #define RF_FIRST_RESOURCE 256
1408 #define RF_NULL_MAP_LENGTH 30
1409 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1411 /* Runtime information about the attribute file. */
1412 typedef struct attr_info
{
1413 vfs_context_t context
;
1418 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1419 apple_double_header_t
*filehdr
;
1420 apple_double_entry_t
*finderinfo
;
1421 apple_double_entry_t
*rsrcfork
;
1422 attr_header_t
*attrhdr
;
1423 attr_entry_t
*attr_entry
;
1425 u_int8_t emptyfinderinfo
;
1429 #define ATTR_SETTING 1
1431 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1433 #define ATTR_ENTRY_LENGTH(namelen) \
1434 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1436 #define ATTR_NEXT(ae) \
1437 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1439 #define ATTR_VALID(ae, ai) \
1440 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1442 #define SWAP16(x) OSSwapBigToHostInt16((x))
1443 #define SWAP32(x) OSSwapBigToHostInt32((x))
1444 #define SWAP64(x) OSSwapBigToHostInt64((x))
1447 static u_int32_t emptyfinfo
[8] = {0};
1451 * Local support routines
1453 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1455 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1457 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1459 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1461 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1463 static void rel_xattrinfo(attr_info_t
*ainfop
);
1465 static int write_xattrinfo(attr_info_t
*ainfop
);
1467 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1469 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1471 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1474 #if BYTE_ORDER == LITTLE_ENDIAN
1475 static void swap_adhdr(apple_double_header_t
*adh
);
1476 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1479 #define swap_adhdr(x)
1480 #define swap_attrhdr(x, y)
1483 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1484 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1485 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1489 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1490 * is in big endian (as it would exist on disk). Verifies the following:
1493 * - number of entries
1494 * - that each entry fits within the file size
1496 * If the header is invalid, ENOATTR is returned.
1498 * NOTE: Does not attempt to validate the extended attributes header that
1499 * may be embedded in the Finder Info entry.
1501 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1504 u_int32_t header_end
;
1505 u_int32_t entry_end
;
1507 apple_double_header_t
*header
;
1509 rawsize
= ainfop
->rawsize
;
1510 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1512 /* Is the file big enough to contain an AppleDouble header? */
1513 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1516 /* Swap the AppleDouble header fields to native order */
1517 header
->magic
= SWAP32(header
->magic
);
1518 header
->version
= SWAP32(header
->version
);
1519 header
->numEntries
= SWAP16(header
->numEntries
);
1521 /* Sanity check the AppleDouble header fields */
1522 if (header
->magic
!= ADH_MAGIC
||
1523 header
->version
!= ADH_VERSION
||
1524 header
->numEntries
< 1 ||
1525 header
->numEntries
> 15) {
1529 /* Calculate where the entries[] array ends */
1530 header_end
= offsetof(apple_double_header_t
, entries
) +
1531 header
->numEntries
* sizeof(apple_double_entry_t
);
1533 /* Is the file big enough to contain the AppleDouble entries? */
1534 if (rawsize
< header_end
) {
1538 /* Swap and sanity check each AppleDouble entry */
1539 for (i
=0; i
<header
->numEntries
; i
++) {
1540 /* Swap the per-entry fields to native order */
1541 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1542 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1543 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1545 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1548 * Does the entry's content start within the header itself,
1549 * did the addition overflow, or does the entry's content
1550 * extend past the end of the file?
1552 if (header
->entries
[i
].offset
< header_end
||
1553 entry_end
< header
->entries
[i
].offset
||
1554 entry_end
> ainfop
->filesize
) {
1559 * Does the current entry's content overlap with a previous
1562 * Yes, this is O(N**2), and there are more efficient algorithms
1563 * for testing pairwise overlap of N ranges when N is large.
1564 * But we have already ensured N < 16, and N is almost always 2.
1565 * So there's no point in using a more complex algorithm.
1568 for (j
=0; j
<i
; j
++) {
1569 if (entry_end
> header
->entries
[j
].offset
&&
1570 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1582 * Retrieve the data of an extended attribute.
1585 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1586 __unused
int options
, vfs_context_t context
)
1590 attr_header_t
*header
;
1591 attr_entry_t
*entry
;
1601 if (strcmp(name
, XATTR_RESOURCEFORK_NAME
) == 0) {
1604 * Open the file locked (shared) since the Carbon
1605 * File Manager may have the Apple Double file open
1606 * and could be changing the resource fork.
1608 fileflags
|= O_SHLOCK
;
1613 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1616 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1617 close_xattrfile(xvp
, fileflags
, context
);
1621 /* Get the Finder Info. */
1622 if (strcmp(name
, XATTR_FINDERINFO_NAME
) == 0) {
1624 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1626 } else if (uio
== NULL
) {
1627 *size
= FINDERINFOSIZE
;
1629 } else if (uio_offset(uio
) != 0) {
1631 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1634 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1635 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1640 /* Read the Resource Fork. */
1642 if (!vnode_isreg(vp
)) {
1644 } else if (ainfo
.rsrcfork
== NULL
) {
1646 } else if (uio
== NULL
) {
1647 *size
= (size_t)ainfo
.rsrcfork
->length
;
1649 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1650 error
= VNOP_READ(xvp
, uio
, 0, context
);
1652 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1657 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1661 if (uio_offset(uio
) != 0) {
1666 namelen
= strlen(name
) + 1;
1667 header
= ainfo
.attrhdr
;
1668 entry
= ainfo
.attr_entry
;
1670 * Search for attribute name in the header.
1672 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1673 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1674 datalen
= (size_t)entry
->length
;
1680 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1684 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1685 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1686 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1688 uio_setoffset(uio
, entry
->offset
);
1689 error
= VNOP_READ(xvp
, uio
, 0, context
);
1690 uio_setoffset(uio
, 0);
1694 entry
= ATTR_NEXT(entry
);
1697 rel_xattrinfo(&ainfo
);
1698 close_xattrfile(xvp
, fileflags
, context
);
1704 * Set the data of an extended attribute.
1707 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1711 attr_header_t
*header
;
1712 attr_entry_t
*entry
;
1713 attr_entry_t
*lastentry
;
1717 size_t datafreespace
;
1724 char finfo
[FINDERINFOSIZE
];
1726 datalen
= uio_resid(uio
);
1727 namelen
= strlen(name
) + 1;
1728 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1731 * By convention, Finder Info that is all zeroes is equivalent to not
1732 * having a Finder Info EA. So if we're trying to set the Finder Info
1733 * to all zeroes, then delete it instead. If a file didn't have an
1734 * AppleDouble file before, this prevents creating an AppleDouble file
1735 * with no useful content.
1737 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1738 * for all zeroes Finder Info before opening the AppleDouble file.
1739 * But if either of those options were specified, we need to open the
1740 * AppleDouble file to see whether there was already Finder Info (so we
1741 * can return an error if needed); this case is handled further below.
1743 * NOTE: this copies the Finder Info data into the "finfo" local.
1745 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1747 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1748 * That means we probably have to open_xattrfile and get_xattrinfo.
1750 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1753 error
= uiomove(finfo
, datalen
, uio
);
1756 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1757 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1758 error
= default_removexattr(vp
, name
, 0, context
);
1759 if (error
== ENOATTR
)
1767 * Open the file locked since setting an attribute
1768 * can change the layout of the Apple Double file.
1770 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1771 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1774 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1775 close_xattrfile(xvp
, fileflags
, context
);
1779 /* Set the Finder Info. */
1780 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1781 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1782 /* attr exists and "create" was specified? */
1783 if (options
& XATTR_CREATE
) {
1788 /* attr doesn't exists and "replace" was specified? */
1789 if (options
& XATTR_REPLACE
) {
1794 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1796 * Setting the Finder Info to all zeroes is equivalent to
1797 * removing it. Close the xattr file and let
1798 * default_removexattr do the work (including deleting
1799 * the xattr file if there are no other xattrs).
1801 * Note that we have to handle the case where the
1802 * Finder Info was already all zeroes, and we ignore
1805 * The common case where options == 0 was handled above.
1807 rel_xattrinfo(&ainfo
);
1808 close_xattrfile(xvp
, fileflags
, context
);
1809 error
= default_removexattr(vp
, name
, 0, context
);
1810 if (error
== ENOATTR
)
1814 if (ainfo
.finderinfo
) {
1815 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1816 bcopy(finfo
, attrdata
, datalen
);
1817 ainfo
.iosize
= sizeof(attr_header_t
);
1818 error
= write_xattrinfo(&ainfo
);
1825 /* Write the Resource Fork. */
1826 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1827 u_int32_t endoffset
;
1829 if (!vnode_isreg(vp
)) {
1833 /* Make sure we have a rsrc fork pointer.. */
1834 if (ainfo
.rsrcfork
== NULL
) {
1838 if (ainfo
.rsrcfork
) {
1839 if (ainfo
.rsrcfork
->length
!= 0) {
1840 if (options
& XATTR_CREATE
) {
1841 /* attr exists, and create specified ? */
1847 /* Zero length AD rsrc fork */
1848 if (options
& XATTR_REPLACE
) {
1849 /* attr doesn't exist (0-length), but replace specified ? */
1856 /* We can't do much if we somehow didn't get an AD rsrc pointer */
1861 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1862 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1863 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1866 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1867 if (endoffset
> ainfo
.rsrcfork
->length
) {
1868 ainfo
.rsrcfork
->length
= endoffset
;
1869 ainfo
.iosize
= sizeof(attr_header_t
);
1870 error
= write_xattrinfo(&ainfo
);
1876 if (datalen
> ATTR_MAX_SIZE
) {
1877 return (E2BIG
); /* EINVAL instead ? */
1880 if (ainfo
.attrhdr
== NULL
) {
1884 header
= ainfo
.attrhdr
;
1885 entry
= ainfo
.attr_entry
;
1887 /* Check if data area crosses the maximum header size. */
1888 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1889 splitdata
= 1; /* do data I/O separately */
1894 * See if attribute already exists.
1896 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1897 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1901 entry
= ATTR_NEXT(entry
);
1905 if (options
& XATTR_CREATE
) {
1909 if (datalen
== entry
->length
) {
1911 uio_setoffset(uio
, entry
->offset
);
1912 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1913 uio_setoffset(uio
, 0);
1915 printf("setxattr: VNOP_WRITE error %d\n", error
);
1918 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1919 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1922 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1923 error
= write_xattrinfo(&ainfo
);
1925 printf("setxattr: write_xattrinfo error %d\n", error
);
1931 * Brute force approach - just remove old entry and set new entry.
1934 rel_xattrinfo(&ainfo
);
1935 close_xattrfile(xvp
, fileflags
, context
);
1936 error
= default_removexattr(vp
, name
, options
, context
);
1940 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1941 options
&= ~XATTR_REPLACE
;
1942 goto start
; /* start over */
1947 if (options
& XATTR_REPLACE
) {
1948 error
= ENOATTR
; /* nothing there to replace */
1951 /* Check if header size limit has been reached. */
1952 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1957 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1959 /* Check if we need more space. */
1960 if ((datalen
+ entrylen
) > datafreespace
) {
1963 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1965 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1966 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1967 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1970 ainfo
.filesize
+= growsize
;
1971 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1973 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1979 * Move the resource fork out of the way.
1981 if (ainfo
.rsrcfork
) {
1982 if (ainfo
.rsrcfork
->length
!= 0) {
1983 shift_data_down(xvp
,
1984 ainfo
.rsrcfork
->offset
,
1985 ainfo
.rsrcfork
->length
,
1988 ainfo
.rsrcfork
->offset
+= growsize
;
1990 ainfo
.finderinfo
->length
+= growsize
;
1991 header
->total_size
+= growsize
;
1994 /* Make space for a new entry. */
1996 shift_data_down(xvp
,
1998 header
->data_length
,
2001 bcopy((u_int8_t
*)header
+ header
->data_start
,
2002 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
2003 header
->data_length
);
2005 header
->data_start
+= entrylen
;
2007 /* Fix up entry data offsets. */
2009 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
2010 entry
->offset
+= entrylen
;
2014 * If the attribute data area is entirely within
2015 * the header buffer, then just update the buffer,
2016 * otherwise we'll write it separately to the file.
2021 /* Write new attribute data after the end of existing data. */
2022 offset
= header
->data_start
+ header
->data_length
;
2023 uio_setoffset(uio
, offset
);
2024 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
2025 uio_setoffset(uio
, 0);
2027 printf("setxattr: VNOP_WRITE error %d\n", error
);
2031 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
2033 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
2035 printf("setxattr: uiomove error %d\n", error
);
2040 /* Create the attribute entry. */
2041 lastentry
->length
= datalen
;
2042 lastentry
->offset
= header
->data_start
+ header
->data_length
;
2043 lastentry
->namelen
= namelen
;
2044 lastentry
->flags
= 0;
2045 bcopy(name
, &lastentry
->name
[0], namelen
);
2047 /* Update the attributes header. */
2048 header
->num_attrs
++;
2049 header
->data_length
+= datalen
;
2052 /* Only write the entries, since the data was written separately. */
2053 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
2055 /* The entry and data are both in the header; write them together. */
2056 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2058 error
= write_xattrinfo(&ainfo
);
2060 printf("setxattr: write_xattrinfo error %d\n", error
);
2064 rel_xattrinfo(&ainfo
);
2065 close_xattrfile(xvp
, fileflags
, context
);
2067 /* Touch the change time if we changed an attribute. */
2069 struct vnode_attr va
;
2071 /* Re-write the mtime to cause a ctime change. */
2073 VATTR_WANTED(&va
, va_modify_time
);
2074 if (vnode_getattr(vp
, &va
, context
) == 0) {
2076 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2077 (void) vnode_setattr(vp
, &va
, context
);
2081 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2088 * Remove an extended attribute.
2091 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
2095 attr_header_t
*header
;
2096 attr_entry_t
*entry
;
2097 attr_entry_t
*oldslot
;
2103 int found
= 0, lastone
= 0;
2111 fileflags
= FREAD
| FWRITE
;
2112 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
2115 * Open the file locked (exclusive) since the Carbon
2116 * File Manager may have the Apple Double file open
2117 * and could be changing the resource fork.
2119 fileflags
|= O_EXLOCK
;
2124 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
2127 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2128 close_xattrfile(xvp
, fileflags
, context
);
2132 attrcount
+= ainfo
.attrhdr
->num_attrs
;
2135 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
2138 /* Clear the Finder Info. */
2139 if (strncmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
2140 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
2144 /* On removal of last attribute the ._ file is removed. */
2145 if (--attrcount
== 0)
2147 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
2148 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
2149 ainfo
.iosize
= sizeof(attr_header_t
);
2150 error
= write_xattrinfo(&ainfo
);
2154 /* Clear the Resource Fork. */
2156 if (!vnode_isreg(vp
)) {
2160 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
2164 /* On removal of last attribute the ._ file is removed. */
2165 if (--attrcount
== 0)
2169 * If the resource fork isn't the last AppleDouble
2170 * entry then the space needs to be reclaimed by
2171 * shifting the entries after the resource fork.
2173 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
2174 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
2175 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2178 ainfo
.rsrcfork
->length
= 0;
2179 ainfo
.iosize
= sizeof(attr_header_t
);
2180 error
= write_xattrinfo(&ainfo
);
2185 if (ainfo
.attrhdr
== NULL
) {
2189 namelen
= strlen(name
) + 1;
2190 header
= ainfo
.attrhdr
;
2191 entry
= ainfo
.attr_entry
;
2194 * See if this attribute exists.
2196 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2197 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2199 if ((i
+1) == header
->num_attrs
)
2203 entry
= ATTR_NEXT(entry
);
2209 /* On removal of last attribute the ._ file is removed. */
2210 if (--attrcount
== 0)
2213 datalen
= entry
->length
;
2214 dataoff
= entry
->offset
;
2215 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2216 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
2221 /* Remove the attribute entry. */
2223 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2224 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2227 /* Adjust the attribute data. */
2231 dataoff
- header
->data_start
,
2237 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2241 /* XXX write zeros to freed space ? */
2242 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2246 bcopy((u_int8_t
*)header
+ header
->data_start
,
2247 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2248 dataoff
- header
->data_start
);
2250 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2251 (u_int8_t
*)header
+ dataoff
- entrylen
,
2252 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2254 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2255 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2258 /* Adjust the header values and entry offsets. */
2259 header
->num_attrs
--;
2260 header
->data_start
-= entrylen
;
2261 header
->data_length
-= datalen
;
2264 entry
= ainfo
.attr_entry
;
2265 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2266 entry
->offset
-= entrylen
;
2267 if (entry
>= oldslot
)
2268 entry
->offset
-= datalen
;
2269 entry
= ATTR_NEXT(entry
);
2271 error
= write_xattrinfo(&ainfo
);
2273 printf("removexattr: write_xattrinfo error %d\n", error
);
2276 rel_xattrinfo(&ainfo
);
2278 /* When there are no more attributes remove the ._ file. */
2279 if (attrcount
== 0) {
2280 if (fileflags
& O_EXLOCK
)
2281 (void) unlock_xattrfile(xvp
, context
);
2282 VNOP_CLOSE(xvp
, fileflags
, context
);
2284 error
= remove_xattrfile(xvp
, context
);
2287 close_xattrfile(xvp
, fileflags
, context
);
2289 /* Touch the change time if we changed an attribute. */
2291 struct vnode_attr va
;
2293 /* Re-write the mtime to cause a ctime change. */
2295 VATTR_WANTED(&va
, va_modify_time
);
2296 if (vnode_getattr(vp
, &va
, context
) == 0) {
2298 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2299 (void) vnode_setattr(vp
, &va
, context
);
2303 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2311 * Retrieve the list of extended attribute names.
2314 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2318 attr_entry_t
*entry
;
2323 * We do not zero "*size" here as we don't want to stomp a size set when
2324 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2325 * system call layer, up in listxattr or flistxattr.
2328 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2329 if (error
== ENOATTR
)
2333 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2334 if (error
== ENOATTR
)
2336 close_xattrfile(xvp
, FREAD
, context
);
2340 /* Check for Finder Info. */
2341 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2343 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2344 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2348 error
= uiomove(XATTR_FINDERINFO_NAME
,
2349 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2357 /* Check for Resource Fork. */
2358 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2360 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2361 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2365 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2366 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2374 /* Check for attributes. */
2375 if (ainfo
.attrhdr
) {
2376 count
= ainfo
.attrhdr
->num_attrs
;
2377 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2378 if (xattr_protected((const char *)entry
->name
) ||
2379 ((entry
->namelen
< XATTR_MAXNAMELEN
) &&
2380 (entry
->name
[entry
->namelen
] == '\0') &&
2381 (xattr_validatename((const char *)entry
->name
) != 0))) {
2382 entry
= ATTR_NEXT(entry
);
2386 *size
+= entry
->namelen
;
2387 entry
= ATTR_NEXT(entry
);
2390 if (uio_resid(uio
) < entry
->namelen
) {
2394 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2396 if (error
!= EFAULT
)
2400 entry
= ATTR_NEXT(entry
);
2404 rel_xattrinfo(&ainfo
);
2405 close_xattrfile(xvp
, FREAD
, context
);
2411 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2413 vnode_t xvp
= NULLVP
;
2414 vnode_t dvp
= NULLVP
;
2415 struct vnode_attr va
;
2416 struct nameidata nd
;
2418 char *filename
= NULL
;
2419 const char *basename
= NULL
;
2425 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2427 * For the root directory use "._." to hold the attributes.
2429 filename
= &smallname
[0];
2430 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2431 dvp
= vp
; /* the "._." file resides in the root dir */
2434 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2438 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2443 /* "._" Attribute files cannot have attributes */
2444 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2445 basename
[0] == '.' && basename
[1] == '_') {
2449 filename
= &smallname
[0];
2450 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2451 if (len
>= sizeof(smallname
)) {
2452 len
++; /* snprintf result doesn't include '\0' */
2453 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2454 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2457 * Note that the lookup here does not authorize. Since we are looking
2458 * up in the same directory that we already have the file vnode in,
2459 * we must have been given the file vnode legitimately. Read/write
2460 * access has already been authorized in layers above for calls from
2461 * userspace, and the authorization code using this path to read
2462 * file security from the EA must always get access
2465 NDINIT(&nd
, LOOKUP
, OP_OPEN
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
,
2466 UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), context
);
2469 if (fileflags
& O_CREAT
) {
2470 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2475 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2477 if ( (error
= namei(&nd
))) {
2482 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2488 * Pick up uid/gid/mode from target file.
2491 VATTR_WANTED(&va
, va_uid
);
2492 VATTR_WANTED(&va
, va_gid
);
2493 VATTR_WANTED(&va
, va_mode
);
2494 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2495 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2496 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2497 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2500 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2501 } else /* fallback values */ {
2502 uid
= KAUTH_UID_NONE
;
2503 gid
= KAUTH_GID_NONE
;
2504 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2508 VATTR_SET(&va
, va_type
, VREG
);
2509 VATTR_SET(&va
, va_mode
, umode
);
2510 if (uid
!= KAUTH_UID_NONE
)
2511 VATTR_SET(&va
, va_uid
, uid
);
2512 if (gid
!= KAUTH_GID_NONE
)
2513 VATTR_SET(&va
, va_gid
, gid
);
2515 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
, &va
,
2516 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2526 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2531 if ((error
= namei(&nd
))) {
2541 if (xvp
->v_type
!= VREG
) {
2546 * Owners must match.
2549 VATTR_WANTED(&va
, va_uid
);
2550 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2551 uid_t owner
= va
.va_uid
;
2554 VATTR_WANTED(&va
, va_uid
);
2555 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2556 error
= ENOATTR
; /* don't use this "._" file */
2561 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2567 if ((error
= vnode_ref(xvp
))) {
2572 /* If create was requested, make sure file header exists. */
2573 if (fileflags
& O_CREAT
) {
2575 VATTR_WANTED(&va
, va_data_size
);
2576 VATTR_WANTED(&va
, va_fileid
);
2577 VATTR_WANTED(&va
, va_nlink
);
2578 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2583 /* If the file is empty then add a default header. */
2584 if (va
.va_data_size
== 0) {
2585 /* Don't adopt hard-linked "._" files. */
2586 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2590 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2594 /* Apply file locking if requested. */
2595 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2598 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2599 error
= lock_xattrfile(xvp
, locktype
, context
);
2605 if (xvp
!= NULLVP
) {
2607 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2610 if (fileflags
& O_CREAT
) {
2611 /* Delete the xattr file if we encountered any errors */
2612 (void) remove_xattrfile (xvp
, context
);
2616 (void) vnode_rele(xvp
);
2618 (void) vnode_put(xvp
);
2621 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2625 /* Release resources after error-handling */
2626 if (dvp
&& (dvp
!= vp
)) {
2630 vnode_putname(basename
);
2632 if (filename
&& filename
!= &smallname
[0]) {
2633 FREE(filename
, M_TEMP
);
2636 *xvpp
= xvp
; /* return a referenced vnode */
2641 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2643 // if (fileflags & FWRITE)
2644 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2646 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2647 (void) unlock_xattrfile(xvp
, context
);
2649 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2650 (void) vnode_rele(xvp
);
2651 (void) vnode_put(xvp
);
2655 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2658 struct nameidata nd
;
2663 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2667 pathlen
= MAXPATHLEN
;
2668 error
= vn_getpath(xvp
, path
, &pathlen
);
2670 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2674 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2675 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2677 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2684 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2693 * Read in and parse the AppleDouble header and entries, and the extended
2694 * attribute header and entries if any. Populates the fields of ainfop
2695 * based on the headers and entries found.
2697 * The basic idea is to:
2698 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2699 * AppleDouble entries, the extended attribute header, and extended
2700 * attribute entries must lie within this part of the file; the rest of
2701 * the AppleDouble handling code assumes this. Plus it allows us to
2702 * somewhat optimize by doing a smaller number of larger I/Os.
2703 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2705 * - Find the Finder Info and Resource Fork entries, if any.
2706 * - If we're going to be writing, try to make sure the Finder Info entry has
2707 * room to store the extended attribute header, plus some space for extended
2709 * - Swap and sanity check the extended attribute header and entries (if any).
2712 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2715 void * buffer
= NULL
;
2716 apple_double_header_t
*filehdr
;
2717 struct vnode_attr va
;
2722 bzero(ainfop
, sizeof(attr_info_t
));
2723 ainfop
->filevp
= xvp
;
2724 ainfop
->context
= context
;
2726 VATTR_WANTED(&va
, va_data_size
);
2727 VATTR_WANTED(&va
, va_fileid
);
2728 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2731 ainfop
->filesize
= va
.va_data_size
;
2733 /* When setting attributes, allow room for the header to grow. */
2735 iosize
= ATTR_MAX_HDR_SIZE
;
2737 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2743 ainfop
->iosize
= iosize
;
2744 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2745 if (buffer
== NULL
){
2750 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2751 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2753 /* Read the file header. */
2754 error
= VNOP_READ(xvp
, auio
, 0, context
);
2758 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2759 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2761 filehdr
= (apple_double_header_t
*)buffer
;
2763 error
= check_and_swap_apple_double_header(ainfop
);
2767 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2769 /* rel_xattrinfo is responsible for freeing the header buffer */
2772 /* Find the Finder Info and Resource Fork entries, if any */
2773 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2774 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2775 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2776 /* We found the Finder Info entry. */
2777 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2780 * Is the Finder Info "empty" (all zeroes)? If so,
2781 * we'll pretend like the Finder Info extended attribute
2784 * Note: we have to make sure the Finder Info is
2785 * contained within the buffer we have already read,
2786 * to avoid accidentally accessing a bogus address.
2787 * If it is outside the buffer, we just assume the
2788 * Finder Info is non-empty.
2790 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2791 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2792 ainfop
->emptyfinderinfo
= 1;
2795 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2797 * Ignore zero-length resource forks when getting. If setting,
2798 * we need to remember the resource fork entry so it can be
2799 * updated once the new content has been written.
2801 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2805 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2807 * The "empty" resource headers we created have a system data tag of:
2808 * "This resource fork intentionally left blank "
2810 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2812 u_int8_t systemData
[64];
2816 /* Read the system data which starts at byte 16 */
2817 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2818 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2819 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2820 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2824 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2825 continue; /* skip this resource fork */
2828 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2829 if (i
!= (filehdr
->numEntries
- 1)) {
2830 printf("get_xattrinfo: resource fork not last entry\n");
2831 ainfop
->readonly
= 1;
2838 * See if this file looks like it is laid out correctly to contain
2839 * extended attributes. If so, then do the following:
2841 * - If we're going to be writing, try to make sure the Finder Info
2842 * entry has room to store the extended attribute header, plus some
2843 * space for extended attributes.
2845 * - Swap and sanity check the extended attribute header and entries
2848 if (filehdr
->numEntries
== 2 &&
2849 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2850 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2851 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2852 attr_header_t
*attrhdr
;
2853 attrhdr
= (attr_header_t
*)filehdr
;
2855 * If we're going to be writing, try to make sure the Finder
2856 * Info entry has room to store the extended attribute header,
2857 * plus some space for extended attributes.
2859 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2863 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2864 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2865 /* Make some room before existing resource fork. */
2866 shift_data_down(xvp
,
2867 filehdr
->entries
[1].offset
,
2868 filehdr
->entries
[1].length
,
2870 writesize
= sizeof(attr_header_t
);
2872 /* Create a new, empty resource fork. */
2873 rsrcfork_header_t
*rsrcforkhdr
;
2875 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2877 /* Steal some space for an empty RF header. */
2878 delta
-= sizeof(rsrcfork_header_t
);
2880 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2881 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2883 /* Fill in Empty Resource Fork Header. */
2884 init_empty_resource_fork(rsrcforkhdr
);
2886 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2887 writesize
= ATTR_BUF_SIZE
;
2889 filehdr
->entries
[0].length
+= delta
;
2890 filehdr
->entries
[1].offset
+= delta
;
2892 /* Fill in Attribute Header. */
2893 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2894 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2895 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2896 attrhdr
->data_start
= sizeof(attr_header_t
);
2897 attrhdr
->data_length
= 0;
2898 attrhdr
->reserved
[0] = 0;
2899 attrhdr
->reserved
[1] = 0;
2900 attrhdr
->reserved
[2] = 0;
2902 attrhdr
->num_attrs
= 0;
2904 /* Push out new header */
2905 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2906 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2908 swap_adhdr(filehdr
); /* to big endian */
2909 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2910 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2911 swap_adhdr(filehdr
); /* back to native */
2912 /* The attribute header gets swapped below. */
2916 * Swap and sanity check the extended attribute header and
2917 * entries (if any). The Finder Info content must be big enough
2918 * to include the extended attribute header; if not, we just
2921 * Note that we're passing the offset + length (i.e. the end)
2922 * of the Finder Info instead of rawsize to validate_attrhdr.
2923 * This ensures that all extended attributes lie within the
2924 * Finder Info content according to the AppleDouble entry.
2926 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2929 if (ainfop
->finderinfo
&&
2930 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2931 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2932 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2934 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2935 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2936 /* First attr_entry starts immediately following attribute header */
2937 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2946 FREE(buffer
, M_TEMP
);
2952 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2955 rsrcfork_header_t
*rsrcforkhdr
;
2961 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2962 bzero(buffer
, ATTR_BUF_SIZE
);
2964 xah
= (attr_header_t
*)buffer
;
2965 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2966 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2967 rsrcforksize
= sizeof(rsrcfork_header_t
);
2968 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2970 /* Fill in Apple Double Header. */
2971 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2972 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2973 xah
->appledouble
.numEntries
= SWAP16 (2);
2974 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2975 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2976 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2977 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2978 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2979 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2980 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2982 /* Fill in Attribute Header. */
2983 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2984 xah
->debug_tag
= SWAP32 (fileid
);
2985 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2986 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2988 /* Fill in Empty Resource Fork Header. */
2989 init_empty_resource_fork(rsrcforkhdr
);
2992 error
= VNOP_WRITE(xvp
, auio
, IO_UNIT
, context
);
2994 /* Did we write out the full uio? */
2995 if (uio_resid(auio
) > 0) {
3000 FREE(buffer
, M_TEMP
);
3006 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
3008 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
3009 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3010 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3011 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
3012 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3013 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3014 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
3015 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
3016 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
3017 rsrcforkhdr
->typeCount
= SWAP16 (-1);
3018 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
3022 rel_xattrinfo(attr_info_t
*ainfop
)
3024 FREE(ainfop
->filehdr
, M_TEMP
);
3025 bzero(ainfop
, sizeof(attr_info_t
));
3029 write_xattrinfo(attr_info_t
*ainfop
)
3034 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3035 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
3037 swap_adhdr(ainfop
->filehdr
);
3038 if (ainfop
->attrhdr
!= NULL
) {
3039 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3042 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
3044 swap_adhdr(ainfop
->filehdr
);
3045 if (ainfop
->attrhdr
!= NULL
) {
3046 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3053 #if BYTE_ORDER == LITTLE_ENDIAN
3055 * Endian swap apple double header
3058 swap_adhdr(apple_double_header_t
*adh
)
3063 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
3065 adh
->magic
= SWAP32 (adh
->magic
);
3066 adh
->version
= SWAP32 (adh
->version
);
3067 adh
->numEntries
= SWAP16 (adh
->numEntries
);
3069 for (i
= 0; i
< count
; i
++) {
3070 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
3071 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
3072 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
3077 * Endian swap extended attributes header
3080 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
3086 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
3088 ah
->magic
= SWAP32 (ah
->magic
);
3089 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3090 ah
->total_size
= SWAP32 (ah
->total_size
);
3091 ah
->data_start
= SWAP32 (ah
->data_start
);
3092 ah
->data_length
= SWAP32 (ah
->data_length
);
3093 ah
->flags
= SWAP16 (ah
->flags
);
3094 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3096 ae
= (attr_entry_t
*)(&ah
[1]);
3097 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
3098 ae
->offset
= SWAP32 (ae
->offset
);
3099 ae
->length
= SWAP32 (ae
->length
);
3100 ae
->flags
= SWAP16 (ae
->flags
);
3106 * Validate and swap the attributes header contents, and each attribute's
3109 * Note: Assumes the caller has verified that the Finder Info content is large
3110 * enough to contain the attr_header structure itself. Therefore, we can
3111 * swap the header fields before sanity checking them.
3114 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
3125 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
3128 /* Swap the basic header fields */
3129 ah
->magic
= SWAP32(ah
->magic
);
3130 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3131 ah
->total_size
= SWAP32 (ah
->total_size
);
3132 ah
->data_start
= SWAP32 (ah
->data_start
);
3133 ah
->data_length
= SWAP32 (ah
->data_length
);
3134 ah
->flags
= SWAP16 (ah
->flags
);
3135 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3138 * Make sure the total_size fits within the Finder Info area, and the
3139 * extended attribute data area fits within total_size.
3141 end
= ah
->data_start
+ ah
->data_length
;
3142 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3143 end
< ah
->data_start
||
3144 end
> ah
->total_size
) {
3149 * Make sure each of the attr_entry_t's fits within total_size.
3151 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3152 count
= ah
->num_attrs
;
3153 ae
= (attr_entry_t
*)(&ah
[1]);
3155 for (i
=0; i
<count
; i
++) {
3156 /* Make sure the fixed-size part of this attr_entry_t fits. */
3157 if ((u_int8_t
*) &ae
[1] > buf_end
)
3160 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3161 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3162 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
3165 /* Swap the attribute entry fields */
3166 ae
->offset
= SWAP32(ae
->offset
);
3167 ae
->length
= SWAP32(ae
->length
);
3168 ae
->flags
= SWAP16(ae
->flags
);
3170 /* Make sure the attribute content fits. */
3171 end
= ae
->offset
+ ae
->length
;
3172 if (end
< ae
->offset
|| end
> ah
->total_size
)
3179 * TODO: Make sure the contents of attributes don't overlap the header
3180 * and don't overlap each other. The hard part is that we don't know
3181 * what the actual header size is until we have looped over all of the
3182 * variable-sized attribute entries.
3184 * XXX Is there any guarantee that attribute entries are stored in
3185 * XXX order sorted by the contents' file offset? If so, that would
3186 * XXX make the pairwise overlap check much easier.
3193 // "start" & "end" are byte offsets in the file.
3194 // "to" is the byte offset we want to move the
3195 // data to. "to" should be > "start".
3197 // we do the copy backwards to avoid problems if
3198 // there's an overlap.
3201 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3204 size_t chunk
, orig_chunk
;
3207 kauth_cred_t ucred
= vfs_context_ucred(context
);
3208 proc_t p
= vfs_context_proc(context
);
3210 if (delta
== 0 || len
== 0) {
3220 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3224 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
3225 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3227 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3228 pos
, ret
, chunk
, ret
);
3232 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3234 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3235 pos
+delta
, ret
, chunk
, ret
);
3239 if ((pos
- (off_t
)chunk
) < start
) {
3240 chunk
= pos
- start
;
3242 if (chunk
== 0) { // we're all done
3247 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3254 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3257 size_t chunk
, orig_chunk
;
3261 kauth_cred_t ucred
= vfs_context_ucred(context
);
3262 proc_t p
= vfs_context_proc(context
);
3264 if (delta
== 0 || len
== 0) {
3275 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3279 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3280 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3282 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3283 pos
, ret
, chunk
, ret
);
3287 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3289 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3290 pos
+delta
, ret
, chunk
, ret
);
3294 if ((pos
+ (off_t
)chunk
) > end
) {
3297 if (chunk
== 0) { // we're all done
3302 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3308 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3313 lf
.l_whence
= SEEK_SET
;
3316 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3317 /* Note: id is just a kernel address that's not a proc */
3318 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
, NULL
);
3319 return (error
== ENOTSUP
? 0 : error
);
3323 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3328 lf
.l_whence
= SEEK_SET
;
3331 lf
.l_type
= F_UNLCK
;
3332 /* Note: id is just a kernel address that's not a proc */
3333 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
, NULL
);
3334 return (error
== ENOTSUP
? 0 : error
);
3337 #else /* CONFIG_APPLEDOUBLE */
3341 default_getxattr(__unused vnode_t vp
, __unused
const char *name
,
3342 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3343 __unused vfs_context_t context
)
3349 default_setxattr(__unused vnode_t vp
, __unused
const char *name
,
3350 __unused uio_t uio
, __unused
int options
, __unused vfs_context_t context
)
3356 default_listxattr(__unused vnode_t vp
,
3357 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3358 __unused vfs_context_t context
)
3364 default_removexattr(__unused vnode_t vp
, __unused
const char *name
,
3365 __unused
int options
, __unused vfs_context_t context
)
3370 #endif /* CONFIG_APPLEDOUBLE */