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
);
144 if ((error
= xattr_validatename(name
))) {
147 if ((error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
))) {
150 /* The offset can only be non-zero for resource forks. */
151 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
152 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
158 /* The offset can only be non-zero for resource forks. */
159 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
160 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
165 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
166 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
168 * A filesystem may keep some EAs natively and return ENOTSUP for others.
170 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
177 * Set the data of an extended attribute.
180 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
184 if (!XATTR_VNODE_SUPPORTED(vp
)) {
188 /* setxattr calls are not allowed for streams. */
189 if (vp
->v_flag
& VISNAMEDSTREAM
) {
194 if ((options
& (XATTR_REPLACE
| XATTR_CREATE
)) == (XATTR_REPLACE
| XATTR_CREATE
)) {
197 if ((error
= xattr_validatename(name
))) {
200 if (!(options
& XATTR_NOSECURITY
)) {
202 error
= mac_vnode_check_setextattr(context
, vp
, name
, uio
);
207 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
212 /* The offset can only be non-zero for resource forks. */
213 if (uio_offset(uio
) != 0 &&
214 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
219 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
222 * An EJUSTRETURN is from a filesystem which keeps this xattr
223 * natively as well as in a dot-underscore file. In this case the
224 * EJUSTRETURN means the filesytem has done nothing, but identifies the
225 * EA as one which may be represented natively and/or in a DU, and
226 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
227 * in vn_setxattr can we do the getxattrs needed to ascertain whether
228 * the XATTR_{CREATE,REPLACE} should yield an error.
230 if (error
== EJUSTRETURN
) {
231 int native
= 0, dufile
= 0;
232 size_t sz
; /* not used */
234 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
235 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
236 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
240 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
245 * Having determined no CREATE/REPLACE error should result, we
246 * zero those bits, so both backing stores get written to.
248 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
249 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
250 /* the mainline path here is to have error==ENOTSUP ... */
252 #endif /* DUAL_EAS */
253 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
255 * A filesystem may keep some EAs natively and return ENOTSUP for others.
257 error
= default_setxattr(vp
, name
, uio
, options
, context
);
260 if ((error
== 0) && !(options
& XATTR_NOSECURITY
)) {
261 mac_vnode_notify_setextattr(context
, vp
, name
, uio
);
262 if (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
) {
263 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
272 * Remove an extended attribute.
275 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
279 if (!XATTR_VNODE_SUPPORTED(vp
)) {
283 /* removexattr calls are not allowed for streams. */
284 if (vp
->v_flag
& VISNAMEDSTREAM
) {
289 if ((error
= xattr_validatename(name
))) {
292 if (!(options
& XATTR_NOSECURITY
)) {
294 error
= mac_vnode_check_deleteextattr(context
, vp
, name
);
299 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
304 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
305 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
307 * A filesystem may keep some EAs natively and return ENOTSUP for others.
309 error
= default_removexattr(vp
, name
, options
, context
);
311 } else if (error
== EJUSTRETURN
) {
313 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
314 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
315 * a native xattr, so failure to find it in a DU file during
316 * default_removexattr should not be considered an error.
318 error
= default_removexattr(vp
, name
, options
, context
);
319 if (error
== ENOATTR
) {
322 #endif /* DUAL_EAS */
325 if ((error
== 0) && !(options
& XATTR_NOSECURITY
)) {
326 mac_vnode_notify_deleteextattr(context
, vp
, name
);
327 if (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
) {
328 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
337 * Retrieve the list of extended attribute names.
340 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
344 if (!XATTR_VNODE_SUPPORTED(vp
)) {
348 /* listxattr calls are not allowed for streams. */
349 if (vp
->v_flag
& VISNAMEDSTREAM
) {
354 if (!(options
& XATTR_NOSECURITY
)) {
356 error
= mac_vnode_check_listextattr(context
, vp
);
362 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
);
368 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
369 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
371 * A filesystem may keep some but not all EAs natively, in which case
372 * the native EA names will have been uiomove-d out (or *size updated)
373 * and the default_listxattr here will finish the job.
375 error
= default_listxattr(vp
, uio
, size
, options
, context
);
382 xattr_validatename(const char *name
)
386 if (name
== NULL
|| name
[0] == '\0') {
389 namelen
= strlen(name
);
390 if (name
[namelen
] != '\0') {
394 if (utf8_validatestr((const unsigned char *)name
, namelen
) != 0) {
403 * Determine whether an EA is a protected system attribute.
406 xattr_protected(const char *attrname
)
408 return !strncmp(attrname
, "com.apple.system.", 17);
413 vnode_setasnamedstream_internal(vnode_t vp
, vnode_t svp
)
415 uint32_t streamflags
= VISNAMEDSTREAM
;
417 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
418 streamflags
|= VISSHADOW
;
422 vnode_lock_spin(svp
);
423 svp
->v_flag
|= streamflags
;
426 /* Tag the parent so we know to flush credentials for streams on setattr */
428 vp
->v_lflag
|= VL_HASSTREAMS
;
431 /* Make the file it's parent.
432 * Note: This parent link helps us distinguish vnodes for
433 * shadow stream files from vnodes for resource fork on file
434 * systems that support namedstream natively (both have
435 * VISNAMEDSTREAM set) by allowing access to mount structure
436 * for checking MNTK_NAMED_STREAMS bit at many places in the
439 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_NAMEDSTREAM_PARENT
);
445 vnode_setasnamedstream(vnode_t vp
, vnode_t svp
)
447 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
451 vnode_setasnamedstream_internal(vp
, svp
);
458 * Obtain a named stream from vnode vp.
461 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
465 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) {
466 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
471 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
476 vnode_setasnamedstream_internal(vp
, *svpp
);
483 * Make a named stream for vnode vp.
486 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
490 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) {
491 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
493 error
= default_makenamedstream(vp
, svpp
, name
, context
);
497 vnode_setasnamedstream_internal(vp
, *svpp
);
504 * Remove a named stream from vnode vp.
507 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
511 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) {
512 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
514 error
= default_removenamedstream(vp
, name
, context
);
520 #define NS_IOBUFSIZE (128 * 1024)
523 * Release a named stream shadow file.
525 * Note: This function is called from two places where we do not need
526 * to check if the vnode has any references held before deleting the
527 * shadow file. Once from vclean() when the vnode is being reclaimed
528 * and we do not hold any references on the vnode. Second time from
529 * default_getnamedstream() when we get an error during shadow stream
530 * file initialization so that other processes who are waiting for the
531 * shadow stream file initialization by the creator will get opportunity
532 * to create and initialize the file again.
535 vnode_relenamedstream(vnode_t vp
, vnode_t svp
)
538 struct componentname cn
;
543 * We need to use the kernel context here. If we used the supplied
544 * VFS context we have no clue whether or not it originated from userland
545 * where it could be subject to a chroot jail. We need to ensure that all
546 * filesystem access to shadow files is done on the same FS regardless of
547 * userland process restrictions.
549 vfs_context_t kernelctx
= vfs_context_kernel();
554 MAKE_SHADOW_NAME(vp
, tmpname
);
557 cn
.cn_nameiop
= DELETE
;
558 cn
.cn_flags
= ISLASTCN
;
559 cn
.cn_context
= kernelctx
;
560 cn
.cn_pnbuf
= tmpname
;
561 cn
.cn_pnlen
= sizeof(tmpname
);
562 cn
.cn_nameptr
= cn
.cn_pnbuf
;
563 cn
.cn_namelen
= strlen(tmpname
);
566 * Obtain the vnode for the shadow files directory. Make sure to
567 * use the kernel ctx as described above.
569 err
= get_shadow_dir(&dvp
);
574 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, kernelctx
);
581 * Flush a named stream shadow file.
583 * 'vp' represents the AppleDouble file.
584 * 'svp' represents the shadow file.
587 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
589 struct vnode_attr va
;
591 caddr_t bufptr
= NULL
;
598 * The kernel context must be used for all I/O to the shadow file
599 * and its namespace operations
601 vfs_context_t kernelctx
= vfs_context_kernel();
603 /* The supplied context is used for access to the AD file itself */
606 VATTR_WANTED(&va
, va_data_size
);
607 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
608 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
611 datasize
= va
.va_data_size
;
613 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
617 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
618 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
621 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
625 * Copy the shadow stream file data into the resource fork.
627 error
= VNOP_OPEN(svp
, 0, kernelctx
);
629 printf("vnode_flushnamedstream: err %d opening file\n", error
);
632 while (offset
< datasize
) {
633 iosize
= MIN(datasize
- offset
, iosize
);
635 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
636 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
637 error
= VNOP_READ(svp
, auio
, 0, kernelctx
);
641 /* Since there's no truncate xattr we must remove the resource fork. */
643 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
644 if ((error
!= 0) && (error
!= ENOATTR
)) {
648 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
649 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
650 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
657 /* close shadowfile */
658 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
661 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
671 * Verify that the vnode 'vp' is a vnode that lives in the shadow
672 * directory. We can't just query the parent pointer directly since
673 * the shadowfile is hooked up to the actual file it's a stream for.
676 vnode_verifynamedstream(vnode_t vp
)
679 struct vnode
*shadow_dvp
= NULL
;
680 struct vnode
*shadowfile
= NULL
;
681 struct componentname cn
;
684 * We need to use the kernel context here. If we used the supplied
685 * VFS context we have no clue whether or not it originated from userland
686 * where it could be subject to a chroot jail. We need to ensure that all
687 * filesystem access to shadow files is done on the same FS regardless of
688 * userland process restrictions.
690 vfs_context_t kernelctx
= vfs_context_kernel();
694 /* Get the shadow directory vnode */
695 error
= get_shadow_dir(&shadow_dvp
);
700 /* Re-generate the shadow name in the buffer */
701 MAKE_SHADOW_NAME(vp
, tmpname
);
703 /* Look up item in shadow dir */
704 bzero(&cn
, sizeof(cn
));
705 cn
.cn_nameiop
= LOOKUP
;
706 cn
.cn_flags
= ISLASTCN
| CN_ALLOWRSRCFORK
;
707 cn
.cn_context
= kernelctx
;
708 cn
.cn_pnbuf
= tmpname
;
709 cn
.cn_pnlen
= sizeof(tmpname
);
710 cn
.cn_nameptr
= cn
.cn_pnbuf
;
711 cn
.cn_namelen
= strlen(tmpname
);
713 if (VNOP_LOOKUP(shadow_dvp
, &shadowfile
, &cn
, kernelctx
) == 0) {
714 /* is the pointer the same? */
715 if (shadowfile
== vp
) {
720 /* drop the iocount acquired */
721 vnode_put(shadowfile
);
724 /* Drop iocount on shadow dir */
725 vnode_put(shadow_dvp
);
730 * Access or create the shadow file as needed.
732 * 'makestream' with non-zero value means that we need to guarantee we were the
733 * creator of the shadow file.
735 * 'context' is the user supplied context for the original VFS operation that
736 * caused us to need a shadow file.
738 * int pointed to by 'creator' is nonzero if we created the shadowfile.
741 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
742 int *creator
, vfs_context_t context
)
744 vnode_t dvp
= NULLVP
;
745 vnode_t svp
= NULLVP
;
746 struct componentname cn
;
747 struct vnode_attr va
;
752 vfs_context_t kernelctx
= vfs_context_kernel();
756 /* Establish a unique file name. */
757 MAKE_SHADOW_NAME(vp
, tmpname
);
758 bzero(&cn
, sizeof(cn
));
759 cn
.cn_nameiop
= LOOKUP
;
760 cn
.cn_flags
= ISLASTCN
;
761 cn
.cn_context
= context
;
762 cn
.cn_pnbuf
= tmpname
;
763 cn
.cn_pnlen
= sizeof(tmpname
);
764 cn
.cn_nameptr
= cn
.cn_pnbuf
;
765 cn
.cn_namelen
= strlen(tmpname
);
767 /* Pick up uid, gid, mode and date from original file. */
769 VATTR_WANTED(&va
, va_uid
);
770 VATTR_WANTED(&va
, va_gid
);
771 VATTR_WANTED(&va
, va_mode
);
772 VATTR_WANTED(&va
, va_create_time
);
773 VATTR_WANTED(&va
, va_modify_time
);
774 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
775 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
776 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
777 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
778 va
.va_uid
= KAUTH_UID_NONE
;
779 va
.va_gid
= KAUTH_GID_NONE
;
780 va
.va_mode
= S_IRUSR
| S_IWUSR
;
782 va
.va_vaflags
= VA_EXCLUSIVE
;
783 VATTR_SET(&va
, va_type
, VREG
);
784 /* We no longer change the access, but we still hide it. */
785 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
787 /* Obtain the vnode for the shadow files directory. */
788 if (get_shadow_dir(&dvp
) != 0) {
793 /* See if someone else already has it open. */
794 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
) == 0) {
795 /* Double check existence by asking for size. */
797 VATTR_WANTED(&va
, va_data_size
);
798 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
799 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
800 goto out
; /* OK to use. */
805 * Otherwise make sure the resource fork data exists.
806 * Use the supplied context for accessing the AD file.
808 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
809 XATTR_NOSECURITY
, context
);
811 * To maintain binary compatibility with legacy Carbon
812 * emulated resource fork support, if the resource fork
813 * doesn't exist but the Finder Info does, then act as
814 * if an empty resource fork is present (see 4724359).
816 if ((error
== ENOATTR
) &&
817 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
818 XATTR_NOSECURITY
, context
) == 0)) {
826 /* If the resource fork exists, its size is expected to be non-zero. */
833 /* Create the shadow stream file. */
834 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, kernelctx
);
838 } else if ((error
== EEXIST
) && !makestream
) {
839 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
);
840 } else if ((error
== ENOENT
) && !makestream
) {
842 * We could have raced with a rmdir on the shadow directory
843 * post-lookup. Retry from the beginning, 1x only, to
844 * try and see if we need to re-create the shadow directory
859 /* Otherwise, just error out normally below */
867 /* On errors, clean up shadow stream file. */
875 *rsrcsize
= datasize
;
882 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
884 vnode_t svp
= NULLVP
;
886 caddr_t bufptr
= NULL
;
892 /* need the kernel context for accessing the shadowfile */
893 vfs_context_t kernelctx
= vfs_context_kernel();
896 * Only the "com.apple.ResourceFork" stream is supported here.
898 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
904 * Obtain a shadow file for the resource fork I/O.
906 * Need to pass along the supplied context so that getshadowfile
907 * can access the AD file as needed, using it.
909 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
916 * The creator of the shadow file provides its file data,
917 * all other threads should wait until its ready. In order to
918 * prevent a deadlock during error codepaths, we need to check if the
919 * vnode is being created, or if it has failed out. Regardless of success or
920 * failure, we set the VISSHADOW bit on the vnode, so we check that
921 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
922 * then we can infer the creator isn't done yet. If it's there, but
923 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
928 if (svp
->v_flag
& VISNAMEDSTREAM
) {
929 /* data is ready, go use it */
933 /* It's not ready, wait for it (sleep using v_parent as channel) */
934 if ((svp
->v_flag
& VISSHADOW
)) {
936 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
937 * thread is done with this vnode. Just unlock the vnode and try again
941 /* Otherwise, sleep if the shadow file is not created yet */
942 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
943 "getnamedstream", NULL
);
952 * Copy the real resource fork data into shadow stream file.
954 if (op
== NS_OPEN
&& datasize
!= 0) {
958 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
959 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
964 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
967 /* open the shadow file */
968 error
= VNOP_OPEN(svp
, 0, kernelctx
);
972 while (offset
< datasize
) {
975 iosize
= MIN(datasize
- offset
, iosize
);
977 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
978 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
979 /* use supplied ctx for AD file */
980 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
981 XATTR_NOSECURITY
, context
);
986 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
987 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
988 /* kernel context for writing shadowfile */
989 error
= VNOP_WRITE(svp
, auio
, 0, kernelctx
);
996 /* close shadow file */
997 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
1000 /* Wake up anyone waiting for svp file content */
1004 /* VISSHADOW would be set later on anyway, so we set it now */
1005 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
1006 wakeup((caddr_t
)&svp
->v_parent
);
1009 /* On post create errors, get rid of the shadow file. This
1010 * way if there is another process waiting for initialization
1011 * of the shadowfile by the current process will wake up and
1012 * retry by creating and initializing the shadow file again.
1013 * Also add the VISSHADOW bit here to indicate we're done operating
1016 (void)vnode_relenamedstream(vp
, svp
);
1018 svp
->v_flag
|= VISSHADOW
;
1019 wakeup((caddr_t
)&svp
->v_parent
);
1025 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
1031 /* On errors, clean up shadow stream file. */
1042 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
1048 * Only the "com.apple.ResourceFork" stream is supported here.
1050 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1055 /* Supply the context to getshadowfile so it can manipulate the AD file */
1056 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
1059 * Wake up any waiters over in default_getnamedstream().
1061 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
1062 vnode_t svp
= *svpp
;
1065 /* If we're the creator, mark it as a named stream */
1066 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
1067 /* Wakeup any waiters on the v_parent channel */
1068 wakeup((caddr_t
)&svp
->v_parent
);
1076 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
1079 * Only the "com.apple.ResourceFork" stream is supported here.
1081 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1085 * XXX - what about other opened instances?
1087 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
1091 get_shadow_dir(vnode_t
*sdvpp
)
1093 vnode_t dvp
= NULLVP
;
1094 vnode_t sdvp
= NULLVP
;
1095 struct componentname cn
;
1096 struct vnode_attr va
;
1100 vfs_context_t kernelctx
= vfs_context_kernel();
1102 bzero(tmpname
, sizeof(tmpname
));
1103 MAKE_SHADOW_DIRNAME(rootvnode
, tmpname
);
1105 * Look up the shadow directory to ensure that it still exists.
1106 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1107 * in caching it when multiple threads may be trying to manipulate the pointers.
1109 * Make sure to use the kernel context. We want a singular view of
1110 * the shadow dir regardless of chrooted processes.
1112 error
= vnode_lookup(tmpname
, 0, &sdvp
, kernelctx
);
1115 * If we get here, then we have successfully looked up the shadow dir,
1116 * and it has an iocount from the lookup. Return the vp in the output argument.
1121 /* In the failure case, no iocount is acquired */
1123 bzero(tmpname
, sizeof(tmpname
));
1126 * Obtain the vnode for "/var/run" directory using the kernel
1129 * This is defined in the SHADOW_DIR_CONTAINER macro
1131 if (vnode_lookup(SHADOW_DIR_CONTAINER
, 0, &dvp
, kernelctx
) != 0) {
1137 * Create the shadow stream directory.
1138 * 'dvp' below suggests the parent directory so
1139 * we only need to provide the leaf entry name
1141 MAKE_SHADOW_DIR_LEAF(rootvnode
, tmpname
);
1142 bzero(&cn
, sizeof(cn
));
1143 cn
.cn_nameiop
= LOOKUP
;
1144 cn
.cn_flags
= ISLASTCN
;
1145 cn
.cn_context
= kernelctx
;
1146 cn
.cn_pnbuf
= tmpname
;
1147 cn
.cn_pnlen
= sizeof(tmpname
);
1148 cn
.cn_nameptr
= cn
.cn_pnbuf
;
1149 cn
.cn_namelen
= strlen(tmpname
);
1152 * owned by root, only readable by root, hidden
1155 VATTR_SET(&va
, va_uid
, 0);
1156 VATTR_SET(&va
, va_gid
, 0);
1157 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
1158 VATTR_SET(&va
, va_type
, VDIR
);
1159 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
1160 va
.va_vaflags
= VA_EXCLUSIVE
;
1162 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, kernelctx
);
1165 * There can be only one winner for an exclusive create.
1167 if (error
== EEXIST
) {
1168 /* loser has to look up directory */
1169 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, kernelctx
);
1171 /* Make sure its in fact a directory */
1172 if (sdvp
->v_type
!= VDIR
) {
1175 /* Obtain the fsid for /var/run directory */
1177 VATTR_WANTED(&va
, va_fsid
);
1178 if (VNOP_GETATTR(dvp
, &va
, kernelctx
) != 0 ||
1179 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1182 tmp_fsid
= va
.va_fsid
;
1185 VATTR_WANTED(&va
, va_uid
);
1186 VATTR_WANTED(&va
, va_gid
);
1187 VATTR_WANTED(&va
, va_mode
);
1188 VATTR_WANTED(&va
, va_fsid
);
1189 VATTR_WANTED(&va
, va_dirlinkcount
);
1190 VATTR_WANTED(&va
, va_acl
);
1191 /* Provide defaults for attrs that may not be supported */
1192 va
.va_dirlinkcount
= 1;
1193 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1195 if (VNOP_GETATTR(sdvp
, &va
, kernelctx
) != 0 ||
1196 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1197 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1198 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1199 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1203 * Make sure its what we want:
1205 * - not writable by anyone
1206 * - on same file system as /var/run
1207 * - not a hard-linked directory
1208 * - no ACLs (they might grant write access)
1210 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1211 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1212 (va
.va_fsid
!= tmp_fsid
) ||
1213 (va
.va_dirlinkcount
!= 1) ||
1214 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1224 /* On errors, clean up shadow stream directory. */
1234 /* This is not the dir we're looking for, move along */
1235 ++shadow_sequence
; /* try something else next time */
1239 #endif /* NAMEDSTREAMS */
1242 #if CONFIG_APPLEDOUBLE
1244 * Default Implementation (Non-native EA)
1249 * Typical "._" AppleDouble Header File layout:
1250 * ------------------------------------------------------------
1252 * VERSION 0x00020000
1255 * .-- AD ENTRY[0] Finder Info Entry (must be first)
1256 * .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1258 * | ///////////// Fixed Size Data (32 bytes)
1261 * | ATTR ENTRY[0] --.
1262 * | ATTR ENTRY[1] --+--.
1263 * | ATTR ENTRY[2] --+--+--.
1265 * | ATTR ENTRY[N] --+--+--+--.
1266 * | ATTR DATA 0 <-' | | |
1267 * | //////////// | | |
1268 * | ATTR DATA 1 <----' | |
1269 * | ///////////// | |
1270 * | ATTR DATA 2 <-------' |
1273 * | ATTR DATA N <----------'
1275 * | Attribute Free Space
1277 * '----> RESOURCE FORK
1278 * ///////////// Variable Sized Data
1287 * ------------------------------------------------------------
1289 * NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1290 * stored as part of the Finder Info. The length in the Finder
1291 * Info AppleDouble entry includes the length of the extended
1292 * attribute header, attribute entries, and attribute data.
1296 * On Disk Data Structures
1298 * Note: Motorola 68K alignment and big-endian.
1300 * See RFC 1740 for additional information about the AppleDouble file format.
1304 #define ADH_MAGIC 0x00051607
1305 #define ADH_VERSION 0x00020000
1306 #define ADH_MACOSX "Mac OS X "
1309 * AppleDouble Entry ID's
1311 #define AD_DATA 1 /* Data fork */
1312 #define AD_RESOURCE 2 /* Resource fork */
1313 #define AD_REALNAME 3 /* File's name on home file system */
1314 #define AD_COMMENT 4 /* Standard Mac comment */
1315 #define AD_ICONBW 5 /* Mac black & white icon */
1316 #define AD_ICONCOLOR 6 /* Mac color icon */
1317 #define AD_UNUSED 7 /* Not used */
1318 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1319 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1320 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1321 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1322 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1323 #define AD_AFPNAME 13 /* Short name on AFP server */
1324 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1325 #define AD_AFPDIRID 15 /* AFP directory ID */
1326 #define AD_ATTRIBUTES AD_FINDERINFO
1329 #define ATTR_FILE_PREFIX "._"
1330 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1332 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1334 /* Implementation Limits */
1335 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1336 #define ATTR_MAX_HDR_SIZE 65536
1338 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1339 * size supported (including the attribute entries). All of
1340 * the attribute entries must reside within this limit. If
1341 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1342 * boundry, then all of the attribute data I/O is performed
1343 * separately from the attribute header I/O.
1345 * In particular, all of the attr_entry structures must lie
1346 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1347 * AppleDouble file. However, the attribute data (i.e. the
1348 * contents of the extended attributes) may extend beyond the
1349 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1350 * limit is to allow the implementation to optimize by reading
1351 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1355 #define FINDERINFOSIZE 32
1357 typedef struct apple_double_entry
{
1358 u_int32_t type
; /* entry type: see list, 0 invalid */
1359 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1360 u_int32_t length
; /* entry data length in bytes. */
1361 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1364 typedef struct apple_double_header
{
1365 u_int32_t magic
; /* == ADH_MAGIC */
1366 u_int32_t version
; /* format version: 2 = 0x00020000 */
1367 u_int32_t filler
[4];
1368 u_int16_t numEntries
; /* number of entries which follow */
1369 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1370 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1371 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1372 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1374 #define ADHDRSIZE (4+4+16+2)
1376 /* Entries are aligned on 4 byte boundaries */
1377 typedef struct attr_entry
{
1378 u_int32_t offset
; /* file offset to data */
1379 u_int32_t length
; /* size of attribute data */
1382 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1383 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1386 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1387 typedef struct attr_header
{
1388 apple_double_header_t appledouble
;
1389 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1390 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1391 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1392 u_int32_t data_start
; /* file offset to attribute data area */
1393 u_int32_t data_length
; /* length of attribute data area */
1394 u_int32_t reserved
[3];
1396 u_int16_t num_attrs
;
1397 } __attribute__((aligned(2), packed
)) attr_header_t
;
1400 /* Empty Resource Fork Header */
1401 typedef struct rsrcfork_header
{
1402 u_int32_t fh_DataOffset
;
1403 u_int32_t fh_MapOffset
;
1404 u_int32_t fh_DataLength
;
1405 u_int32_t fh_MapLength
;
1406 u_int8_t systemData
[112];
1407 u_int8_t appData
[128];
1408 u_int32_t mh_DataOffset
;
1409 u_int32_t mh_MapOffset
;
1410 u_int32_t mh_DataLength
;
1411 u_int32_t mh_MapLength
;
1413 u_int16_t mh_RefNum
;
1415 u_int8_t mh_InMemoryAttr
;
1418 u_int16_t typeCount
;
1419 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1421 #define RF_FIRST_RESOURCE 256
1422 #define RF_NULL_MAP_LENGTH 30
1423 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1425 /* Runtime information about the attribute file. */
1426 typedef struct attr_info
{
1427 vfs_context_t context
;
1432 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1433 apple_double_header_t
*filehdr
;
1434 apple_double_entry_t
*finderinfo
;
1435 apple_double_entry_t
*rsrcfork
;
1436 attr_header_t
*attrhdr
;
1437 attr_entry_t
*attr_entry
;
1439 u_int8_t emptyfinderinfo
;
1443 #define ATTR_SETTING 1
1445 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1447 #define ATTR_ENTRY_LENGTH(namelen) \
1448 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1450 #define ATTR_NEXT(ae) \
1451 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1453 #define ATTR_VALID(ae, ai) \
1454 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1456 #define SWAP16(x) OSSwapBigToHostInt16((x))
1457 #define SWAP32(x) OSSwapBigToHostInt32((x))
1458 #define SWAP64(x) OSSwapBigToHostInt64((x))
1461 static u_int32_t emptyfinfo
[8] = {0};
1465 * Local support routines
1467 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1469 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1471 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1473 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1475 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1477 static void rel_xattrinfo(attr_info_t
*ainfop
);
1479 static int write_xattrinfo(attr_info_t
*ainfop
);
1481 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1483 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1485 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1488 #if BYTE_ORDER == LITTLE_ENDIAN
1489 static void swap_adhdr(apple_double_header_t
*adh
);
1490 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1493 #define swap_adhdr(x)
1494 #define swap_attrhdr(x, y)
1497 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1498 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1499 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1503 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1504 * is in big endian (as it would exist on disk). Verifies the following:
1507 * - number of entries
1508 * - that each entry fits within the file size
1510 * If the header is invalid, ENOATTR is returned.
1512 * NOTE: Does not attempt to validate the extended attributes header that
1513 * may be embedded in the Finder Info entry.
1516 check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1519 u_int32_t header_end
;
1520 u_int32_t entry_end
;
1522 apple_double_header_t
*header
;
1524 rawsize
= ainfop
->rawsize
;
1525 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1527 /* Is the file big enough to contain an AppleDouble header? */
1528 if (rawsize
< offsetof(apple_double_header_t
, entries
)) {
1532 /* Swap the AppleDouble header fields to native order */
1533 header
->magic
= SWAP32(header
->magic
);
1534 header
->version
= SWAP32(header
->version
);
1535 header
->numEntries
= SWAP16(header
->numEntries
);
1537 /* Sanity check the AppleDouble header fields */
1538 if (header
->magic
!= ADH_MAGIC
||
1539 header
->version
!= ADH_VERSION
||
1540 header
->numEntries
< 1 ||
1541 header
->numEntries
> 15) {
1545 /* Calculate where the entries[] array ends */
1546 header_end
= offsetof(apple_double_header_t
, entries
) +
1547 header
->numEntries
* sizeof(apple_double_entry_t
);
1549 /* Is the file big enough to contain the AppleDouble entries? */
1550 if (rawsize
< header_end
) {
1554 /* Swap and sanity check each AppleDouble entry */
1555 for (i
= 0; i
< header
->numEntries
; i
++) {
1556 /* Swap the per-entry fields to native order */
1557 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1558 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1559 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1561 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1564 * Does the entry's content start within the header itself,
1565 * did the addition overflow, or does the entry's content
1566 * extend past the end of the file?
1568 if (header
->entries
[i
].offset
< header_end
||
1569 entry_end
< header
->entries
[i
].offset
||
1570 entry_end
> ainfop
->filesize
) {
1575 * Does the current entry's content overlap with a previous
1578 * Yes, this is O(N**2), and there are more efficient algorithms
1579 * for testing pairwise overlap of N ranges when N is large.
1580 * But we have already ensured N < 16, and N is almost always 2.
1581 * So there's no point in using a more complex algorithm.
1584 for (j
= 0; j
< i
; j
++) {
1585 if (entry_end
> header
->entries
[j
].offset
&&
1586 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1598 * Retrieve the data of an extended attribute.
1601 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1602 __unused
int options
, vfs_context_t context
)
1606 attr_header_t
*header
;
1607 attr_entry_t
*entry
;
1617 if (strcmp(name
, XATTR_RESOURCEFORK_NAME
) == 0) {
1620 * Open the file locked (shared) since the Carbon
1621 * File Manager may have the Apple Double file open
1622 * and could be changing the resource fork.
1624 fileflags
|= O_SHLOCK
;
1629 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1632 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1633 close_xattrfile(xvp
, fileflags
, context
);
1637 /* Get the Finder Info. */
1638 if (strcmp(name
, XATTR_FINDERINFO_NAME
) == 0) {
1639 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1641 } else if (uio
== NULL
) {
1642 *size
= FINDERINFOSIZE
;
1644 } else if (uio_offset(uio
) != 0) {
1646 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1649 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1650 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1655 /* Read the Resource Fork. */
1657 if (!vnode_isreg(vp
)) {
1659 } else if (ainfo
.rsrcfork
== NULL
) {
1661 } else if (uio
== NULL
) {
1662 *size
= (size_t)ainfo
.rsrcfork
->length
;
1664 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1665 error
= VNOP_READ(xvp
, uio
, 0, context
);
1667 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1673 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1677 if (uio_offset(uio
) != 0) {
1682 namelen
= strlen(name
) + 1;
1683 header
= ainfo
.attrhdr
;
1684 entry
= ainfo
.attr_entry
;
1686 * Search for attribute name in the header.
1688 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1689 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1690 datalen
= (size_t)entry
->length
;
1696 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1700 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1701 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1702 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1704 uio_setoffset(uio
, entry
->offset
);
1705 error
= VNOP_READ(xvp
, uio
, 0, context
);
1706 uio_setoffset(uio
, 0);
1710 entry
= ATTR_NEXT(entry
);
1713 rel_xattrinfo(&ainfo
);
1714 close_xattrfile(xvp
, fileflags
, context
);
1720 * Set the data of an extended attribute.
1723 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1727 attr_header_t
*header
;
1728 attr_entry_t
*entry
;
1729 attr_entry_t
*lastentry
;
1733 size_t datafreespace
;
1740 char finfo
[FINDERINFOSIZE
];
1742 datalen
= uio_resid(uio
);
1743 namelen
= strlen(name
) + 1;
1744 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1747 * By convention, Finder Info that is all zeroes is equivalent to not
1748 * having a Finder Info EA. So if we're trying to set the Finder Info
1749 * to all zeroes, then delete it instead. If a file didn't have an
1750 * AppleDouble file before, this prevents creating an AppleDouble file
1751 * with no useful content.
1753 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1754 * for all zeroes Finder Info before opening the AppleDouble file.
1755 * But if either of those options were specified, we need to open the
1756 * AppleDouble file to see whether there was already Finder Info (so we
1757 * can return an error if needed); this case is handled further below.
1759 * NOTE: this copies the Finder Info data into the "finfo" local.
1761 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1763 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1764 * That means we probably have to open_xattrfile and get_xattrinfo.
1766 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1769 error
= uiomove(finfo
, datalen
, uio
);
1773 if ((options
& (XATTR_CREATE
| XATTR_REPLACE
)) == 0 &&
1774 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1775 error
= default_removexattr(vp
, name
, 0, context
);
1776 if (error
== ENOATTR
) {
1785 * Open the file locked since setting an attribute
1786 * can change the layout of the Apple Double file.
1788 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1789 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1792 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1793 close_xattrfile(xvp
, fileflags
, context
);
1797 /* Set the Finder Info. */
1798 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1799 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1800 /* attr exists and "create" was specified? */
1801 if (options
& XATTR_CREATE
) {
1806 /* attr doesn't exists and "replace" was specified? */
1807 if (options
& XATTR_REPLACE
) {
1812 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1814 * Setting the Finder Info to all zeroes is equivalent to
1815 * removing it. Close the xattr file and let
1816 * default_removexattr do the work (including deleting
1817 * the xattr file if there are no other xattrs).
1819 * Note that we have to handle the case where the
1820 * Finder Info was already all zeroes, and we ignore
1823 * The common case where options == 0 was handled above.
1825 rel_xattrinfo(&ainfo
);
1826 close_xattrfile(xvp
, fileflags
, context
);
1827 error
= default_removexattr(vp
, name
, 0, context
);
1828 if (error
== ENOATTR
) {
1833 if (ainfo
.finderinfo
) {
1834 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1835 bcopy(finfo
, attrdata
, datalen
);
1836 ainfo
.iosize
= sizeof(attr_header_t
);
1837 error
= write_xattrinfo(&ainfo
);
1844 /* Write the Resource Fork. */
1845 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1846 u_int32_t endoffset
;
1848 if (!vnode_isreg(vp
)) {
1852 /* Make sure we have a rsrc fork pointer.. */
1853 if (ainfo
.rsrcfork
== NULL
) {
1857 if (ainfo
.rsrcfork
) {
1858 if (ainfo
.rsrcfork
->length
!= 0) {
1859 if (options
& XATTR_CREATE
) {
1860 /* attr exists, and create specified ? */
1865 /* Zero length AD rsrc fork */
1866 if (options
& XATTR_REPLACE
) {
1867 /* attr doesn't exist (0-length), but replace specified ? */
1873 /* We can't do much if we somehow didn't get an AD rsrc pointer */
1878 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1879 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1880 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1884 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1885 if (endoffset
> ainfo
.rsrcfork
->length
) {
1886 ainfo
.rsrcfork
->length
= endoffset
;
1887 ainfo
.iosize
= sizeof(attr_header_t
);
1888 error
= write_xattrinfo(&ainfo
);
1894 if (datalen
> ATTR_MAX_SIZE
) {
1895 return E2BIG
; /* EINVAL instead ? */
1898 if (ainfo
.attrhdr
== NULL
) {
1902 header
= ainfo
.attrhdr
;
1903 entry
= ainfo
.attr_entry
;
1905 /* Check if data area crosses the maximum header size. */
1906 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
) {
1907 splitdata
= 1; /* do data I/O separately */
1913 * See if attribute already exists.
1915 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1916 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1920 entry
= ATTR_NEXT(entry
);
1924 if (options
& XATTR_CREATE
) {
1928 if (datalen
== entry
->length
) {
1930 uio_setoffset(uio
, entry
->offset
);
1931 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1932 uio_setoffset(uio
, 0);
1934 printf("setxattr: VNOP_WRITE error %d\n", error
);
1937 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1938 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1942 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1943 error
= write_xattrinfo(&ainfo
);
1945 printf("setxattr: write_xattrinfo error %d\n", error
);
1951 * Brute force approach - just remove old entry and set new entry.
1954 rel_xattrinfo(&ainfo
);
1955 close_xattrfile(xvp
, fileflags
, context
);
1956 error
= default_removexattr(vp
, name
, options
, context
);
1960 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1961 options
&= ~XATTR_REPLACE
;
1962 goto start
; /* start over */
1966 if (options
& XATTR_REPLACE
) {
1967 error
= ENOATTR
; /* nothing there to replace */
1970 /* Check if header size limit has been reached. */
1971 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1976 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1978 /* Check if we need more space. */
1979 if ((datalen
+ entrylen
) > datafreespace
) {
1982 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1984 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1985 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1986 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1989 ainfo
.filesize
+= growsize
;
1990 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1992 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1999 * Move the resource fork out of the way.
2001 if (ainfo
.rsrcfork
) {
2002 if (ainfo
.rsrcfork
->length
!= 0) {
2003 shift_data_down(xvp
,
2004 ainfo
.rsrcfork
->offset
,
2005 ainfo
.rsrcfork
->length
,
2008 ainfo
.rsrcfork
->offset
+= growsize
;
2010 ainfo
.finderinfo
->length
+= growsize
;
2011 header
->total_size
+= growsize
;
2014 /* Make space for a new entry. */
2016 shift_data_down(xvp
,
2018 header
->data_length
,
2021 bcopy((u_int8_t
*)header
+ header
->data_start
,
2022 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
2023 header
->data_length
);
2025 header
->data_start
+= entrylen
;
2027 /* Fix up entry data offsets. */
2029 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
2030 entry
->offset
+= entrylen
;
2034 * If the attribute data area is entirely within
2035 * the header buffer, then just update the buffer,
2036 * otherwise we'll write it separately to the file.
2041 /* Write new attribute data after the end of existing data. */
2042 offset
= header
->data_start
+ header
->data_length
;
2043 uio_setoffset(uio
, offset
);
2044 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
2045 uio_setoffset(uio
, 0);
2047 printf("setxattr: VNOP_WRITE error %d\n", error
);
2051 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
2053 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
2055 printf("setxattr: uiomove error %d\n", error
);
2060 /* Create the attribute entry. */
2061 lastentry
->length
= datalen
;
2062 lastentry
->offset
= header
->data_start
+ header
->data_length
;
2063 lastentry
->namelen
= namelen
;
2064 lastentry
->flags
= 0;
2065 bcopy(name
, &lastentry
->name
[0], namelen
);
2067 /* Update the attributes header. */
2068 header
->num_attrs
++;
2069 header
->data_length
+= datalen
;
2072 /* Only write the entries, since the data was written separately. */
2073 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
2075 /* The entry and data are both in the header; write them together. */
2076 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2078 error
= write_xattrinfo(&ainfo
);
2080 printf("setxattr: write_xattrinfo error %d\n", error
);
2084 rel_xattrinfo(&ainfo
);
2085 close_xattrfile(xvp
, fileflags
, context
);
2087 /* Touch the change time if we changed an attribute. */
2089 struct vnode_attr va
;
2091 /* Re-write the mtime to cause a ctime change. */
2093 VATTR_WANTED(&va
, va_modify_time
);
2094 if (vnode_getattr(vp
, &va
, context
) == 0) {
2096 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2097 (void) vnode_setattr(vp
, &va
, context
);
2101 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2108 * Remove an extended attribute.
2111 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
2115 attr_header_t
*header
;
2116 attr_entry_t
*entry
;
2117 attr_entry_t
*oldslot
;
2123 int found
= 0, lastone
= 0;
2131 fileflags
= FREAD
| FWRITE
;
2132 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
2135 * Open the file locked (exclusive) since the Carbon
2136 * File Manager may have the Apple Double file open
2137 * and could be changing the resource fork.
2139 fileflags
|= O_EXLOCK
;
2144 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
2147 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2148 close_xattrfile(xvp
, fileflags
, context
);
2151 if (ainfo
.attrhdr
) {
2152 attrcount
+= ainfo
.attrhdr
->num_attrs
;
2154 if (ainfo
.rsrcfork
) {
2157 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2161 /* Clear the Finder Info. */
2162 if (strncmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
2163 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
2167 /* On removal of last attribute the ._ file is removed. */
2168 if (--attrcount
== 0) {
2171 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
2172 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
2173 ainfo
.iosize
= sizeof(attr_header_t
);
2174 error
= write_xattrinfo(&ainfo
);
2178 /* Clear the Resource Fork. */
2180 if (!vnode_isreg(vp
)) {
2184 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
2188 /* On removal of last attribute the ._ file is removed. */
2189 if (--attrcount
== 0) {
2194 * If the resource fork isn't the last AppleDouble
2195 * entry then the space needs to be reclaimed by
2196 * shifting the entries after the resource fork.
2198 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
2199 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
2200 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2203 ainfo
.rsrcfork
->length
= 0;
2204 ainfo
.iosize
= sizeof(attr_header_t
);
2205 error
= write_xattrinfo(&ainfo
);
2210 if (ainfo
.attrhdr
== NULL
) {
2214 namelen
= strlen(name
) + 1;
2215 header
= ainfo
.attrhdr
;
2216 entry
= ainfo
.attr_entry
;
2219 * See if this attribute exists.
2221 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2222 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2224 if ((i
+ 1) == header
->num_attrs
) {
2229 entry
= ATTR_NEXT(entry
);
2235 /* On removal of last attribute the ._ file is removed. */
2236 if (--attrcount
== 0) {
2240 datalen
= entry
->length
;
2241 dataoff
= entry
->offset
;
2242 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2243 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
) {
2249 /* Remove the attribute entry. */
2251 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2252 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2255 /* Adjust the attribute data. */
2259 dataoff
- header
->data_start
,
2265 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2269 /* XXX write zeros to freed space ? */
2270 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2272 bcopy((u_int8_t
*)header
+ header
->data_start
,
2273 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2274 dataoff
- header
->data_start
);
2276 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2277 (u_int8_t
*)header
+ dataoff
- entrylen
,
2278 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2280 bzero(((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2281 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2284 /* Adjust the header values and entry offsets. */
2285 header
->num_attrs
--;
2286 header
->data_start
-= entrylen
;
2287 header
->data_length
-= datalen
;
2290 entry
= ainfo
.attr_entry
;
2291 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2292 entry
->offset
-= entrylen
;
2293 if (entry
>= oldslot
) {
2294 entry
->offset
-= datalen
;
2296 entry
= ATTR_NEXT(entry
);
2298 error
= write_xattrinfo(&ainfo
);
2300 printf("removexattr: write_xattrinfo error %d\n", error
);
2303 rel_xattrinfo(&ainfo
);
2305 /* When there are no more attributes remove the ._ file. */
2306 if (attrcount
== 0) {
2307 if (fileflags
& O_EXLOCK
) {
2308 (void) unlock_xattrfile(xvp
, context
);
2310 VNOP_CLOSE(xvp
, fileflags
, context
);
2312 error
= remove_xattrfile(xvp
, context
);
2315 close_xattrfile(xvp
, fileflags
, context
);
2317 /* Touch the change time if we changed an attribute. */
2319 struct vnode_attr va
;
2321 /* Re-write the mtime to cause a ctime change. */
2323 VATTR_WANTED(&va
, va_modify_time
);
2324 if (vnode_getattr(vp
, &va
, context
) == 0) {
2326 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2327 (void) vnode_setattr(vp
, &va
, context
);
2331 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2338 * Retrieve the list of extended attribute names.
2341 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2345 attr_entry_t
*entry
;
2350 * We do not zero "*size" here as we don't want to stomp a size set when
2351 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2352 * system call layer, up in listxattr or flistxattr.
2355 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2356 if (error
== ENOATTR
) {
2361 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2362 if (error
== ENOATTR
) {
2365 close_xattrfile(xvp
, FREAD
, context
);
2369 /* Check for Finder Info. */
2370 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2372 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2373 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2377 error
= uiomove(XATTR_FINDERINFO_NAME
,
2378 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2386 /* Check for Resource Fork. */
2387 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2389 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2390 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2394 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2395 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2403 /* Check for attributes. */
2404 if (ainfo
.attrhdr
) {
2405 count
= ainfo
.attrhdr
->num_attrs
;
2406 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2407 if (xattr_protected((const char *)entry
->name
) ||
2408 ((entry
->namelen
< XATTR_MAXNAMELEN
) &&
2409 (entry
->name
[entry
->namelen
] == '\0') &&
2410 (xattr_validatename((const char *)entry
->name
) != 0))) {
2411 entry
= ATTR_NEXT(entry
);
2415 *size
+= entry
->namelen
;
2416 entry
= ATTR_NEXT(entry
);
2419 if (uio_resid(uio
) < entry
->namelen
) {
2423 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2425 if (error
!= EFAULT
) {
2430 entry
= ATTR_NEXT(entry
);
2434 rel_xattrinfo(&ainfo
);
2435 close_xattrfile(xvp
, FREAD
, context
);
2441 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2443 vnode_t xvp
= NULLVP
;
2444 vnode_t dvp
= NULLVP
;
2445 struct vnode_attr va
;
2446 struct nameidata nd
;
2448 char *filename
= NULL
;
2449 const char *basename
= NULL
;
2455 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2457 * For the root directory use "._." to hold the attributes.
2459 filename
= &smallname
[0];
2460 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2461 dvp
= vp
; /* the "._." file resides in the root dir */
2464 if ((dvp
= vnode_getparent(vp
)) == NULLVP
) {
2468 if ((basename
= vnode_getname(vp
)) == NULL
) {
2473 /* "._" Attribute files cannot have attributes */
2474 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2475 basename
[0] == '.' && basename
[1] == '_') {
2479 filename
= &smallname
[0];
2480 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2481 if (len
>= sizeof(smallname
)) {
2482 len
++; /* snprintf result doesn't include '\0' */
2483 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2484 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2487 * Note that the lookup here does not authorize. Since we are looking
2488 * up in the same directory that we already have the file vnode in,
2489 * we must have been given the file vnode legitimately. Read/write
2490 * access has already been authorized in layers above for calls from
2491 * userspace, and the authorization code using this path to read
2492 * file security from the EA must always get access
2495 NDINIT(&nd
, LOOKUP
, OP_OPEN
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
,
2496 UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), context
);
2499 if (fileflags
& O_CREAT
) {
2500 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2505 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2507 if ((error
= namei(&nd
))) {
2512 if ((xvp
= nd
.ni_vp
) == NULLVP
) {
2518 * Pick up uid/gid/mode from target file.
2521 VATTR_WANTED(&va
, va_uid
);
2522 VATTR_WANTED(&va
, va_gid
);
2523 VATTR_WANTED(&va
, va_mode
);
2524 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2525 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2526 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2527 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2530 umode
= va
.va_mode
& (S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
);
2531 } else { /* fallback values */
2532 uid
= KAUTH_UID_NONE
;
2533 gid
= KAUTH_GID_NONE
;
2534 umode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
2538 VATTR_SET(&va
, va_type
, VREG
);
2539 VATTR_SET(&va
, va_mode
, umode
);
2540 if (uid
!= KAUTH_UID_NONE
) {
2541 VATTR_SET(&va
, va_uid
, uid
);
2543 if (gid
!= KAUTH_GID_NONE
) {
2544 VATTR_SET(&va
, va_gid
, gid
);
2547 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
, &va
,
2548 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2559 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2565 if ((error
= namei(&nd
))) {
2575 if (xvp
->v_type
!= VREG
) {
2580 * Owners must match.
2583 VATTR_WANTED(&va
, va_uid
);
2584 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2585 uid_t owner
= va
.va_uid
;
2588 VATTR_WANTED(&va
, va_uid
);
2589 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2590 error
= ENOATTR
; /* don't use this "._" file */
2595 if ((error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2601 if ((error
= vnode_ref(xvp
))) {
2606 /* If create was requested, make sure file header exists. */
2607 if (fileflags
& O_CREAT
) {
2609 VATTR_WANTED(&va
, va_data_size
);
2610 VATTR_WANTED(&va
, va_fileid
);
2611 VATTR_WANTED(&va
, va_nlink
);
2612 if ((error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2617 /* If the file is empty then add a default header. */
2618 if (va
.va_data_size
== 0) {
2619 /* Don't adopt hard-linked "._" files. */
2620 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2624 if ((error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
))) {
2629 /* Apply file locking if requested. */
2630 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2633 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2634 error
= lock_xattrfile(xvp
, locktype
, context
);
2641 if (xvp
!= NULLVP
) {
2643 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2646 if (fileflags
& O_CREAT
) {
2647 /* Delete the xattr file if we encountered any errors */
2648 (void) remove_xattrfile(xvp
, context
);
2652 (void) vnode_rele(xvp
);
2654 (void) vnode_put(xvp
);
2657 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2661 /* Release resources after error-handling */
2662 if (dvp
&& (dvp
!= vp
)) {
2666 vnode_putname(basename
);
2668 if (filename
&& filename
!= &smallname
[0]) {
2669 FREE(filename
, M_TEMP
);
2672 *xvpp
= xvp
; /* return a referenced vnode */
2677 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2679 // if (fileflags & FWRITE)
2680 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2682 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2683 (void) unlock_xattrfile(xvp
, context
);
2686 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2687 (void) vnode_rele(xvp
);
2688 (void) vnode_put(xvp
);
2692 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2695 struct nameidata nd
;
2700 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2705 pathlen
= MAXPATHLEN
;
2706 error
= vn_getpath(xvp
, path
, &pathlen
);
2708 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2712 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2713 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2715 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2722 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2731 * Read in and parse the AppleDouble header and entries, and the extended
2732 * attribute header and entries if any. Populates the fields of ainfop
2733 * based on the headers and entries found.
2735 * The basic idea is to:
2736 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2737 * AppleDouble entries, the extended attribute header, and extended
2738 * attribute entries must lie within this part of the file; the rest of
2739 * the AppleDouble handling code assumes this. Plus it allows us to
2740 * somewhat optimize by doing a smaller number of larger I/Os.
2741 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2743 * - Find the Finder Info and Resource Fork entries, if any.
2744 * - If we're going to be writing, try to make sure the Finder Info entry has
2745 * room to store the extended attribute header, plus some space for extended
2747 * - Swap and sanity check the extended attribute header and entries (if any).
2750 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2753 void * buffer
= NULL
;
2754 apple_double_header_t
*filehdr
;
2755 struct vnode_attr va
;
2760 bzero(ainfop
, sizeof(attr_info_t
));
2761 ainfop
->filevp
= xvp
;
2762 ainfop
->context
= context
;
2764 VATTR_WANTED(&va
, va_data_size
);
2765 VATTR_WANTED(&va
, va_fileid
);
2766 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2769 ainfop
->filesize
= va
.va_data_size
;
2771 /* When setting attributes, allow room for the header to grow. */
2773 iosize
= ATTR_MAX_HDR_SIZE
;
2775 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2782 ainfop
->iosize
= iosize
;
2783 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2784 if (buffer
== NULL
) {
2789 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2790 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2792 /* Read the file header. */
2793 error
= VNOP_READ(xvp
, auio
, 0, context
);
2797 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2798 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2800 filehdr
= (apple_double_header_t
*)buffer
;
2802 error
= check_and_swap_apple_double_header(ainfop
);
2807 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2809 /* rel_xattrinfo is responsible for freeing the header buffer */
2812 /* Find the Finder Info and Resource Fork entries, if any */
2813 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2814 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2815 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2816 /* We found the Finder Info entry. */
2817 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2820 * Is the Finder Info "empty" (all zeroes)? If so,
2821 * we'll pretend like the Finder Info extended attribute
2824 * Note: we have to make sure the Finder Info is
2825 * contained within the buffer we have already read,
2826 * to avoid accidentally accessing a bogus address.
2827 * If it is outside the buffer, we just assume the
2828 * Finder Info is non-empty.
2830 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2831 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2832 ainfop
->emptyfinderinfo
= 1;
2835 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2837 * Ignore zero-length resource forks when getting. If setting,
2838 * we need to remember the resource fork entry so it can be
2839 * updated once the new content has been written.
2841 if (filehdr
->entries
[i
].length
== 0 && !setting
) {
2846 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2848 * The "empty" resource headers we created have a system data tag of:
2849 * "This resource fork intentionally left blank "
2851 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2853 u_int8_t systemData
[64];
2857 /* Read the system data which starts at byte 16 */
2858 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2859 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2860 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2861 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2865 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2866 continue; /* skip this resource fork */
2869 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2870 if (i
!= (filehdr
->numEntries
- 1)) {
2871 printf("get_xattrinfo: resource fork not last entry\n");
2872 ainfop
->readonly
= 1;
2879 * See if this file looks like it is laid out correctly to contain
2880 * extended attributes. If so, then do the following:
2882 * - If we're going to be writing, try to make sure the Finder Info
2883 * entry has room to store the extended attribute header, plus some
2884 * space for extended attributes.
2886 * - Swap and sanity check the extended attribute header and entries
2889 if (filehdr
->numEntries
== 2 &&
2890 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2891 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2892 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2893 attr_header_t
*attrhdr
;
2894 attrhdr
= (attr_header_t
*)filehdr
;
2896 * If we're going to be writing, try to make sure the Finder
2897 * Info entry has room to store the extended attribute header,
2898 * plus some space for extended attributes.
2900 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2904 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2905 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2906 /* Make some room before existing resource fork. */
2907 shift_data_down(xvp
,
2908 filehdr
->entries
[1].offset
,
2909 filehdr
->entries
[1].length
,
2911 writesize
= sizeof(attr_header_t
);
2913 /* Create a new, empty resource fork. */
2914 rsrcfork_header_t
*rsrcforkhdr
;
2916 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2918 /* Steal some space for an empty RF header. */
2919 delta
-= sizeof(rsrcfork_header_t
);
2921 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2922 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2924 /* Fill in Empty Resource Fork Header. */
2925 init_empty_resource_fork(rsrcforkhdr
);
2927 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2928 writesize
= ATTR_BUF_SIZE
;
2930 filehdr
->entries
[0].length
+= delta
;
2931 filehdr
->entries
[1].offset
+= delta
;
2933 /* Fill in Attribute Header. */
2934 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2935 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2936 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2937 attrhdr
->data_start
= sizeof(attr_header_t
);
2938 attrhdr
->data_length
= 0;
2939 attrhdr
->reserved
[0] = 0;
2940 attrhdr
->reserved
[1] = 0;
2941 attrhdr
->reserved
[2] = 0;
2943 attrhdr
->num_attrs
= 0;
2945 /* Push out new header */
2946 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2947 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2949 swap_adhdr(filehdr
); /* to big endian */
2950 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2951 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2952 swap_adhdr(filehdr
); /* back to native */
2953 /* The attribute header gets swapped below. */
2957 * Swap and sanity check the extended attribute header and
2958 * entries (if any). The Finder Info content must be big enough
2959 * to include the extended attribute header; if not, we just
2962 * Note that we're passing the offset + length (i.e. the end)
2963 * of the Finder Info instead of rawsize to validate_attrhdr.
2964 * This ensures that all extended attributes lie within the
2965 * Finder Info content according to the AppleDouble entry.
2967 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2970 if (ainfop
->finderinfo
&&
2971 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2972 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2973 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2975 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2976 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2977 /* First attr_entry starts immediately following attribute header */
2978 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2987 if (buffer
!= NULL
) {
2988 FREE(buffer
, M_TEMP
);
2995 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2998 rsrcfork_header_t
*rsrcforkhdr
;
3004 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
3005 bzero(buffer
, ATTR_BUF_SIZE
);
3007 xah
= (attr_header_t
*)buffer
;
3008 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3009 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
3010 rsrcforksize
= sizeof(rsrcfork_header_t
);
3011 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
3013 /* Fill in Apple Double Header. */
3014 xah
->appledouble
.magic
= SWAP32(ADH_MAGIC
);
3015 xah
->appledouble
.version
= SWAP32(ADH_VERSION
);
3016 xah
->appledouble
.numEntries
= SWAP16(2);
3017 xah
->appledouble
.entries
[0].type
= SWAP32(AD_FINDERINFO
);
3018 xah
->appledouble
.entries
[0].offset
= SWAP32(offsetof(apple_double_header_t
, finfo
));
3019 xah
->appledouble
.entries
[0].length
= SWAP32(ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
3020 xah
->appledouble
.entries
[1].type
= SWAP32(AD_RESOURCE
);
3021 xah
->appledouble
.entries
[1].offset
= SWAP32(ATTR_BUF_SIZE
- rsrcforksize
);
3022 xah
->appledouble
.entries
[1].length
= SWAP32(rsrcforksize
);
3023 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
3025 /* Fill in Attribute Header. */
3026 xah
->magic
= SWAP32(ATTR_HDR_MAGIC
);
3027 xah
->debug_tag
= SWAP32(fileid
);
3028 xah
->total_size
= SWAP32(ATTR_BUF_SIZE
- rsrcforksize
);
3029 xah
->data_start
= SWAP32(sizeof(attr_header_t
));
3031 /* Fill in Empty Resource Fork Header. */
3032 init_empty_resource_fork(rsrcforkhdr
);
3035 error
= VNOP_WRITE(xvp
, auio
, IO_UNIT
, context
);
3037 /* Did we write out the full uio? */
3038 if (uio_resid(auio
) > 0) {
3043 FREE(buffer
, M_TEMP
);
3049 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
3051 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
3052 rsrcforkhdr
->fh_DataOffset
= SWAP32(RF_FIRST_RESOURCE
);
3053 rsrcforkhdr
->fh_MapOffset
= SWAP32(RF_FIRST_RESOURCE
);
3054 rsrcforkhdr
->fh_MapLength
= SWAP32(RF_NULL_MAP_LENGTH
);
3055 rsrcforkhdr
->mh_DataOffset
= SWAP32(RF_FIRST_RESOURCE
);
3056 rsrcforkhdr
->mh_MapOffset
= SWAP32(RF_FIRST_RESOURCE
);
3057 rsrcforkhdr
->mh_MapLength
= SWAP32(RF_NULL_MAP_LENGTH
);
3058 rsrcforkhdr
->mh_Types
= SWAP16(RF_NULL_MAP_LENGTH
- 2 );
3059 rsrcforkhdr
->mh_Names
= SWAP16(RF_NULL_MAP_LENGTH
);
3060 rsrcforkhdr
->typeCount
= SWAP16(-1);
3061 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
3065 rel_xattrinfo(attr_info_t
*ainfop
)
3067 FREE(ainfop
->filehdr
, M_TEMP
);
3068 bzero(ainfop
, sizeof(attr_info_t
));
3072 write_xattrinfo(attr_info_t
*ainfop
)
3077 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3078 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
3080 swap_adhdr(ainfop
->filehdr
);
3081 if (ainfop
->attrhdr
!= NULL
) {
3082 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3085 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
3087 swap_adhdr(ainfop
->filehdr
);
3088 if (ainfop
->attrhdr
!= NULL
) {
3089 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3096 #if BYTE_ORDER == LITTLE_ENDIAN
3098 * Endian swap apple double header
3101 swap_adhdr(apple_double_header_t
*adh
)
3106 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
3108 adh
->magic
= SWAP32(adh
->magic
);
3109 adh
->version
= SWAP32(adh
->version
);
3110 adh
->numEntries
= SWAP16(adh
->numEntries
);
3112 for (i
= 0; i
< count
; i
++) {
3113 adh
->entries
[i
].type
= SWAP32(adh
->entries
[i
].type
);
3114 adh
->entries
[i
].offset
= SWAP32(adh
->entries
[i
].offset
);
3115 adh
->entries
[i
].length
= SWAP32(adh
->entries
[i
].length
);
3120 * Endian swap extended attributes header
3123 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
3129 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
3131 ah
->magic
= SWAP32(ah
->magic
);
3132 ah
->debug_tag
= SWAP32(ah
->debug_tag
);
3133 ah
->total_size
= SWAP32(ah
->total_size
);
3134 ah
->data_start
= SWAP32(ah
->data_start
);
3135 ah
->data_length
= SWAP32(ah
->data_length
);
3136 ah
->flags
= SWAP16(ah
->flags
);
3137 ah
->num_attrs
= SWAP16(ah
->num_attrs
);
3139 ae
= (attr_entry_t
*)(&ah
[1]);
3140 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
3141 ae
->offset
= SWAP32(ae
->offset
);
3142 ae
->length
= SWAP32(ae
->length
);
3143 ae
->flags
= SWAP16(ae
->flags
);
3149 * Validate and swap the attributes header contents, and each attribute's
3152 * Note: Assumes the caller has verified that the Finder Info content is large
3153 * enough to contain the attr_header structure itself. Therefore, we can
3154 * swap the header fields before sanity checking them.
3157 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
3169 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
) {
3173 /* Swap the basic header fields */
3174 ah
->magic
= SWAP32(ah
->magic
);
3175 ah
->debug_tag
= SWAP32(ah
->debug_tag
);
3176 ah
->total_size
= SWAP32(ah
->total_size
);
3177 ah
->data_start
= SWAP32(ah
->data_start
);
3178 ah
->data_length
= SWAP32(ah
->data_length
);
3179 ah
->flags
= SWAP16(ah
->flags
);
3180 ah
->num_attrs
= SWAP16(ah
->num_attrs
);
3183 * Make sure the total_size fits within the Finder Info area, and the
3184 * extended attribute data area fits within total_size.
3186 end
= ah
->data_start
+ ah
->data_length
;
3187 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3188 end
< ah
->data_start
||
3189 end
> ah
->total_size
) {
3194 * Make sure each of the attr_entry_t's fits within total_size.
3196 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3197 count
= ah
->num_attrs
;
3198 ae
= (attr_entry_t
*)(&ah
[1]);
3200 for (i
= 0; i
< count
; i
++) {
3201 /* Make sure the fixed-size part of this attr_entry_t fits. */
3202 if ((u_int8_t
*) &ae
[1] > buf_end
) {
3206 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3207 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3208 if (&ae
->name
[ae
->namelen
+ 1] > buf_end
) {
3212 /* Swap the attribute entry fields */
3213 ae
->offset
= SWAP32(ae
->offset
);
3214 ae
->length
= SWAP32(ae
->length
);
3215 ae
->flags
= SWAP16(ae
->flags
);
3217 /* Make sure the attribute content fits. */
3218 end
= ae
->offset
+ ae
->length
;
3219 if (end
< ae
->offset
|| end
> ah
->total_size
) {
3227 * TODO: Make sure the contents of attributes don't overlap the header
3228 * and don't overlap each other. The hard part is that we don't know
3229 * what the actual header size is until we have looped over all of the
3230 * variable-sized attribute entries.
3232 * XXX Is there any guarantee that attribute entries are stored in
3233 * XXX order sorted by the contents' file offset? If so, that would
3234 * XXX make the pairwise overlap check much easier.
3241 // "start" & "end" are byte offsets in the file.
3242 // "to" is the byte offset we want to move the
3243 // data to. "to" should be > "start".
3245 // we do the copy backwards to avoid problems if
3246 // there's an overlap.
3249 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3252 size_t chunk
, orig_chunk
;
3255 kauth_cred_t ucred
= vfs_context_ucred(context
);
3256 proc_t p
= vfs_context_proc(context
);
3258 if (delta
== 0 || len
== 0) {
3268 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3272 for (pos
= start
+ len
- chunk
; pos
>= start
; pos
-= chunk
) {
3273 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
| IO_NOAUTH
, ucred
, &iolen
, p
);
3275 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3276 pos
, ret
, chunk
, ret
);
3280 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
| IO_NOAUTH
, ucred
, &iolen
, p
);
3282 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3283 pos
+ delta
, ret
, chunk
, ret
);
3287 if ((pos
- (off_t
)chunk
) < start
) {
3288 chunk
= pos
- start
;
3290 if (chunk
== 0) { // we're all done
3295 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3302 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3305 size_t chunk
, orig_chunk
;
3309 kauth_cred_t ucred
= vfs_context_ucred(context
);
3310 proc_t p
= vfs_context_proc(context
);
3312 if (delta
== 0 || len
== 0) {
3323 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3327 for (pos
= start
; pos
< end
; pos
+= chunk
) {
3328 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
| IO_NOAUTH
, ucred
, &iolen
, p
);
3330 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3331 pos
, ret
, chunk
, ret
);
3335 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
| IO_NOAUTH
, ucred
, &iolen
, p
);
3337 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3338 pos
+ delta
, ret
, chunk
, ret
);
3342 if ((pos
+ (off_t
)chunk
) > end
) {
3345 if (chunk
== 0) { // we're all done
3350 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3356 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3361 lf
.l_whence
= SEEK_SET
;
3364 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3365 /* Note: id is just a kernel address that's not a proc */
3366 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
| F_WAIT
, context
, NULL
);
3367 return error
== ENOTSUP
? 0 : error
;
3371 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3376 lf
.l_whence
= SEEK_SET
;
3379 lf
.l_type
= F_UNLCK
;
3380 /* Note: id is just a kernel address that's not a proc */
3381 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
, NULL
);
3382 return error
== ENOTSUP
? 0 : error
;
3385 #else /* CONFIG_APPLEDOUBLE */
3389 default_getxattr(__unused vnode_t vp
, __unused
const char *name
,
3390 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3391 __unused vfs_context_t context
)
3397 default_setxattr(__unused vnode_t vp
, __unused
const char *name
,
3398 __unused uio_t uio
, __unused
int options
, __unused vfs_context_t context
)
3404 default_listxattr(__unused vnode_t vp
,
3405 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3406 __unused vfs_context_t context
)
3412 default_removexattr(__unused vnode_t vp
, __unused
const char *name
,
3413 __unused
int options
, __unused vfs_context_t context
)
3418 #endif /* CONFIG_APPLEDOUBLE */