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 <kern/kalloc.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 strncmp(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 strncmp(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 strncmp(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
);
391 if (utf8_validatestr((const unsigned char *)name
, namelen
) != 0) {
400 * Determine whether an EA is a protected system attribute.
403 xattr_protected(const char *attrname
)
405 return !strncmp(attrname
, "com.apple.system.", 17);
410 vnode_setasnamedstream_internal(vnode_t vp
, vnode_t svp
)
412 uint32_t streamflags
= VISNAMEDSTREAM
;
414 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
415 streamflags
|= VISSHADOW
;
419 vnode_lock_spin(svp
);
420 svp
->v_flag
|= streamflags
;
423 /* Tag the parent so we know to flush credentials for streams on setattr */
425 vp
->v_lflag
|= VL_HASSTREAMS
;
428 /* Make the file it's parent.
429 * Note: This parent link helps us distinguish vnodes for
430 * shadow stream files from vnodes for resource fork on file
431 * systems that support namedstream natively (both have
432 * VISNAMEDSTREAM set) by allowing access to mount structure
433 * for checking MNTK_NAMED_STREAMS bit at many places in the
436 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_NAMEDSTREAM_PARENT
);
438 if (vnode_isdyldsharedcache(vp
)) {
439 vnode_lock_spin(svp
);
440 svp
->v_flag
|= VSHARED_DYLD
;
448 vnode_setasnamedstream(vnode_t vp
, vnode_t svp
)
450 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
454 vnode_setasnamedstream_internal(vp
, svp
);
461 * Obtain a named stream from vnode vp.
464 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
468 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) {
469 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
474 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
479 vnode_setasnamedstream_internal(vp
, *svpp
);
486 * Make a named stream for vnode vp.
489 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
493 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) {
494 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
496 error
= default_makenamedstream(vp
, svpp
, name
, context
);
500 vnode_setasnamedstream_internal(vp
, *svpp
);
507 * Remove a named stream from vnode vp.
510 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
514 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) {
515 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
517 error
= default_removenamedstream(vp
, name
, context
);
523 #define NS_IOBUFSIZE (128 * 1024)
526 * Release a named stream shadow file.
528 * Note: This function is called from two places where we do not need
529 * to check if the vnode has any references held before deleting the
530 * shadow file. Once from vclean() when the vnode is being reclaimed
531 * and we do not hold any references on the vnode. Second time from
532 * default_getnamedstream() when we get an error during shadow stream
533 * file initialization so that other processes who are waiting for the
534 * shadow stream file initialization by the creator will get opportunity
535 * to create and initialize the file again.
538 vnode_relenamedstream(vnode_t vp
, vnode_t svp
)
541 struct componentname cn
;
546 * We need to use the kernel context here. If we used the supplied
547 * VFS context we have no clue whether or not it originated from userland
548 * where it could be subject to a chroot jail. We need to ensure that all
549 * filesystem access to shadow files is done on the same FS regardless of
550 * userland process restrictions.
552 vfs_context_t kernelctx
= vfs_context_kernel();
557 MAKE_SHADOW_NAME(vp
, tmpname
);
560 cn
.cn_nameiop
= DELETE
;
561 cn
.cn_flags
= ISLASTCN
;
562 cn
.cn_context
= kernelctx
;
563 cn
.cn_pnbuf
= tmpname
;
564 cn
.cn_pnlen
= sizeof(tmpname
);
565 cn
.cn_nameptr
= cn
.cn_pnbuf
;
566 cn
.cn_namelen
= (int)strlen(tmpname
);
569 * Obtain the vnode for the shadow files directory. Make sure to
570 * use the kernel ctx as described above.
572 err
= get_shadow_dir(&dvp
);
577 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, kernelctx
);
584 * Flush a named stream shadow file.
586 * 'vp' represents the AppleDouble file.
587 * 'svp' represents the shadow file.
590 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
592 struct vnode_attr va
;
594 caddr_t bufptr
= NULL
;
601 * The kernel context must be used for all I/O to the shadow file
602 * and its namespace operations
604 vfs_context_t kernelctx
= vfs_context_kernel();
606 /* The supplied context is used for access to the AD file itself */
609 VATTR_WANTED(&va
, va_data_size
);
610 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
611 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
614 if (va
.va_data_size
> UINT32_MAX
) {
617 datasize
= (size_t)va
.va_data_size
;
619 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
623 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
624 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
627 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
631 * Copy the shadow stream file data into the resource fork.
633 error
= VNOP_OPEN(svp
, 0, kernelctx
);
635 printf("vnode_flushnamedstream: err %d opening file\n", error
);
638 while (offset
< datasize
) {
639 iosize
= MIN(datasize
- offset
, iosize
);
641 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
642 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
643 error
= VNOP_READ(svp
, auio
, 0, kernelctx
);
647 /* Since there's no truncate xattr we must remove the resource fork. */
649 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
650 if ((error
!= 0) && (error
!= ENOATTR
)) {
654 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
655 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
656 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
663 /* close shadowfile */
664 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
667 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
677 * Verify that the vnode 'vp' is a vnode that lives in the shadow
678 * directory. We can't just query the parent pointer directly since
679 * the shadowfile is hooked up to the actual file it's a stream for.
682 vnode_verifynamedstream(vnode_t vp
)
685 struct vnode
*shadow_dvp
= NULL
;
686 struct vnode
*shadowfile
= NULL
;
687 struct componentname cn
;
690 * We need to use the kernel context here. If we used the supplied
691 * VFS context we have no clue whether or not it originated from userland
692 * where it could be subject to a chroot jail. We need to ensure that all
693 * filesystem access to shadow files is done on the same FS regardless of
694 * userland process restrictions.
696 vfs_context_t kernelctx
= vfs_context_kernel();
700 /* Get the shadow directory vnode */
701 error
= get_shadow_dir(&shadow_dvp
);
706 /* Re-generate the shadow name in the buffer */
707 MAKE_SHADOW_NAME(vp
, tmpname
);
709 /* Look up item in shadow dir */
710 bzero(&cn
, sizeof(cn
));
711 cn
.cn_nameiop
= LOOKUP
;
712 cn
.cn_flags
= ISLASTCN
| CN_ALLOWRSRCFORK
;
713 cn
.cn_context
= kernelctx
;
714 cn
.cn_pnbuf
= tmpname
;
715 cn
.cn_pnlen
= sizeof(tmpname
);
716 cn
.cn_nameptr
= cn
.cn_pnbuf
;
717 cn
.cn_namelen
= (int)strlen(tmpname
);
719 if (VNOP_LOOKUP(shadow_dvp
, &shadowfile
, &cn
, kernelctx
) == 0) {
720 /* is the pointer the same? */
721 if (shadowfile
== vp
) {
726 /* drop the iocount acquired */
727 vnode_put(shadowfile
);
730 /* Drop iocount on shadow dir */
731 vnode_put(shadow_dvp
);
736 * Access or create the shadow file as needed.
738 * 'makestream' with non-zero value means that we need to guarantee we were the
739 * creator of the shadow file.
741 * 'context' is the user supplied context for the original VFS operation that
742 * caused us to need a shadow file.
744 * int pointed to by 'creator' is nonzero if we created the shadowfile.
747 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
748 int *creator
, vfs_context_t context
)
750 vnode_t dvp
= NULLVP
;
751 vnode_t svp
= NULLVP
;
752 struct componentname cn
;
753 struct vnode_attr va
;
758 vfs_context_t kernelctx
= vfs_context_kernel();
762 /* Establish a unique file name. */
763 MAKE_SHADOW_NAME(vp
, tmpname
);
764 bzero(&cn
, sizeof(cn
));
765 cn
.cn_nameiop
= LOOKUP
;
766 cn
.cn_flags
= ISLASTCN
;
767 cn
.cn_context
= context
;
768 cn
.cn_pnbuf
= tmpname
;
769 cn
.cn_pnlen
= sizeof(tmpname
);
770 cn
.cn_nameptr
= cn
.cn_pnbuf
;
771 cn
.cn_namelen
= (int)strlen(tmpname
);
773 /* Pick up uid, gid, mode and date from original file. */
775 VATTR_WANTED(&va
, va_uid
);
776 VATTR_WANTED(&va
, va_gid
);
777 VATTR_WANTED(&va
, va_mode
);
778 VATTR_WANTED(&va
, va_create_time
);
779 VATTR_WANTED(&va
, va_modify_time
);
780 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
781 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
782 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
783 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
784 va
.va_uid
= KAUTH_UID_NONE
;
785 va
.va_gid
= KAUTH_GID_NONE
;
786 va
.va_mode
= S_IRUSR
| S_IWUSR
;
788 va
.va_vaflags
= VA_EXCLUSIVE
;
789 VATTR_SET(&va
, va_type
, VREG
);
790 /* We no longer change the access, but we still hide it. */
791 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
793 /* Obtain the vnode for the shadow files directory. */
794 if (get_shadow_dir(&dvp
) != 0) {
799 /* See if someone else already has it open. */
800 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
) == 0) {
801 /* Double check existence by asking for size. */
803 VATTR_WANTED(&va
, va_data_size
);
804 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
805 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
806 goto out
; /* OK to use. */
811 * Otherwise make sure the resource fork data exists.
812 * Use the supplied context for accessing the AD file.
814 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
815 XATTR_NOSECURITY
, context
);
817 * To maintain binary compatibility with legacy Carbon
818 * emulated resource fork support, if the resource fork
819 * doesn't exist but the Finder Info does, then act as
820 * if an empty resource fork is present (see 4724359).
822 if ((error
== ENOATTR
) &&
823 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
824 XATTR_NOSECURITY
, context
) == 0)) {
832 /* If the resource fork exists, its size is expected to be non-zero. */
839 /* Create the shadow stream file. */
840 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, kernelctx
);
844 } else if ((error
== EEXIST
) && !makestream
) {
845 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
);
846 } else if ((error
== ENOENT
) && !makestream
) {
848 * We could have raced with a rmdir on the shadow directory
849 * post-lookup. Retry from the beginning, 1x only, to
850 * try and see if we need to re-create the shadow directory
865 /* Otherwise, just error out normally below */
873 /* On errors, clean up shadow stream file. */
881 *rsrcsize
= datasize
;
888 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
890 vnode_t svp
= NULLVP
;
892 caddr_t bufptr
= NULL
;
898 /* need the kernel context for accessing the shadowfile */
899 vfs_context_t kernelctx
= vfs_context_kernel();
902 * Only the "com.apple.ResourceFork" stream is supported here.
904 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
910 * Obtain a shadow file for the resource fork I/O.
912 * Need to pass along the supplied context so that getshadowfile
913 * can access the AD file as needed, using it.
915 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
922 * The creator of the shadow file provides its file data,
923 * all other threads should wait until its ready. In order to
924 * prevent a deadlock during error codepaths, we need to check if the
925 * vnode is being created, or if it has failed out. Regardless of success or
926 * failure, we set the VISSHADOW bit on the vnode, so we check that
927 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
928 * then we can infer the creator isn't done yet. If it's there, but
929 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
934 if (svp
->v_flag
& VISNAMEDSTREAM
) {
935 /* data is ready, go use it */
939 /* It's not ready, wait for it (sleep using v_parent as channel) */
940 if ((svp
->v_flag
& VISSHADOW
)) {
942 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
943 * thread is done with this vnode. Just unlock the vnode and try again
947 /* Otherwise, sleep if the shadow file is not created yet */
948 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
949 "getnamedstream", NULL
);
958 * Copy the real resource fork data into shadow stream file.
960 if (op
== NS_OPEN
&& datasize
!= 0) {
964 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
965 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
970 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
973 /* open the shadow file */
974 error
= VNOP_OPEN(svp
, 0, kernelctx
);
978 while (offset
< datasize
) {
981 iosize
= MIN(datasize
- offset
, iosize
);
983 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
984 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
985 /* use supplied ctx for AD file */
986 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
987 XATTR_NOSECURITY
, context
);
992 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
993 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
994 /* kernel context for writing shadowfile */
995 error
= VNOP_WRITE(svp
, auio
, 0, kernelctx
);
1002 /* close shadow file */
1003 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
1006 /* Wake up anyone waiting for svp file content */
1010 /* VISSHADOW would be set later on anyway, so we set it now */
1011 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
1012 wakeup((caddr_t
)&svp
->v_parent
);
1015 /* On post create errors, get rid of the shadow file. This
1016 * way if there is another process waiting for initialization
1017 * of the shadowfile by the current process will wake up and
1018 * retry by creating and initializing the shadow file again.
1019 * Also add the VISSHADOW bit here to indicate we're done operating
1022 (void)vnode_relenamedstream(vp
, svp
);
1024 svp
->v_flag
|= VISSHADOW
;
1025 wakeup((caddr_t
)&svp
->v_parent
);
1031 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
1037 /* On errors, clean up shadow stream file. */
1048 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
1054 * Only the "com.apple.ResourceFork" stream is supported here.
1056 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1061 /* Supply the context to getshadowfile so it can manipulate the AD file */
1062 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
1065 * Wake up any waiters over in default_getnamedstream().
1067 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
1068 vnode_t svp
= *svpp
;
1071 /* If we're the creator, mark it as a named stream */
1072 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
1073 /* Wakeup any waiters on the v_parent channel */
1074 wakeup((caddr_t
)&svp
->v_parent
);
1082 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
1085 * Only the "com.apple.ResourceFork" stream is supported here.
1087 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1091 * XXX - what about other opened instances?
1093 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
1097 get_shadow_dir(vnode_t
*sdvpp
)
1099 vnode_t dvp
= NULLVP
;
1100 vnode_t sdvp
= NULLVP
;
1101 struct componentname cn
;
1102 struct vnode_attr va
;
1106 vfs_context_t kernelctx
= vfs_context_kernel();
1108 bzero(tmpname
, sizeof(tmpname
));
1109 MAKE_SHADOW_DIRNAME(rootvnode
, tmpname
);
1111 * Look up the shadow directory to ensure that it still exists.
1112 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1113 * in caching it when multiple threads may be trying to manipulate the pointers.
1115 * Make sure to use the kernel context. We want a singular view of
1116 * the shadow dir regardless of chrooted processes.
1118 error
= vnode_lookup(tmpname
, 0, &sdvp
, kernelctx
);
1121 * If we get here, then we have successfully looked up the shadow dir,
1122 * and it has an iocount from the lookup. Return the vp in the output argument.
1127 /* In the failure case, no iocount is acquired */
1129 bzero(tmpname
, sizeof(tmpname
));
1132 * Obtain the vnode for "/var/run" directory using the kernel
1135 * This is defined in the SHADOW_DIR_CONTAINER macro
1137 if (vnode_lookup(SHADOW_DIR_CONTAINER
, 0, &dvp
, kernelctx
) != 0) {
1143 * Create the shadow stream directory.
1144 * 'dvp' below suggests the parent directory so
1145 * we only need to provide the leaf entry name
1147 MAKE_SHADOW_DIR_LEAF(rootvnode
, tmpname
);
1148 bzero(&cn
, sizeof(cn
));
1149 cn
.cn_nameiop
= LOOKUP
;
1150 cn
.cn_flags
= ISLASTCN
;
1151 cn
.cn_context
= kernelctx
;
1152 cn
.cn_pnbuf
= tmpname
;
1153 cn
.cn_pnlen
= sizeof(tmpname
);
1154 cn
.cn_nameptr
= cn
.cn_pnbuf
;
1155 cn
.cn_namelen
= (int)strlen(tmpname
);
1158 * owned by root, only readable by root, hidden
1161 VATTR_SET(&va
, va_uid
, 0);
1162 VATTR_SET(&va
, va_gid
, 0);
1163 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
1164 VATTR_SET(&va
, va_type
, VDIR
);
1165 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
1166 va
.va_vaflags
= VA_EXCLUSIVE
;
1168 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, kernelctx
);
1171 * There can be only one winner for an exclusive create.
1173 if (error
== EEXIST
) {
1174 /* loser has to look up directory */
1175 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, kernelctx
);
1177 /* Make sure its in fact a directory */
1178 if (sdvp
->v_type
!= VDIR
) {
1181 /* Obtain the fsid for /var/run directory */
1183 VATTR_WANTED(&va
, va_fsid
);
1184 if (VNOP_GETATTR(dvp
, &va
, kernelctx
) != 0 ||
1185 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1188 tmp_fsid
= va
.va_fsid
;
1191 VATTR_WANTED(&va
, va_uid
);
1192 VATTR_WANTED(&va
, va_gid
);
1193 VATTR_WANTED(&va
, va_mode
);
1194 VATTR_WANTED(&va
, va_fsid
);
1195 VATTR_WANTED(&va
, va_dirlinkcount
);
1196 VATTR_WANTED(&va
, va_acl
);
1197 /* Provide defaults for attrs that may not be supported */
1198 va
.va_dirlinkcount
= 1;
1199 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1201 if (VNOP_GETATTR(sdvp
, &va
, kernelctx
) != 0 ||
1202 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1203 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1204 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1205 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1209 * Make sure its what we want:
1211 * - not writable by anyone
1212 * - on same file system as /var/run
1213 * - not a hard-linked directory
1214 * - no ACLs (they might grant write access)
1216 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1217 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1218 (va
.va_fsid
!= tmp_fsid
) ||
1219 (va
.va_dirlinkcount
!= 1) ||
1220 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1230 /* On errors, clean up shadow stream directory. */
1240 /* This is not the dir we're looking for, move along */
1241 ++shadow_sequence
; /* try something else next time */
1245 #endif /* NAMEDSTREAMS */
1248 #if CONFIG_APPLEDOUBLE
1250 * Default Implementation (Non-native EA)
1255 * Typical "._" AppleDouble Header File layout:
1256 * ------------------------------------------------------------
1258 * VERSION 0x00020000
1261 * .-- AD ENTRY[0] Finder Info Entry (must be first)
1262 * .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1264 * | ///////////// Fixed Size Data (32 bytes)
1267 * | ATTR ENTRY[0] --.
1268 * | ATTR ENTRY[1] --+--.
1269 * | ATTR ENTRY[2] --+--+--.
1271 * | ATTR ENTRY[N] --+--+--+--.
1272 * | ATTR DATA 0 <-' | | |
1273 * | //////////// | | |
1274 * | ATTR DATA 1 <----' | |
1275 * | ///////////// | |
1276 * | ATTR DATA 2 <-------' |
1279 * | ATTR DATA N <----------'
1281 * | Attribute Free Space
1283 * '----> RESOURCE FORK
1284 * ///////////// Variable Sized Data
1293 * ------------------------------------------------------------
1295 * NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1296 * stored as part of the Finder Info. The length in the Finder
1297 * Info AppleDouble entry includes the length of the extended
1298 * attribute header, attribute entries, and attribute data.
1302 * On Disk Data Structures
1304 * Note: Motorola 68K alignment and big-endian.
1306 * See RFC 1740 for additional information about the AppleDouble file format.
1310 #define ADH_MAGIC 0x00051607
1311 #define ADH_VERSION 0x00020000
1312 #define ADH_MACOSX "Mac OS X "
1315 * AppleDouble Entry ID's
1317 #define AD_DATA 1 /* Data fork */
1318 #define AD_RESOURCE 2 /* Resource fork */
1319 #define AD_REALNAME 3 /* File's name on home file system */
1320 #define AD_COMMENT 4 /* Standard Mac comment */
1321 #define AD_ICONBW 5 /* Mac black & white icon */
1322 #define AD_ICONCOLOR 6 /* Mac color icon */
1323 #define AD_UNUSED 7 /* Not used */
1324 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1325 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1326 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1327 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1328 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1329 #define AD_AFPNAME 13 /* Short name on AFP server */
1330 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1331 #define AD_AFPDIRID 15 /* AFP directory ID */
1332 #define AD_ATTRIBUTES AD_FINDERINFO
1335 #define ATTR_FILE_PREFIX "._"
1336 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1338 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1340 /* Implementation Limits */
1341 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1342 #define ATTR_MAX_HDR_SIZE 65536
1344 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1345 * size supported (including the attribute entries). All of
1346 * the attribute entries must reside within this limit. If
1347 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1348 * boundry, then all of the attribute data I/O is performed
1349 * separately from the attribute header I/O.
1351 * In particular, all of the attr_entry structures must lie
1352 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1353 * AppleDouble file. However, the attribute data (i.e. the
1354 * contents of the extended attributes) may extend beyond the
1355 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1356 * limit is to allow the implementation to optimize by reading
1357 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1361 #define FINDERINFOSIZE 32
1363 typedef struct apple_double_entry
{
1364 u_int32_t type
; /* entry type: see list, 0 invalid */
1365 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1366 u_int32_t length
; /* entry data length in bytes. */
1367 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1370 typedef struct apple_double_header
{
1371 u_int32_t magic
; /* == ADH_MAGIC */
1372 u_int32_t version
; /* format version: 2 = 0x00020000 */
1373 u_int32_t filler
[4];
1374 u_int16_t numEntries
; /* number of entries which follow */
1375 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1376 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1377 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1378 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1380 #define ADHDRSIZE (4+4+16+2)
1382 /* Entries are aligned on 4 byte boundaries */
1383 typedef struct attr_entry
{
1384 u_int32_t offset
; /* file offset to data */
1385 u_int32_t length
; /* size of attribute data */
1388 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1389 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1392 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1393 typedef struct attr_header
{
1394 apple_double_header_t appledouble
;
1395 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1396 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1397 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1398 u_int32_t data_start
; /* file offset to attribute data area */
1399 u_int32_t data_length
; /* length of attribute data area */
1400 u_int32_t reserved
[3];
1402 u_int16_t num_attrs
;
1403 } __attribute__((aligned(2), packed
)) attr_header_t
;
1406 /* Empty Resource Fork Header */
1407 typedef struct rsrcfork_header
{
1408 u_int32_t fh_DataOffset
;
1409 u_int32_t fh_MapOffset
;
1410 u_int32_t fh_DataLength
;
1411 u_int32_t fh_MapLength
;
1412 u_int8_t systemData
[112];
1413 u_int8_t appData
[128];
1414 u_int32_t mh_DataOffset
;
1415 u_int32_t mh_MapOffset
;
1416 u_int32_t mh_DataLength
;
1417 u_int32_t mh_MapLength
;
1419 u_int16_t mh_RefNum
;
1421 u_int8_t mh_InMemoryAttr
;
1424 u_int16_t typeCount
;
1425 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1427 #define RF_FIRST_RESOURCE 256
1428 #define RF_NULL_MAP_LENGTH 30
1429 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1431 /* Runtime information about the attribute file. */
1432 typedef struct attr_info
{
1433 vfs_context_t context
;
1438 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1439 apple_double_header_t
*filehdr
;
1440 apple_double_entry_t
*finderinfo
;
1441 apple_double_entry_t
*rsrcfork
;
1442 attr_header_t
*attrhdr
;
1443 attr_entry_t
*attr_entry
;
1445 u_int8_t emptyfinderinfo
;
1449 #define ATTR_SETTING 1
1451 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1453 #define ATTR_ENTRY_LENGTH(namelen) \
1454 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1456 #define ATTR_NEXT(ae) \
1457 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1459 #define ATTR_VALID(ae, ai) \
1460 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1462 #define SWAP16(x) OSSwapBigToHostInt16((x))
1463 #define SWAP32(x) OSSwapBigToHostInt32((x))
1464 #define SWAP64(x) OSSwapBigToHostInt64((x))
1467 static u_int32_t emptyfinfo
[8] = {0};
1471 * Local support routines
1473 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1475 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1477 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1479 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1481 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1483 static void rel_xattrinfo(attr_info_t
*ainfop
);
1485 static int write_xattrinfo(attr_info_t
*ainfop
);
1487 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1489 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1491 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1494 #if BYTE_ORDER == LITTLE_ENDIAN
1495 static void swap_adhdr(apple_double_header_t
*adh
);
1496 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1499 #define swap_adhdr(x)
1500 #define swap_attrhdr(x, y)
1503 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1504 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1505 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1509 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1510 * is in big endian (as it would exist on disk). Verifies the following:
1513 * - number of entries
1514 * - that each entry fits within the file size
1516 * If the header is invalid, ENOATTR is returned.
1518 * NOTE: Does not attempt to validate the extended attributes header that
1519 * may be embedded in the Finder Info entry.
1522 check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1525 u_int32_t header_end
;
1526 u_int32_t entry_end
;
1528 apple_double_header_t
*header
;
1530 rawsize
= ainfop
->rawsize
;
1531 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1533 /* Is the file big enough to contain an AppleDouble header? */
1534 if (rawsize
< offsetof(apple_double_header_t
, entries
)) {
1538 /* Swap the AppleDouble header fields to native order */
1539 header
->magic
= SWAP32(header
->magic
);
1540 header
->version
= SWAP32(header
->version
);
1541 header
->numEntries
= SWAP16(header
->numEntries
);
1543 /* Sanity check the AppleDouble header fields */
1544 if (header
->magic
!= ADH_MAGIC
||
1545 header
->version
!= ADH_VERSION
||
1546 header
->numEntries
< 1 ||
1547 header
->numEntries
> 15) {
1551 /* Calculate where the entries[] array ends */
1552 header_end
= offsetof(apple_double_header_t
, entries
) +
1553 header
->numEntries
* sizeof(apple_double_entry_t
);
1555 /* Is the file big enough to contain the AppleDouble entries? */
1556 if (rawsize
< header_end
) {
1560 /* Swap and sanity check each AppleDouble entry */
1561 for (i
= 0; i
< header
->numEntries
; i
++) {
1562 /* Swap the per-entry fields to native order */
1563 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1564 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1565 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1567 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1570 * Does the entry's content start within the header itself,
1571 * did the addition overflow, or does the entry's content
1572 * extend past the end of the file?
1574 if (header
->entries
[i
].offset
< header_end
||
1575 entry_end
< header
->entries
[i
].offset
||
1576 entry_end
> ainfop
->filesize
) {
1581 * Does the current entry's content overlap with a previous
1584 * Yes, this is O(N**2), and there are more efficient algorithms
1585 * for testing pairwise overlap of N ranges when N is large.
1586 * But we have already ensured N < 16, and N is almost always 2.
1587 * So there's no point in using a more complex algorithm.
1590 for (j
= 0; j
< i
; j
++) {
1591 if (entry_end
> header
->entries
[j
].offset
&&
1592 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1604 * Retrieve the data of an extended attribute.
1607 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1608 __unused
int options
, vfs_context_t context
)
1612 attr_header_t
*header
;
1613 attr_entry_t
*entry
;
1623 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1626 * Open the file locked (shared) since the Carbon
1627 * File Manager may have the Apple Double file open
1628 * and could be changing the resource fork.
1630 fileflags
|= O_SHLOCK
;
1635 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1638 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1639 close_xattrfile(xvp
, fileflags
, context
);
1643 /* Get the Finder Info. */
1644 if (strncmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1645 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1647 } else if (uio
== NULL
) {
1648 *size
= FINDERINFOSIZE
;
1650 } else if (uio_offset(uio
) != 0) {
1652 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1655 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1656 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1661 /* Read the Resource Fork. */
1663 if (!vnode_isreg(vp
)) {
1665 } else if (ainfo
.rsrcfork
== NULL
) {
1667 } else if (uio
== NULL
) {
1668 *size
= (size_t)ainfo
.rsrcfork
->length
;
1670 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1671 error
= VNOP_READ(xvp
, uio
, 0, context
);
1673 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1679 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1683 if (uio_offset(uio
) != 0) {
1688 namelen
= strlen(name
) + 1;
1689 header
= ainfo
.attrhdr
;
1690 entry
= ainfo
.attr_entry
;
1692 * Search for attribute name in the header.
1694 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1695 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1696 datalen
= entry
->length
;
1702 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1706 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1707 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1708 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1710 uio_setoffset(uio
, entry
->offset
);
1711 error
= VNOP_READ(xvp
, uio
, 0, context
);
1712 uio_setoffset(uio
, 0);
1716 entry
= ATTR_NEXT(entry
);
1719 rel_xattrinfo(&ainfo
);
1720 close_xattrfile(xvp
, fileflags
, context
);
1726 * Set the data of an extended attribute.
1729 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1733 attr_header_t
*header
;
1734 attr_entry_t
*entry
;
1735 attr_entry_t
*lastentry
;
1739 size_t datafreespace
;
1746 char finfo
[FINDERINFOSIZE
];
1748 datalen
= uio_resid(uio
);
1749 if (datalen
> XATTR_MAXSIZE
) {
1752 namelen
= (int)strlen(name
) + 1;
1753 if (namelen
> UINT8_MAX
) {
1756 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1759 * By convention, Finder Info that is all zeroes is equivalent to not
1760 * having a Finder Info EA. So if we're trying to set the Finder Info
1761 * to all zeroes, then delete it instead. If a file didn't have an
1762 * AppleDouble file before, this prevents creating an AppleDouble file
1763 * with no useful content.
1765 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1766 * for all zeroes Finder Info before opening the AppleDouble file.
1767 * But if either of those options were specified, we need to open the
1768 * AppleDouble file to see whether there was already Finder Info (so we
1769 * can return an error if needed); this case is handled further below.
1771 * NOTE: this copies the Finder Info data into the "finfo" local.
1773 if (strncmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1775 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1776 * That means we probably have to open_xattrfile and get_xattrinfo.
1778 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1781 error
= uiomove(finfo
, (int)datalen
, uio
);
1785 if ((options
& (XATTR_CREATE
| XATTR_REPLACE
)) == 0 &&
1786 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1787 error
= default_removexattr(vp
, name
, 0, context
);
1788 if (error
== ENOATTR
) {
1797 * Open the file locked since setting an attribute
1798 * can change the layout of the Apple Double file.
1800 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1801 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1804 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1805 close_xattrfile(xvp
, fileflags
, context
);
1809 /* Set the Finder Info. */
1810 if (strncmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1811 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1812 /* attr exists and "create" was specified? */
1813 if (options
& XATTR_CREATE
) {
1818 /* attr doesn't exists and "replace" was specified? */
1819 if (options
& XATTR_REPLACE
) {
1824 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1826 * Setting the Finder Info to all zeroes is equivalent to
1827 * removing it. Close the xattr file and let
1828 * default_removexattr do the work (including deleting
1829 * the xattr file if there are no other xattrs).
1831 * Note that we have to handle the case where the
1832 * Finder Info was already all zeroes, and we ignore
1835 * The common case where options == 0 was handled above.
1837 rel_xattrinfo(&ainfo
);
1838 close_xattrfile(xvp
, fileflags
, context
);
1839 error
= default_removexattr(vp
, name
, 0, context
);
1840 if (error
== ENOATTR
) {
1845 if (ainfo
.finderinfo
) {
1846 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1847 bcopy(finfo
, attrdata
, datalen
);
1848 ainfo
.iosize
= sizeof(attr_header_t
);
1849 error
= write_xattrinfo(&ainfo
);
1856 /* Write the Resource Fork. */
1857 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1860 if (!vnode_isreg(vp
)) {
1864 /* Make sure we have a rsrc fork pointer.. */
1865 if (ainfo
.rsrcfork
== NULL
) {
1869 if (ainfo
.rsrcfork
) {
1870 if (ainfo
.rsrcfork
->length
!= 0) {
1871 if (options
& XATTR_CREATE
) {
1872 /* attr exists, and create specified ? */
1877 /* Zero length AD rsrc fork */
1878 if (options
& XATTR_REPLACE
) {
1879 /* attr doesn't exist (0-length), but replace specified ? */
1885 /* We can't do much if we somehow didn't get an AD rsrc pointer */
1890 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1891 if (endoffset
> UINT32_MAX
|| endoffset
< 0) {
1895 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1896 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1900 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1901 if (endoffset
> ainfo
.rsrcfork
->length
) {
1902 ainfo
.rsrcfork
->length
= (u_int32_t
)endoffset
;
1903 ainfo
.iosize
= sizeof(attr_header_t
);
1904 error
= write_xattrinfo(&ainfo
);
1910 if (datalen
> ATTR_MAX_SIZE
) {
1911 return E2BIG
; /* EINVAL instead ? */
1914 if (ainfo
.attrhdr
== NULL
) {
1918 header
= ainfo
.attrhdr
;
1919 entry
= ainfo
.attr_entry
;
1921 /* Check if data area crosses the maximum header size. */
1922 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
) {
1923 splitdata
= 1; /* do data I/O separately */
1929 * See if attribute already exists.
1931 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1932 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1936 entry
= ATTR_NEXT(entry
);
1940 if (options
& XATTR_CREATE
) {
1944 if (datalen
== entry
->length
) {
1946 uio_setoffset(uio
, entry
->offset
);
1947 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1948 uio_setoffset(uio
, 0);
1950 printf("setxattr: VNOP_WRITE error %d\n", error
);
1953 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1954 error
= uiomove((caddr_t
)attrdata
, (int)datalen
, uio
);
1958 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1959 error
= write_xattrinfo(&ainfo
);
1961 printf("setxattr: write_xattrinfo error %d\n", error
);
1967 * Brute force approach - just remove old entry and set new entry.
1970 rel_xattrinfo(&ainfo
);
1971 close_xattrfile(xvp
, fileflags
, context
);
1972 error
= default_removexattr(vp
, name
, options
, context
);
1976 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1977 options
&= ~XATTR_REPLACE
;
1978 goto start
; /* start over */
1982 if (options
& XATTR_REPLACE
) {
1983 error
= ENOATTR
; /* nothing there to replace */
1986 /* Check if header size limit has been reached. */
1987 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1992 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1994 /* Check if we need more space. */
1995 if ((datalen
+ entrylen
) > datafreespace
) {
1998 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
2000 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
2001 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
2002 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
2005 ainfo
.filesize
+= growsize
;
2006 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2008 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
2015 * Move the resource fork out of the way.
2017 if (ainfo
.rsrcfork
) {
2018 if (ainfo
.rsrcfork
->length
!= 0) {
2019 shift_data_down(xvp
,
2020 ainfo
.rsrcfork
->offset
,
2021 ainfo
.rsrcfork
->length
,
2024 ainfo
.rsrcfork
->offset
+= growsize
;
2026 ainfo
.finderinfo
->length
+= growsize
;
2027 header
->total_size
+= growsize
;
2030 /* Make space for a new entry. */
2032 shift_data_down(xvp
,
2034 header
->data_length
,
2037 bcopy((u_int8_t
*)header
+ header
->data_start
,
2038 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
2039 header
->data_length
);
2041 header
->data_start
+= entrylen
;
2043 /* Fix up entry data offsets. */
2045 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
2046 entry
->offset
+= entrylen
;
2050 * If the attribute data area is entirely within
2051 * the header buffer, then just update the buffer,
2052 * otherwise we'll write it separately to the file.
2057 /* Write new attribute data after the end of existing data. */
2058 offset
= header
->data_start
+ header
->data_length
;
2059 uio_setoffset(uio
, offset
);
2060 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
2061 uio_setoffset(uio
, 0);
2063 printf("setxattr: VNOP_WRITE error %d\n", error
);
2067 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
2069 error
= uiomove((caddr_t
)attrdata
, (int)datalen
, uio
);
2071 printf("setxattr: uiomove error %d\n", error
);
2076 /* Create the attribute entry. */
2077 lastentry
->length
= (u_int32_t
)datalen
;
2078 lastentry
->offset
= header
->data_start
+ header
->data_length
;
2079 lastentry
->namelen
= (u_int8_t
)namelen
;
2080 lastentry
->flags
= 0;
2081 bcopy(name
, &lastentry
->name
[0], namelen
);
2083 /* Update the attributes header. */
2084 header
->num_attrs
++;
2085 header
->data_length
+= datalen
;
2088 /* Only write the entries, since the data was written separately. */
2089 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
2091 /* The entry and data are both in the header; write them together. */
2092 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2094 error
= write_xattrinfo(&ainfo
);
2096 printf("setxattr: write_xattrinfo error %d\n", error
);
2100 rel_xattrinfo(&ainfo
);
2101 close_xattrfile(xvp
, fileflags
, context
);
2103 /* Touch the change time if we changed an attribute. */
2105 struct vnode_attr va
;
2107 /* Re-write the mtime to cause a ctime change. */
2109 VATTR_WANTED(&va
, va_modify_time
);
2110 if (vnode_getattr(vp
, &va
, context
) == 0) {
2112 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2113 (void) vnode_setattr(vp
, &va
, context
);
2117 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2124 * Remove an extended attribute.
2127 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
2131 attr_header_t
*header
;
2132 attr_entry_t
*entry
;
2133 attr_entry_t
*oldslot
;
2139 int found
= 0, lastone
= 0;
2147 fileflags
= FREAD
| FWRITE
;
2148 if (strncmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
2151 * Open the file locked (exclusive) since the Carbon
2152 * File Manager may have the Apple Double file open
2153 * and could be changing the resource fork.
2155 fileflags
|= O_EXLOCK
;
2160 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
2163 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2164 close_xattrfile(xvp
, fileflags
, context
);
2167 if (ainfo
.attrhdr
) {
2168 attrcount
+= ainfo
.attrhdr
->num_attrs
;
2170 if (ainfo
.rsrcfork
) {
2173 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2177 /* Clear the Finder Info. */
2178 if (strncmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
2179 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
2183 /* On removal of last attribute the ._ file is removed. */
2184 if (--attrcount
== 0) {
2187 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
2188 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
2189 ainfo
.iosize
= sizeof(attr_header_t
);
2190 error
= write_xattrinfo(&ainfo
);
2194 /* Clear the Resource Fork. */
2196 if (!vnode_isreg(vp
)) {
2200 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
2204 /* On removal of last attribute the ._ file is removed. */
2205 if (--attrcount
== 0) {
2210 * If the resource fork isn't the last AppleDouble
2211 * entry then the space needs to be reclaimed by
2212 * shifting the entries after the resource fork.
2214 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
2215 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
2216 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2219 ainfo
.rsrcfork
->length
= 0;
2220 ainfo
.iosize
= sizeof(attr_header_t
);
2221 error
= write_xattrinfo(&ainfo
);
2226 if (ainfo
.attrhdr
== NULL
) {
2230 namelen
= (int)strlen(name
) + 1;
2231 header
= ainfo
.attrhdr
;
2232 entry
= ainfo
.attr_entry
;
2235 * See if this attribute exists.
2237 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2238 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2240 if ((i
+ 1) == header
->num_attrs
) {
2245 entry
= ATTR_NEXT(entry
);
2251 /* On removal of last attribute the ._ file is removed. */
2252 if (--attrcount
== 0) {
2256 datalen
= entry
->length
;
2257 dataoff
= entry
->offset
;
2258 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2259 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
) {
2265 /* Remove the attribute entry. */
2267 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2268 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2271 /* Adjust the attribute data. */
2275 dataoff
- header
->data_start
,
2281 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2285 /* XXX write zeros to freed space ? */
2286 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2288 bcopy((u_int8_t
*)header
+ header
->data_start
,
2289 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2290 dataoff
- header
->data_start
);
2292 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2293 (u_int8_t
*)header
+ dataoff
- entrylen
,
2294 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2296 bzero(((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2297 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2300 /* Adjust the header values and entry offsets. */
2301 header
->num_attrs
--;
2302 header
->data_start
-= entrylen
;
2303 header
->data_length
-= datalen
;
2306 entry
= ainfo
.attr_entry
;
2307 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2308 entry
->offset
-= entrylen
;
2309 if (entry
>= oldslot
) {
2310 entry
->offset
-= datalen
;
2312 entry
= ATTR_NEXT(entry
);
2314 error
= write_xattrinfo(&ainfo
);
2316 printf("removexattr: write_xattrinfo error %d\n", error
);
2319 rel_xattrinfo(&ainfo
);
2321 /* When there are no more attributes remove the ._ file. */
2322 if (attrcount
== 0) {
2323 if (fileflags
& O_EXLOCK
) {
2324 (void) unlock_xattrfile(xvp
, context
);
2326 VNOP_CLOSE(xvp
, fileflags
, context
);
2328 error
= remove_xattrfile(xvp
, context
);
2331 close_xattrfile(xvp
, fileflags
, context
);
2333 /* Touch the change time if we changed an attribute. */
2335 struct vnode_attr va
;
2337 /* Re-write the mtime to cause a ctime change. */
2339 VATTR_WANTED(&va
, va_modify_time
);
2340 if (vnode_getattr(vp
, &va
, context
) == 0) {
2342 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2343 (void) vnode_setattr(vp
, &va
, context
);
2347 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2354 * Retrieve the list of extended attribute names.
2357 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2361 attr_entry_t
*entry
;
2366 * We do not zero "*size" here as we don't want to stomp a size set when
2367 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2368 * system call layer, up in listxattr or flistxattr.
2371 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2372 if (error
== ENOATTR
) {
2377 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2378 if (error
== ENOATTR
) {
2381 close_xattrfile(xvp
, FREAD
, context
);
2385 /* Check for Finder Info. */
2386 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2388 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2389 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2393 error
= uiomove(XATTR_FINDERINFO_NAME
,
2394 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2402 /* Check for Resource Fork. */
2403 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2405 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2406 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2410 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2411 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2419 /* Check for attributes. */
2420 if (ainfo
.attrhdr
) {
2421 count
= ainfo
.attrhdr
->num_attrs
;
2422 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2423 if (xattr_protected((const char *)entry
->name
) ||
2424 ((entry
->namelen
< XATTR_MAXNAMELEN
) &&
2425 (entry
->name
[entry
->namelen
] == '\0') &&
2426 (xattr_validatename((const char *)entry
->name
) != 0))) {
2427 entry
= ATTR_NEXT(entry
);
2431 *size
+= entry
->namelen
;
2432 entry
= ATTR_NEXT(entry
);
2435 if (uio_resid(uio
) < entry
->namelen
) {
2439 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2441 if (error
!= EFAULT
) {
2446 entry
= ATTR_NEXT(entry
);
2450 rel_xattrinfo(&ainfo
);
2451 close_xattrfile(xvp
, FREAD
, context
);
2457 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2459 vnode_t xvp
= NULLVP
;
2460 vnode_t dvp
= NULLVP
;
2461 struct vnode_attr
*va
= NULL
;
2462 struct nameidata
*nd
= NULL
;
2464 char *filename
= NULL
;
2465 const char *basename
= NULL
;
2471 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2473 * For the root directory use "._." to hold the attributes.
2475 filename
= &smallname
[0];
2476 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2477 dvp
= vp
; /* the "._." file resides in the root dir */
2480 if ((dvp
= vnode_getparent(vp
)) == NULLVP
) {
2484 if ((basename
= vnode_getname(vp
)) == NULL
) {
2489 /* "._" Attribute files cannot have attributes */
2490 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2491 basename
[0] == '.' && basename
[1] == '_') {
2495 filename
= &smallname
[0];
2496 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2497 if (len
>= sizeof(smallname
)) {
2498 len
++; /* snprintf result doesn't include '\0' */
2499 filename
= kheap_alloc(KHEAP_TEMP
, len
, Z_WAITOK
);
2500 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2503 * Note that the lookup here does not authorize. Since we are looking
2504 * up in the same directory that we already have the file vnode in,
2505 * we must have been given the file vnode legitimately. Read/write
2506 * access has already been authorized in layers above for calls from
2507 * userspace, and the authorization code using this path to read
2508 * file security from the EA must always get access
2511 nd
= kheap_alloc(KHEAP_TEMP
, sizeof(struct nameidata
), Z_WAITOK
);
2512 NDINIT(nd
, LOOKUP
, OP_OPEN
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
,
2513 UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), context
);
2516 va
= kheap_alloc(KHEAP_TEMP
, sizeof(struct vnode_attr
), Z_WAITOK
);
2518 if (fileflags
& O_CREAT
) {
2519 nd
->ni_cnd
.cn_nameiop
= CREATE
;
2521 nd
->ni_op
= OP_LINK
;
2524 nd
->ni_cnd
.cn_flags
|= LOCKPARENT
;
2526 if ((error
= namei(nd
))) {
2527 nd
->ni_dvp
= NULLVP
;
2531 if ((xvp
= nd
->ni_vp
) == NULLVP
) {
2537 * Pick up uid/gid/mode from target file.
2540 VATTR_WANTED(va
, va_uid
);
2541 VATTR_WANTED(va
, va_gid
);
2542 VATTR_WANTED(va
, va_mode
);
2543 if (VNOP_GETATTR(vp
, va
, context
) == 0 &&
2544 VATTR_IS_SUPPORTED(va
, va_uid
) &&
2545 VATTR_IS_SUPPORTED(va
, va_gid
) &&
2546 VATTR_IS_SUPPORTED(va
, va_mode
)) {
2549 umode
= va
->va_mode
& (S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
);
2550 } else { /* fallback values */
2551 uid
= KAUTH_UID_NONE
;
2552 gid
= KAUTH_GID_NONE
;
2553 umode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
2557 VATTR_SET(va
, va_type
, VREG
);
2558 VATTR_SET(va
, va_mode
, umode
);
2559 if (uid
!= KAUTH_UID_NONE
) {
2560 VATTR_SET(va
, va_uid
, uid
);
2562 if (gid
!= KAUTH_GID_NONE
) {
2563 VATTR_SET(va
, va_gid
, gid
);
2566 error
= vn_create(dvp
, &nd
->ni_vp
, nd
, va
,
2567 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2578 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2584 if ((error
= namei(nd
))) {
2585 nd
->ni_dvp
= NULLVP
;
2592 nd
->ni_dvp
= NULLVP
;
2594 if (xvp
->v_type
!= VREG
) {
2599 * Owners must match.
2602 VATTR_WANTED(va
, va_uid
);
2603 if (VNOP_GETATTR(vp
, va
, context
) == 0 && VATTR_IS_SUPPORTED(va
, va_uid
)) {
2604 uid_t owner
= va
->va_uid
;
2607 VATTR_WANTED(va
, va_uid
);
2608 if (VNOP_GETATTR(xvp
, va
, context
) == 0 && (owner
!= va
->va_uid
)) {
2609 error
= ENOATTR
; /* don't use this "._" file */
2614 if ((error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2620 if ((error
= vnode_ref(xvp
))) {
2625 /* If create was requested, make sure file header exists. */
2626 if (fileflags
& O_CREAT
) {
2628 VATTR_WANTED(va
, va_data_size
);
2629 VATTR_WANTED(va
, va_fileid
);
2630 VATTR_WANTED(va
, va_nlink
);
2631 if ((error
= vnode_getattr(xvp
, va
, context
)) != 0) {
2636 /* If the file is empty then add a default header. */
2637 if (va
->va_data_size
== 0) {
2638 /* Don't adopt hard-linked "._" files. */
2639 if (VATTR_IS_SUPPORTED(va
, va_nlink
) && va
->va_nlink
> 1) {
2643 if ((error
= create_xattrfile(xvp
, (u_int32_t
)va
->va_fileid
, context
))) {
2648 /* Apply file locking if requested. */
2649 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2652 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2653 error
= lock_xattrfile(xvp
, locktype
, context
);
2660 if (xvp
!= NULLVP
) {
2662 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2665 if (fileflags
& O_CREAT
) {
2666 /* Delete the xattr file if we encountered any errors */
2667 (void) remove_xattrfile(xvp
, context
);
2671 (void) vnode_rele(xvp
);
2673 (void) vnode_put(xvp
);
2676 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2680 /* Release resources after error-handling */
2681 kheap_free(KHEAP_TEMP
, nd
, sizeof(struct nameidata
));
2682 kheap_free(KHEAP_TEMP
, va
, sizeof(struct vnode_attr
));
2683 if (dvp
&& (dvp
!= vp
)) {
2687 vnode_putname(basename
);
2689 if (filename
&& filename
!= &smallname
[0]) {
2690 kheap_free(KHEAP_TEMP
, filename
, len
);
2693 *xvpp
= xvp
; /* return a referenced vnode */
2698 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2700 // if (fileflags & FWRITE)
2701 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2703 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2704 (void) unlock_xattrfile(xvp
, context
);
2707 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2708 (void) vnode_rele(xvp
);
2709 (void) vnode_put(xvp
);
2713 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2716 struct nameidata nd
;
2721 path
= zalloc(ZV_NAMEI
);
2722 pathlen
= MAXPATHLEN
;
2723 error
= vn_getpath(xvp
, path
, &pathlen
);
2725 zfree(ZV_NAMEI
, path
);
2729 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2730 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2732 zfree(ZV_NAMEI
, path
);
2739 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2748 * Read in and parse the AppleDouble header and entries, and the extended
2749 * attribute header and entries if any. Populates the fields of ainfop
2750 * based on the headers and entries found.
2752 * The basic idea is to:
2753 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2754 * AppleDouble entries, the extended attribute header, and extended
2755 * attribute entries must lie within this part of the file; the rest of
2756 * the AppleDouble handling code assumes this. Plus it allows us to
2757 * somewhat optimize by doing a smaller number of larger I/Os.
2758 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2760 * - Find the Finder Info and Resource Fork entries, if any.
2761 * - If we're going to be writing, try to make sure the Finder Info entry has
2762 * room to store the extended attribute header, plus some space for extended
2764 * - Swap and sanity check the extended attribute header and entries (if any).
2767 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2770 void * buffer
= NULL
;
2771 apple_double_header_t
*filehdr
;
2772 struct vnode_attr va
;
2777 bzero(ainfop
, sizeof(attr_info_t
));
2778 ainfop
->filevp
= xvp
;
2779 ainfop
->context
= context
;
2781 VATTR_WANTED(&va
, va_data_size
);
2782 VATTR_WANTED(&va
, va_fileid
);
2783 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2786 ainfop
->filesize
= va
.va_data_size
;
2788 /* When setting attributes, allow room for the header to grow. */
2790 iosize
= ATTR_MAX_HDR_SIZE
;
2792 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2799 ainfop
->iosize
= iosize
;
2800 buffer
= kheap_alloc(KHEAP_DATA_BUFFERS
, iosize
, Z_WAITOK
);
2801 if (buffer
== NULL
) {
2806 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2807 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2809 /* Read the file header. */
2810 error
= VNOP_READ(xvp
, auio
, 0, context
);
2814 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2815 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2817 filehdr
= (apple_double_header_t
*)buffer
;
2819 error
= check_and_swap_apple_double_header(ainfop
);
2824 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2826 /* rel_xattrinfo is responsible for freeing the header buffer */
2829 /* Find the Finder Info and Resource Fork entries, if any */
2830 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2831 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2832 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2833 /* We found the Finder Info entry. */
2834 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2836 /* At this point check_and_swap_apple_double_header() call above
2837 * verified that all apple double entires are valid:
2838 * they point somewhere within the file.
2840 * Now for finderinfo make sure that the fixed portion
2841 * is within the buffer we read in.
2843 if (((ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
) > ainfop
->finderinfo
->offset
) &&
2844 ((ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
) <= ainfop
->rawsize
)) {
2846 * Is the Finder Info "empty" (all zeroes)? If so,
2847 * we'll pretend like the Finder Info extended attribute
2850 if (bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2851 ainfop
->emptyfinderinfo
= 1;
2858 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2860 * Ignore zero-length resource forks when getting. If setting,
2861 * we need to remember the resource fork entry so it can be
2862 * updated once the new content has been written.
2864 if (filehdr
->entries
[i
].length
== 0 && !setting
) {
2869 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2871 * The "empty" resource headers we created have a system data tag of:
2872 * "This resource fork intentionally left blank "
2874 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2876 u_int8_t systemData
[64];
2880 /* Read the system data which starts at byte 16 */
2881 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2882 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2883 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2884 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2888 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2889 continue; /* skip this resource fork */
2892 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2893 if (i
!= (filehdr
->numEntries
- 1)) {
2894 printf("get_xattrinfo: resource fork not last entry\n");
2895 ainfop
->readonly
= 1;
2902 * See if this file looks like it is laid out correctly to contain
2903 * extended attributes. If so, then do the following:
2905 * - If we're going to be writing, try to make sure the Finder Info
2906 * entry has room to store the extended attribute header, plus some
2907 * space for extended attributes.
2909 * - Swap and sanity check the extended attribute header and entries
2912 if (filehdr
->numEntries
== 2 &&
2913 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2914 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2915 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2916 attr_header_t
*attrhdr
;
2917 attrhdr
= (attr_header_t
*)filehdr
;
2919 * If we're going to be writing, try to make sure the Finder
2920 * Info entry has room to store the extended attribute header,
2921 * plus some space for extended attributes.
2923 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2927 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2928 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2929 /* Make some room before existing resource fork. */
2930 shift_data_down(xvp
,
2931 filehdr
->entries
[1].offset
,
2932 filehdr
->entries
[1].length
,
2934 writesize
= sizeof(attr_header_t
);
2936 /* Create a new, empty resource fork. */
2937 rsrcfork_header_t
*rsrcforkhdr
;
2939 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2941 /* Steal some space for an empty RF header. */
2942 delta
-= sizeof(rsrcfork_header_t
);
2944 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2945 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2947 /* Fill in Empty Resource Fork Header. */
2948 init_empty_resource_fork(rsrcforkhdr
);
2950 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2951 writesize
= ATTR_BUF_SIZE
;
2953 filehdr
->entries
[0].length
+= delta
;
2954 filehdr
->entries
[1].offset
+= delta
;
2956 /* Fill in Attribute Header. */
2957 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2958 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2959 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2960 attrhdr
->data_start
= sizeof(attr_header_t
);
2961 attrhdr
->data_length
= 0;
2962 attrhdr
->reserved
[0] = 0;
2963 attrhdr
->reserved
[1] = 0;
2964 attrhdr
->reserved
[2] = 0;
2966 attrhdr
->num_attrs
= 0;
2968 /* Push out new header */
2969 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2970 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2972 swap_adhdr(filehdr
); /* to big endian */
2973 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2974 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2975 swap_adhdr(filehdr
); /* back to native */
2976 /* The attribute header gets swapped below. */
2980 * Swap and sanity check the extended attribute header and
2981 * entries (if any). The Finder Info content must be big enough
2982 * to include the extended attribute header; if not, we just
2985 * Note that we're passing the offset + length (i.e. the end)
2986 * of the Finder Info instead of rawsize to validate_attrhdr.
2987 * This ensures that all extended attributes lie within the
2988 * Finder Info content according to the AppleDouble entry.
2990 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2993 if (ainfop
->finderinfo
&&
2994 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2995 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2996 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2998 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2999 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
3000 /* First attr_entry starts immediately following attribute header */
3001 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
3010 kheap_free(KHEAP_DATA_BUFFERS
, buffer
, iosize
);
3016 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
3019 rsrcfork_header_t
*rsrcforkhdr
;
3025 buffer
= kheap_alloc(KHEAP_TEMP
, ATTR_BUF_SIZE
, Z_WAITOK
);
3026 bzero(buffer
, ATTR_BUF_SIZE
);
3028 xah
= (attr_header_t
*)buffer
;
3029 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3030 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
3031 rsrcforksize
= sizeof(rsrcfork_header_t
);
3032 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
3034 /* Fill in Apple Double Header. */
3035 xah
->appledouble
.magic
= SWAP32(ADH_MAGIC
);
3036 xah
->appledouble
.version
= SWAP32(ADH_VERSION
);
3037 xah
->appledouble
.numEntries
= SWAP16(2);
3038 xah
->appledouble
.entries
[0].type
= SWAP32(AD_FINDERINFO
);
3039 xah
->appledouble
.entries
[0].offset
= SWAP32(offsetof(apple_double_header_t
, finfo
));
3040 xah
->appledouble
.entries
[0].length
= SWAP32(ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
3041 xah
->appledouble
.entries
[1].type
= SWAP32(AD_RESOURCE
);
3042 xah
->appledouble
.entries
[1].offset
= SWAP32(ATTR_BUF_SIZE
- rsrcforksize
);
3043 xah
->appledouble
.entries
[1].length
= SWAP32(rsrcforksize
);
3044 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
3046 /* Fill in Attribute Header. */
3047 xah
->magic
= SWAP32(ATTR_HDR_MAGIC
);
3048 xah
->debug_tag
= SWAP32(fileid
);
3049 xah
->total_size
= SWAP32(ATTR_BUF_SIZE
- rsrcforksize
);
3050 xah
->data_start
= SWAP32(sizeof(attr_header_t
));
3052 /* Fill in Empty Resource Fork Header. */
3053 init_empty_resource_fork(rsrcforkhdr
);
3056 error
= VNOP_WRITE(xvp
, auio
, IO_UNIT
, context
);
3058 /* Did we write out the full uio? */
3059 if (uio_resid(auio
) > 0) {
3064 kheap_free(KHEAP_TEMP
, buffer
, ATTR_BUF_SIZE
);
3070 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
3072 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
3073 rsrcforkhdr
->fh_DataOffset
= SWAP32(RF_FIRST_RESOURCE
);
3074 rsrcforkhdr
->fh_MapOffset
= SWAP32(RF_FIRST_RESOURCE
);
3075 rsrcforkhdr
->fh_MapLength
= SWAP32(RF_NULL_MAP_LENGTH
);
3076 rsrcforkhdr
->mh_DataOffset
= SWAP32(RF_FIRST_RESOURCE
);
3077 rsrcforkhdr
->mh_MapOffset
= SWAP32(RF_FIRST_RESOURCE
);
3078 rsrcforkhdr
->mh_MapLength
= SWAP32(RF_NULL_MAP_LENGTH
);
3079 rsrcforkhdr
->mh_Types
= SWAP16(RF_NULL_MAP_LENGTH
- 2 );
3080 rsrcforkhdr
->mh_Names
= SWAP16(RF_NULL_MAP_LENGTH
);
3081 rsrcforkhdr
->typeCount
= SWAP16(-1);
3082 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
3086 rel_xattrinfo(attr_info_t
*ainfop
)
3088 kheap_free_addr(KHEAP_DATA_BUFFERS
, ainfop
->filehdr
);
3089 bzero(ainfop
, sizeof(attr_info_t
));
3093 write_xattrinfo(attr_info_t
*ainfop
)
3098 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3099 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
3101 swap_adhdr(ainfop
->filehdr
);
3102 if (ainfop
->attrhdr
!= NULL
) {
3103 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3106 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
3108 swap_adhdr(ainfop
->filehdr
);
3109 if (ainfop
->attrhdr
!= NULL
) {
3110 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3117 #if BYTE_ORDER == LITTLE_ENDIAN
3119 * Endian swap apple double header
3122 swap_adhdr(apple_double_header_t
*adh
)
3127 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
3129 adh
->magic
= SWAP32(adh
->magic
);
3130 adh
->version
= SWAP32(adh
->version
);
3131 adh
->numEntries
= SWAP16(adh
->numEntries
);
3133 for (i
= 0; i
< count
; i
++) {
3134 adh
->entries
[i
].type
= SWAP32(adh
->entries
[i
].type
);
3135 adh
->entries
[i
].offset
= SWAP32(adh
->entries
[i
].offset
);
3136 adh
->entries
[i
].length
= SWAP32(adh
->entries
[i
].length
);
3141 * Endian swap extended attributes header
3144 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
3150 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
3152 ah
->magic
= SWAP32(ah
->magic
);
3153 ah
->debug_tag
= SWAP32(ah
->debug_tag
);
3154 ah
->total_size
= SWAP32(ah
->total_size
);
3155 ah
->data_start
= SWAP32(ah
->data_start
);
3156 ah
->data_length
= SWAP32(ah
->data_length
);
3157 ah
->flags
= SWAP16(ah
->flags
);
3158 ah
->num_attrs
= SWAP16(ah
->num_attrs
);
3160 ae
= (attr_entry_t
*)(&ah
[1]);
3161 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
3162 ae
->offset
= SWAP32(ae
->offset
);
3163 ae
->length
= SWAP32(ae
->length
);
3164 ae
->flags
= SWAP16(ae
->flags
);
3170 * Validate and swap the attributes header contents, and each attribute's
3173 * Note: Assumes the caller has verified that the Finder Info content is large
3174 * enough to contain the attr_header structure itself. Therefore, we can
3175 * swap the header fields before sanity checking them.
3178 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
3190 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
) {
3194 /* Swap the basic header fields */
3195 ah
->magic
= SWAP32(ah
->magic
);
3196 ah
->debug_tag
= SWAP32(ah
->debug_tag
);
3197 ah
->total_size
= SWAP32(ah
->total_size
);
3198 ah
->data_start
= SWAP32(ah
->data_start
);
3199 ah
->data_length
= SWAP32(ah
->data_length
);
3200 ah
->flags
= SWAP16(ah
->flags
);
3201 ah
->num_attrs
= SWAP16(ah
->num_attrs
);
3204 * Make sure the total_size fits within the Finder Info area, and the
3205 * extended attribute data area fits within total_size.
3207 end
= ah
->data_start
+ ah
->data_length
;
3208 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3209 end
< ah
->data_start
||
3210 end
> ah
->total_size
) {
3215 * Make sure each of the attr_entry_t's fits within total_size.
3217 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3218 count
= ah
->num_attrs
;
3219 ae
= (attr_entry_t
*)(&ah
[1]);
3221 for (i
= 0; i
< count
; i
++) {
3222 /* Make sure the fixed-size part of this attr_entry_t fits. */
3223 if ((u_int8_t
*) &ae
[1] > buf_end
) {
3227 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3228 if (&ae
->name
[ae
->namelen
+ 1] > buf_end
) {
3232 /* Make sure that namelen is matching name's real length, namelen included NUL */
3233 if (strnlen((const char *)ae
->name
, ae
->namelen
) != ae
->namelen
- 1) {
3238 /* Swap the attribute entry fields */
3239 ae
->offset
= SWAP32(ae
->offset
);
3240 ae
->length
= SWAP32(ae
->length
);
3241 ae
->flags
= SWAP16(ae
->flags
);
3243 /* Make sure the attribute content fits and points to the data part */
3244 end
= ae
->offset
+ ae
->length
;
3245 if (end
< ae
->offset
|| end
> ah
->total_size
) {
3249 /* Make sure entry points to data section and not header */
3250 if (ae
->offset
< ah
->data_start
) {
3261 // "start" & "end" are byte offsets in the file.
3262 // "to" is the byte offset we want to move the
3263 // data to. "to" should be > "start".
3265 // we do the copy backwards to avoid problems if
3266 // there's an overlap.
3269 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3272 size_t chunk
, orig_chunk
;
3275 kauth_cred_t ucred
= vfs_context_ucred(context
);
3276 proc_t p
= vfs_context_proc(context
);
3278 if (delta
== 0 || len
== 0) {
3288 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3292 for (pos
= start
+ len
- chunk
; pos
>= start
; pos
-= chunk
) {
3293 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, (int)chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
| IO_NOAUTH
, ucred
, &iolen
, p
);
3295 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3296 pos
, ret
, chunk
, ret
);
3300 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, (int)chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
| IO_NOAUTH
, ucred
, &iolen
, p
);
3302 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3303 pos
+ delta
, ret
, chunk
, ret
);
3307 if ((pos
- (off_t
)chunk
) < start
) {
3308 chunk
= pos
- start
;
3310 if (chunk
== 0) { // we're all done
3315 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3322 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3325 size_t chunk
, orig_chunk
;
3329 kauth_cred_t ucred
= vfs_context_ucred(context
);
3330 proc_t p
= vfs_context_proc(context
);
3332 if (delta
== 0 || len
== 0) {
3343 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3347 for (pos
= start
; pos
< end
; pos
+= chunk
) {
3348 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, (int)chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
| IO_NOAUTH
, ucred
, &iolen
, p
);
3350 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3351 pos
, ret
, chunk
, ret
);
3355 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, (int)chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
| IO_NOAUTH
, ucred
, &iolen
, p
);
3357 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3358 pos
+ delta
, ret
, chunk
, ret
);
3362 if ((pos
+ (off_t
)chunk
) > end
) {
3365 if (chunk
== 0) { // we're all done
3370 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3376 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3381 lf
.l_whence
= SEEK_SET
;
3384 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3385 /* Note: id is just a kernel address that's not a proc */
3386 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
| F_WAIT
, context
, NULL
);
3387 return error
== ENOTSUP
? 0 : error
;
3391 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3396 lf
.l_whence
= SEEK_SET
;
3399 lf
.l_type
= F_UNLCK
;
3400 /* Note: id is just a kernel address that's not a proc */
3401 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
, NULL
);
3402 return error
== ENOTSUP
? 0 : error
;
3405 #else /* CONFIG_APPLEDOUBLE */
3409 default_getxattr(__unused vnode_t vp
, __unused
const char *name
,
3410 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3411 __unused vfs_context_t context
)
3417 default_setxattr(__unused vnode_t vp
, __unused
const char *name
,
3418 __unused uio_t uio
, __unused
int options
, __unused vfs_context_t context
)
3424 default_listxattr(__unused vnode_t vp
,
3425 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3426 __unused vfs_context_t context
)
3432 default_removexattr(__unused vnode_t vp
, __unused
const char *name
,
3433 __unused
int options
, __unused vfs_context_t context
)
3438 #endif /* CONFIG_APPLEDOUBLE */