2 * Copyright (c) 2004-2012 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
30 * support for mandatory and extensible security protections. This notice
31 * is included in support of clause 2.2 (b) of the Apple Public License,
35 #include <sys/param.h>
37 #include <sys/fcntl.h>
38 #include <sys/fsevents.h>
39 #include <sys/kernel.h>
40 #include <sys/kauth.h>
41 #include <sys/malloc.h>
42 #include <sys/mount_internal.h>
43 #include <sys/namei.h>
44 #include <sys/proc_internal.h>
47 #include <sys/utfconv.h>
48 #include <sys/vnode.h>
49 #include <sys/vnode_internal.h>
50 #include <sys/xattr.h>
52 #include <libkern/OSByteOrder.h>
53 #include <vm/vm_kern.h>
56 #include <security/mac_framework.h>
62 static int shadow_sequence
;
65 * We use %p to prevent loss of precision for pointers on varying architectures.
68 #define SHADOW_NAME_FMT ".vfs_rsrc_stream_%p%08x%p"
69 #define SHADOW_DIR_FMT ".vfs_rsrc_streams_%p%x"
70 #define SHADOW_DIR_CONTAINER "/var/run"
72 #define MAKE_SHADOW_NAME(VP, NAME) \
73 snprintf((NAME), sizeof((NAME)), (SHADOW_NAME_FMT), \
74 ((void*)(VM_KERNEL_ADDRPERM(VP))), \
76 ((void*)(VM_KERNEL_ADDRPERM((VP)->v_data))))
78 /* The full path to the shadow directory */
79 #define MAKE_SHADOW_DIRNAME(VP, NAME) \
80 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_CONTAINER "/" SHADOW_DIR_FMT), \
81 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
83 /* The shadow directory as a 'leaf' entry */
84 #define MAKE_SHADOW_DIR_LEAF(VP, NAME) \
85 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_FMT), \
86 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
88 static int default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
);
90 static int default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
);
92 static int default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
);
94 static int getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
, int *creator
, vfs_context_t context
);
96 static int get_shadow_dir(vnode_t
*sdvpp
);
98 #endif /* NAMEDSTREAMS */
101 * Default xattr support routines.
104 static int default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
, int options
,
105 vfs_context_t context
);
106 static int default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
,
107 vfs_context_t context
);
108 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
109 vfs_context_t context
);
110 static int default_removexattr(vnode_t vp
, const char *name
, int options
,
111 vfs_context_t context
);
114 * Retrieve the data of an extended attribute.
117 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
118 int options
, vfs_context_t context
)
122 if (!XATTR_VNODE_SUPPORTED(vp
)) {
126 /* getxattr calls are not allowed for streams. */
127 if (vp
->v_flag
& VISNAMEDSTREAM
) {
133 * Non-kernel request need extra checks performed.
135 * The XATTR_NOSECURITY flag implies a kernel request.
137 if (!(options
& XATTR_NOSECURITY
)) {
139 error
= mac_vnode_check_getextattr(context
, vp
, name
, uio
);
143 if ((error
= xattr_validatename(name
))) {
146 if ((error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
))) {
149 /* The offset can only be non-zero for resource forks. */
150 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
151 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
157 /* The offset can only be non-zero for resource forks. */
158 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
159 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
164 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
165 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
167 * A filesystem may keep some EAs natively and return ENOTSUP for others.
169 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
176 * Set the data of an extended attribute.
179 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
183 if (!XATTR_VNODE_SUPPORTED(vp
)) {
187 /* setxattr calls are not allowed for streams. */
188 if (vp
->v_flag
& VISNAMEDSTREAM
) {
193 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
196 if ((error
= xattr_validatename(name
))) {
199 if (!(options
& XATTR_NOSECURITY
)) {
201 error
= mac_vnode_check_setextattr(context
, vp
, name
, uio
);
205 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
209 /* The offset can only be non-zero for resource forks. */
210 if (uio_offset(uio
) != 0 &&
211 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0 ) {
216 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
219 * An EJUSTRETURN is from a filesystem which keeps this xattr
220 * natively as well as in a dot-underscore file. In this case the
221 * EJUSTRETURN means the filesytem has done nothing, but identifies the
222 * EA as one which may be represented natively and/or in a DU, and
223 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
224 * in vn_setxattr can we do the getxattrs needed to ascertain whether
225 * the XATTR_{CREATE,REPLACE} should yield an error.
227 if (error
== EJUSTRETURN
) {
228 int native
= 0, dufile
= 0;
229 size_t sz
; /* not used */
231 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
232 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
233 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
237 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
242 * Having determined no CREATE/REPLACE error should result, we
243 * zero those bits, so both backing stores get written to.
245 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
246 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
247 /* the mainline path here is to have error==ENOTSUP ... */
249 #endif /* DUAL_EAS */
250 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
252 * A filesystem may keep some EAs natively and return ENOTSUP for others.
254 error
= default_setxattr(vp
, name
, uio
, options
, context
);
257 if ((error
== 0) && !(options
& XATTR_NOSECURITY
)) {
258 mac_vnode_notify_setextattr(context
, vp
, name
, uio
);
259 if (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
)
260 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
268 * Remove an extended attribute.
271 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
275 if (!XATTR_VNODE_SUPPORTED(vp
)) {
279 /* removexattr calls are not allowed for streams. */
280 if (vp
->v_flag
& VISNAMEDSTREAM
) {
285 if ((error
= xattr_validatename(name
))) {
288 if (!(options
& XATTR_NOSECURITY
)) {
290 error
= mac_vnode_check_deleteextattr(context
, vp
, name
);
294 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
298 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
299 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
301 * A filesystem may keep some EAs natively and return ENOTSUP for others.
303 error
= default_removexattr(vp
, name
, options
, context
);
305 } else if (error
== EJUSTRETURN
) {
307 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
308 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
309 * a native xattr, so failure to find it in a DU file during
310 * default_removexattr should not be considered an error.
312 error
= default_removexattr(vp
, name
, options
, context
);
313 if (error
== ENOATTR
)
315 #endif /* DUAL_EAS */
318 if ((error
== 0) && !(options
& XATTR_NOSECURITY
)) {
319 mac_vnode_notify_deleteextattr(context
, vp
, name
);
320 if (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
)
321 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
329 * Retrieve the list of extended attribute names.
332 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
336 if (!XATTR_VNODE_SUPPORTED(vp
)) {
340 /* listxattr calls are not allowed for streams. */
341 if (vp
->v_flag
& VISNAMEDSTREAM
) {
346 if (!(options
& XATTR_NOSECURITY
)) {
348 error
= mac_vnode_check_listextattr(context
, vp
);
353 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
);
358 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
359 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
361 * A filesystem may keep some but not all EAs natively, in which case
362 * the native EA names will have been uiomove-d out (or *size updated)
363 * and the default_listxattr here will finish the job.
365 error
= default_listxattr(vp
, uio
, size
, options
, context
);
372 xattr_validatename(const char *name
)
376 if (name
== NULL
|| name
[0] == '\0') {
379 namelen
= strnlen(name
, XATTR_MAXNAMELEN
);
380 if (name
[namelen
] != '\0')
381 return (ENAMETOOLONG
);
383 if (utf8_validatestr((const unsigned char *)name
, namelen
) != 0)
391 * Determine whether an EA is a protected system attribute.
394 xattr_protected(const char *attrname
)
396 return(!strncmp(attrname
, "com.apple.system.", 17));
403 * Obtain a named stream from vnode vp.
406 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
410 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
411 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
413 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
416 uint32_t streamflags
= VISNAMEDSTREAM
;
419 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
420 streamflags
|= VISSHADOW
;
424 vnode_lock_spin(svp
);
425 svp
->v_flag
|= streamflags
;
428 /* Tag the parent so we know to flush credentials for streams on setattr */
430 vp
->v_lflag
|= VL_HASSTREAMS
;
433 /* Make the file it's parent.
434 * Note: This parent link helps us distinguish vnodes for
435 * shadow stream files from vnodes for resource fork on file
436 * systems that support namedstream natively (both have
437 * VISNAMEDSTREAM set) by allowing access to mount structure
438 * for checking MNTK_NAMED_STREAMS bit at many places in the
441 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
448 * Make a named stream for vnode vp.
451 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
455 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
456 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
458 error
= default_makenamedstream(vp
, svpp
, name
, context
);
461 uint32_t streamflags
= VISNAMEDSTREAM
;
465 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
466 streamflags
|= VISSHADOW
;
470 vnode_lock_spin(svp
);
471 svp
->v_flag
|= streamflags
;
474 /* Tag the parent so we know to flush credentials for streams on setattr */
476 vp
->v_lflag
|= VL_HASSTREAMS
;
479 /* Make the file it's parent.
480 * Note: This parent link helps us distinguish vnodes for
481 * shadow stream files from vnodes for resource fork on file
482 * systems that support namedstream natively (both have
483 * VISNAMEDSTREAM set) by allowing access to mount structure
484 * for checking MNTK_NAMED_STREAMS bit at many places in the
487 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
493 * Remove a named stream from vnode vp.
496 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
500 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
501 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
503 error
= default_removenamedstream(vp
, name
, context
);
508 #define NS_IOBUFSIZE (128 * 1024)
511 * Release a named stream shadow file.
513 * Note: This function is called from two places where we do not need
514 * to check if the vnode has any references held before deleting the
515 * shadow file. Once from vclean() when the vnode is being reclaimed
516 * and we do not hold any references on the vnode. Second time from
517 * default_getnamedstream() when we get an error during shadow stream
518 * file initialization so that other processes who are waiting for the
519 * shadow stream file initialization by the creator will get opportunity
520 * to create and initialize the file again.
523 vnode_relenamedstream(vnode_t vp
, vnode_t svp
) {
525 struct componentname cn
;
530 * We need to use the kernel context here. If we used the supplied
531 * VFS context we have no clue whether or not it originated from userland
532 * where it could be subject to a chroot jail. We need to ensure that all
533 * filesystem access to shadow files is done on the same FS regardless of
534 * userland process restrictions.
536 vfs_context_t kernelctx
= vfs_context_kernel();
541 MAKE_SHADOW_NAME(vp
, tmpname
);
544 cn
.cn_nameiop
= DELETE
;
545 cn
.cn_flags
= ISLASTCN
;
546 cn
.cn_context
= kernelctx
;
547 cn
.cn_pnbuf
= tmpname
;
548 cn
.cn_pnlen
= sizeof(tmpname
);
549 cn
.cn_nameptr
= cn
.cn_pnbuf
;
550 cn
.cn_namelen
= strlen(tmpname
);
553 * Obtain the vnode for the shadow files directory. Make sure to
554 * use the kernel ctx as described above.
556 err
= get_shadow_dir(&dvp
);
561 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, kernelctx
);
568 * Flush a named stream shadow file.
570 * 'vp' represents the AppleDouble file.
571 * 'svp' represents the shadow file.
574 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
576 struct vnode_attr va
;
578 caddr_t bufptr
= NULL
;
585 * The kernel context must be used for all I/O to the shadow file
586 * and its namespace operations
588 vfs_context_t kernelctx
= vfs_context_kernel();
590 /* The supplied context is used for access to the AD file itself */
593 VATTR_WANTED(&va
, va_data_size
);
594 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
595 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
598 datasize
= va
.va_data_size
;
600 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
604 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
605 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
608 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
612 * Copy the shadow stream file data into the resource fork.
614 error
= VNOP_OPEN(svp
, 0, kernelctx
);
616 printf("vnode_flushnamedstream: err %d opening file\n", error
);
619 while (offset
< datasize
) {
620 iosize
= MIN(datasize
- offset
, iosize
);
622 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
623 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
624 error
= VNOP_READ(svp
, auio
, 0, kernelctx
);
628 /* Since there's no truncate xattr we must remove the resource fork. */
630 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
631 if ((error
!= 0) && (error
!= ENOATTR
)) {
635 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
636 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
637 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
644 /* close shadowfile */
645 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
648 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
658 * Verify that the vnode 'vp' is a vnode that lives in the shadow
659 * directory. We can't just query the parent pointer directly since
660 * the shadowfile is hooked up to the actual file it's a stream for.
662 errno_t
vnode_verifynamedstream(vnode_t vp
) {
664 struct vnode
*shadow_dvp
= NULL
;
665 struct vnode
*shadowfile
= NULL
;
666 struct componentname cn
;
669 * We need to use the kernel context here. If we used the supplied
670 * VFS context we have no clue whether or not it originated from userland
671 * where it could be subject to a chroot jail. We need to ensure that all
672 * filesystem access to shadow files is done on the same FS regardless of
673 * userland process restrictions.
675 vfs_context_t kernelctx
= vfs_context_kernel();
679 /* Get the shadow directory vnode */
680 error
= get_shadow_dir(&shadow_dvp
);
685 /* Re-generate the shadow name in the buffer */
686 MAKE_SHADOW_NAME (vp
, tmpname
);
688 /* Look up item in shadow dir */
689 bzero(&cn
, sizeof(cn
));
690 cn
.cn_nameiop
= LOOKUP
;
691 cn
.cn_flags
= ISLASTCN
| CN_ALLOWRSRCFORK
;
692 cn
.cn_context
= kernelctx
;
693 cn
.cn_pnbuf
= tmpname
;
694 cn
.cn_pnlen
= sizeof(tmpname
);
695 cn
.cn_nameptr
= cn
.cn_pnbuf
;
696 cn
.cn_namelen
= strlen(tmpname
);
698 if (VNOP_LOOKUP (shadow_dvp
, &shadowfile
, &cn
, kernelctx
) == 0) {
699 /* is the pointer the same? */
700 if (shadowfile
== vp
) {
706 /* drop the iocount acquired */
707 vnode_put (shadowfile
);
710 /* Drop iocount on shadow dir */
711 vnode_put (shadow_dvp
);
716 * Access or create the shadow file as needed.
718 * 'makestream' with non-zero value means that we need to guarantee we were the
719 * creator of the shadow file.
721 * 'context' is the user supplied context for the original VFS operation that
722 * caused us to need a shadow file.
724 * int pointed to by 'creator' is nonzero if we created the shadowfile.
727 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
728 int *creator
, vfs_context_t context
)
730 vnode_t dvp
= NULLVP
;
731 vnode_t svp
= NULLVP
;
732 struct componentname cn
;
733 struct vnode_attr va
;
738 vfs_context_t kernelctx
= vfs_context_kernel();
742 /* Establish a unique file name. */
743 MAKE_SHADOW_NAME(vp
, tmpname
);
744 bzero(&cn
, sizeof(cn
));
745 cn
.cn_nameiop
= LOOKUP
;
746 cn
.cn_flags
= ISLASTCN
;
747 cn
.cn_context
= context
;
748 cn
.cn_pnbuf
= tmpname
;
749 cn
.cn_pnlen
= sizeof(tmpname
);
750 cn
.cn_nameptr
= cn
.cn_pnbuf
;
751 cn
.cn_namelen
= strlen(tmpname
);
753 /* Pick up uid, gid, mode and date from original file. */
755 VATTR_WANTED(&va
, va_uid
);
756 VATTR_WANTED(&va
, va_gid
);
757 VATTR_WANTED(&va
, va_mode
);
758 VATTR_WANTED(&va
, va_create_time
);
759 VATTR_WANTED(&va
, va_modify_time
);
760 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
761 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
762 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
763 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
764 va
.va_uid
= KAUTH_UID_NONE
;
765 va
.va_gid
= KAUTH_GID_NONE
;
766 va
.va_mode
= S_IRUSR
| S_IWUSR
;
768 va
.va_vaflags
= VA_EXCLUSIVE
;
769 VATTR_SET(&va
, va_type
, VREG
);
770 /* We no longer change the access, but we still hide it. */
771 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
773 /* Obtain the vnode for the shadow files directory. */
774 if (get_shadow_dir(&dvp
) != 0) {
779 /* See if someone else already has it open. */
780 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
) == 0) {
781 /* Double check existence by asking for size. */
783 VATTR_WANTED(&va
, va_data_size
);
784 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
785 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
786 goto out
; /* OK to use. */
791 * Otherwise make sure the resource fork data exists.
792 * Use the supplied context for accessing the AD file.
794 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
795 XATTR_NOSECURITY
, context
);
797 * To maintain binary compatibility with legacy Carbon
798 * emulated resource fork support, if the resource fork
799 * doesn't exist but the Finder Info does, then act as
800 * if an empty resource fork is present (see 4724359).
802 if ((error
== ENOATTR
) &&
803 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
804 XATTR_NOSECURITY
, context
) == 0)) {
812 /* If the resource fork exists, its size is expected to be non-zero. */
819 /* Create the shadow stream file. */
820 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, kernelctx
);
825 else if ((error
== EEXIST
) && !makestream
) {
826 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
);
828 else if ((error
== ENOENT
) && !makestream
) {
830 * We could have raced with a rmdir on the shadow directory
831 * post-lookup. Retry from the beginning, 1x only, to
832 * try and see if we need to re-create the shadow directory
847 /* Otherwise, just error out normally below */
855 /* On errors, clean up shadow stream file. */
863 *rsrcsize
= datasize
;
870 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
872 vnode_t svp
= NULLVP
;
874 caddr_t bufptr
= NULL
;
880 /* need the kernel context for accessing the shadowfile */
881 vfs_context_t kernelctx
= vfs_context_kernel();
884 * Only the "com.apple.ResourceFork" stream is supported here.
886 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
892 * Obtain a shadow file for the resource fork I/O.
894 * Need to pass along the supplied context so that getshadowfile
895 * can access the AD file as needed, using it.
897 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
904 * The creator of the shadow file provides its file data,
905 * all other threads should wait until its ready. In order to
906 * prevent a deadlock during error codepaths, we need to check if the
907 * vnode is being created, or if it has failed out. Regardless of success or
908 * failure, we set the VISSHADOW bit on the vnode, so we check that
909 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
910 * then we can infer the creator isn't done yet. If it's there, but
911 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
916 if (svp
->v_flag
& VISNAMEDSTREAM
) {
917 /* data is ready, go use it */
921 /* It's not ready, wait for it (sleep using v_parent as channel) */
922 if ((svp
->v_flag
& VISSHADOW
)) {
924 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
925 * thread is done with this vnode. Just unlock the vnode and try again
930 /* Otherwise, sleep if the shadow file is not created yet */
931 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
932 "getnamedstream", NULL
);
941 * Copy the real resource fork data into shadow stream file.
943 if (op
== NS_OPEN
&& datasize
!= 0) {
947 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
948 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
, VM_KERN_MEMORY_FILE
)) {
953 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
956 /* open the shadow file */
957 error
= VNOP_OPEN(svp
, 0, kernelctx
);
961 while (offset
< datasize
) {
964 iosize
= MIN(datasize
- offset
, iosize
);
966 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
967 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
968 /* use supplied ctx for AD file */
969 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
970 XATTR_NOSECURITY
, context
);
975 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
976 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
977 /* kernel context for writing shadowfile */
978 error
= VNOP_WRITE(svp
, auio
, 0, kernelctx
);
985 /* close shadow file */
986 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
989 /* Wake up anyone waiting for svp file content */
993 /* VISSHADOW would be set later on anyway, so we set it now */
994 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
995 wakeup((caddr_t
)&svp
->v_parent
);
998 /* On post create errors, get rid of the shadow file. This
999 * way if there is another process waiting for initialization
1000 * of the shadowfile by the current process will wake up and
1001 * retry by creating and initializing the shadow file again.
1002 * Also add the VISSHADOW bit here to indicate we're done operating
1005 (void)vnode_relenamedstream(vp
, svp
);
1007 svp
->v_flag
|= VISSHADOW
;
1008 wakeup((caddr_t
)&svp
->v_parent
);
1014 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
1020 /* On errors, clean up shadow stream file. */
1031 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
1037 * Only the "com.apple.ResourceFork" stream is supported here.
1039 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1044 /* Supply the context to getshadowfile so it can manipulate the AD file */
1045 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
1048 * Wake up any waiters over in default_getnamedstream().
1050 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
1051 vnode_t svp
= *svpp
;
1054 /* If we're the creator, mark it as a named stream */
1055 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
1056 /* Wakeup any waiters on the v_parent channel */
1057 wakeup((caddr_t
)&svp
->v_parent
);
1066 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
1069 * Only the "com.apple.ResourceFork" stream is supported here.
1071 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1075 * XXX - what about other opened instances?
1077 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
1081 get_shadow_dir(vnode_t
*sdvpp
) {
1082 vnode_t dvp
= NULLVP
;
1083 vnode_t sdvp
= NULLVP
;
1084 struct componentname cn
;
1085 struct vnode_attr va
;
1089 vfs_context_t kernelctx
= vfs_context_kernel();
1091 bzero(tmpname
, sizeof(tmpname
));
1092 MAKE_SHADOW_DIRNAME(rootvnode
, tmpname
);
1094 * Look up the shadow directory to ensure that it still exists.
1095 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1096 * in caching it when multiple threads may be trying to manipulate the pointers.
1098 * Make sure to use the kernel context. We want a singular view of
1099 * the shadow dir regardless of chrooted processes.
1101 error
= vnode_lookup(tmpname
, 0, &sdvp
, kernelctx
);
1104 * If we get here, then we have successfully looked up the shadow dir,
1105 * and it has an iocount from the lookup. Return the vp in the output argument.
1110 /* In the failure case, no iocount is acquired */
1112 bzero (tmpname
, sizeof(tmpname
));
1115 * Obtain the vnode for "/var/run" directory using the kernel
1118 * This is defined in the SHADOW_DIR_CONTAINER macro
1120 if (vnode_lookup(SHADOW_DIR_CONTAINER
, 0, &dvp
, kernelctx
) != 0) {
1126 * Create the shadow stream directory.
1127 * 'dvp' below suggests the parent directory so
1128 * we only need to provide the leaf entry name
1130 MAKE_SHADOW_DIR_LEAF(rootvnode
, tmpname
);
1131 bzero(&cn
, sizeof(cn
));
1132 cn
.cn_nameiop
= LOOKUP
;
1133 cn
.cn_flags
= ISLASTCN
;
1134 cn
.cn_context
= kernelctx
;
1135 cn
.cn_pnbuf
= tmpname
;
1136 cn
.cn_pnlen
= sizeof(tmpname
);
1137 cn
.cn_nameptr
= cn
.cn_pnbuf
;
1138 cn
.cn_namelen
= strlen(tmpname
);
1141 * owned by root, only readable by root, hidden
1144 VATTR_SET(&va
, va_uid
, 0);
1145 VATTR_SET(&va
, va_gid
, 0);
1146 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
1147 VATTR_SET(&va
, va_type
, VDIR
);
1148 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
1149 va
.va_vaflags
= VA_EXCLUSIVE
;
1151 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, kernelctx
);
1154 * There can be only one winner for an exclusive create.
1156 if (error
== EEXIST
) {
1157 /* loser has to look up directory */
1158 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, kernelctx
);
1160 /* Make sure its in fact a directory */
1161 if (sdvp
->v_type
!= VDIR
) {
1164 /* Obtain the fsid for /var/run directory */
1166 VATTR_WANTED(&va
, va_fsid
);
1167 if (VNOP_GETATTR(dvp
, &va
, kernelctx
) != 0 ||
1168 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1171 tmp_fsid
= va
.va_fsid
;
1174 VATTR_WANTED(&va
, va_uid
);
1175 VATTR_WANTED(&va
, va_gid
);
1176 VATTR_WANTED(&va
, va_mode
);
1177 VATTR_WANTED(&va
, va_fsid
);
1178 VATTR_WANTED(&va
, va_dirlinkcount
);
1179 VATTR_WANTED(&va
, va_acl
);
1180 /* Provide defaults for attrs that may not be supported */
1181 va
.va_dirlinkcount
= 1;
1182 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1184 if (VNOP_GETATTR(sdvp
, &va
, kernelctx
) != 0 ||
1185 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1186 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1187 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1188 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1192 * Make sure its what we want:
1194 * - not writable by anyone
1195 * - on same file system as /var/run
1196 * - not a hard-linked directory
1197 * - no ACLs (they might grant write access)
1199 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1200 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1201 (va
.va_fsid
!= tmp_fsid
) ||
1202 (va
.va_dirlinkcount
!= 1) ||
1203 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1213 /* On errors, clean up shadow stream directory. */
1223 /* This is not the dir we're looking for, move along */
1224 ++shadow_sequence
; /* try something else next time */
1228 #endif /* NAMEDSTREAMS */
1231 #if CONFIG_APPLEDOUBLE
1233 * Default Implementation (Non-native EA)
1238 Typical "._" AppleDouble Header File layout:
1239 ------------------------------------------------------------
1244 .-- AD ENTRY[0] Finder Info Entry (must be first)
1245 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1247 | ///////////// Fixed Size Data (32 bytes)
1251 | ATTR ENTRY[1] --+--.
1252 | ATTR ENTRY[2] --+--+--.
1254 | ATTR ENTRY[N] --+--+--+--.
1255 | ATTR DATA 0 <-' | | |
1256 | //////////// | | |
1257 | ATTR DATA 1 <----' | |
1259 | ATTR DATA 2 <-------' |
1262 | ATTR DATA N <----------'
1264 | Attribute Free Space
1266 '----> RESOURCE FORK
1267 ///////////// Variable Sized Data
1276 ------------------------------------------------------------
1278 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1279 stored as part of the Finder Info. The length in the Finder
1280 Info AppleDouble entry includes the length of the extended
1281 attribute header, attribute entries, and attribute data.
1286 * On Disk Data Structures
1288 * Note: Motorola 68K alignment and big-endian.
1290 * See RFC 1740 for additional information about the AppleDouble file format.
1294 #define ADH_MAGIC 0x00051607
1295 #define ADH_VERSION 0x00020000
1296 #define ADH_MACOSX "Mac OS X "
1299 * AppleDouble Entry ID's
1301 #define AD_DATA 1 /* Data fork */
1302 #define AD_RESOURCE 2 /* Resource fork */
1303 #define AD_REALNAME 3 /* FileÕs name on home file system */
1304 #define AD_COMMENT 4 /* Standard Mac comment */
1305 #define AD_ICONBW 5 /* Mac black & white icon */
1306 #define AD_ICONCOLOR 6 /* Mac color icon */
1307 #define AD_UNUSED 7 /* Not used */
1308 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1309 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1310 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1311 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1312 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1313 #define AD_AFPNAME 13 /* Short name on AFP server */
1314 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1315 #define AD_AFPDIRID 15 /* AFP directory ID */
1316 #define AD_ATTRIBUTES AD_FINDERINFO
1319 #define ATTR_FILE_PREFIX "._"
1320 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1322 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1324 /* Implementation Limits */
1325 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1326 #define ATTR_MAX_HDR_SIZE 65536
1328 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1329 * size supported (including the attribute entries). All of
1330 * the attribute entries must reside within this limit. If
1331 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1332 * boundry, then all of the attribute data I/O is performed
1333 * separately from the attribute header I/O.
1335 * In particular, all of the attr_entry structures must lie
1336 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1337 * AppleDouble file. However, the attribute data (i.e. the
1338 * contents of the extended attributes) may extend beyond the
1339 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1340 * limit is to allow the implementation to optimize by reading
1341 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1345 #define FINDERINFOSIZE 32
1347 typedef struct apple_double_entry
{
1348 u_int32_t type
; /* entry type: see list, 0 invalid */
1349 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1350 u_int32_t length
; /* entry data length in bytes. */
1351 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1354 typedef struct apple_double_header
{
1355 u_int32_t magic
; /* == ADH_MAGIC */
1356 u_int32_t version
; /* format version: 2 = 0x00020000 */
1357 u_int32_t filler
[4];
1358 u_int16_t numEntries
; /* number of entries which follow */
1359 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1360 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1361 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1362 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1364 #define ADHDRSIZE (4+4+16+2)
1366 /* Entries are aligned on 4 byte boundaries */
1367 typedef struct attr_entry
{
1368 u_int32_t offset
; /* file offset to data */
1369 u_int32_t length
; /* size of attribute data */
1372 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1373 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1376 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1377 typedef struct attr_header
{
1378 apple_double_header_t appledouble
;
1379 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1380 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1381 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1382 u_int32_t data_start
; /* file offset to attribute data area */
1383 u_int32_t data_length
; /* length of attribute data area */
1384 u_int32_t reserved
[3];
1386 u_int16_t num_attrs
;
1387 } __attribute__((aligned(2), packed
)) attr_header_t
;
1390 /* Empty Resource Fork Header */
1391 typedef struct rsrcfork_header
{
1392 u_int32_t fh_DataOffset
;
1393 u_int32_t fh_MapOffset
;
1394 u_int32_t fh_DataLength
;
1395 u_int32_t fh_MapLength
;
1396 u_int8_t systemData
[112];
1397 u_int8_t appData
[128];
1398 u_int32_t mh_DataOffset
;
1399 u_int32_t mh_MapOffset
;
1400 u_int32_t mh_DataLength
;
1401 u_int32_t mh_MapLength
;
1403 u_int16_t mh_RefNum
;
1405 u_int8_t mh_InMemoryAttr
;
1408 u_int16_t typeCount
;
1409 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1411 #define RF_FIRST_RESOURCE 256
1412 #define RF_NULL_MAP_LENGTH 30
1413 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1415 /* Runtime information about the attribute file. */
1416 typedef struct attr_info
{
1417 vfs_context_t context
;
1422 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1423 apple_double_header_t
*filehdr
;
1424 apple_double_entry_t
*finderinfo
;
1425 apple_double_entry_t
*rsrcfork
;
1426 attr_header_t
*attrhdr
;
1427 attr_entry_t
*attr_entry
;
1429 u_int8_t emptyfinderinfo
;
1433 #define ATTR_SETTING 1
1435 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1437 #define ATTR_ENTRY_LENGTH(namelen) \
1438 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1440 #define ATTR_NEXT(ae) \
1441 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1443 #define ATTR_VALID(ae, ai) \
1444 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1446 #define SWAP16(x) OSSwapBigToHostInt16((x))
1447 #define SWAP32(x) OSSwapBigToHostInt32((x))
1448 #define SWAP64(x) OSSwapBigToHostInt64((x))
1451 static u_int32_t emptyfinfo
[8] = {0};
1455 * Local support routines
1457 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1459 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1461 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1463 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1465 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1467 static void rel_xattrinfo(attr_info_t
*ainfop
);
1469 static int write_xattrinfo(attr_info_t
*ainfop
);
1471 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1473 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1475 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1478 #if BYTE_ORDER == LITTLE_ENDIAN
1479 static void swap_adhdr(apple_double_header_t
*adh
);
1480 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1483 #define swap_adhdr(x)
1484 #define swap_attrhdr(x, y)
1487 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1488 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1489 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1493 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1494 * is in big endian (as it would exist on disk). Verifies the following:
1497 * - number of entries
1498 * - that each entry fits within the file size
1500 * If the header is invalid, ENOATTR is returned.
1502 * NOTE: Does not attempt to validate the extended attributes header that
1503 * may be embedded in the Finder Info entry.
1505 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1508 u_int32_t header_end
;
1509 u_int32_t entry_end
;
1511 apple_double_header_t
*header
;
1513 rawsize
= ainfop
->rawsize
;
1514 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1516 /* Is the file big enough to contain an AppleDouble header? */
1517 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1520 /* Swap the AppleDouble header fields to native order */
1521 header
->magic
= SWAP32(header
->magic
);
1522 header
->version
= SWAP32(header
->version
);
1523 header
->numEntries
= SWAP16(header
->numEntries
);
1525 /* Sanity check the AppleDouble header fields */
1526 if (header
->magic
!= ADH_MAGIC
||
1527 header
->version
!= ADH_VERSION
||
1528 header
->numEntries
< 1 ||
1529 header
->numEntries
> 15) {
1533 /* Calculate where the entries[] array ends */
1534 header_end
= offsetof(apple_double_header_t
, entries
) +
1535 header
->numEntries
* sizeof(apple_double_entry_t
);
1537 /* Is the file big enough to contain the AppleDouble entries? */
1538 if (rawsize
< header_end
) {
1542 /* Swap and sanity check each AppleDouble entry */
1543 for (i
=0; i
<header
->numEntries
; i
++) {
1544 /* Swap the per-entry fields to native order */
1545 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1546 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1547 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1549 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1552 * Does the entry's content start within the header itself,
1553 * did the addition overflow, or does the entry's content
1554 * extend past the end of the file?
1556 if (header
->entries
[i
].offset
< header_end
||
1557 entry_end
< header
->entries
[i
].offset
||
1558 entry_end
> ainfop
->filesize
) {
1563 * Does the current entry's content overlap with a previous
1566 * Yes, this is O(N**2), and there are more efficient algorithms
1567 * for testing pairwise overlap of N ranges when N is large.
1568 * But we have already ensured N < 16, and N is almost always 2.
1569 * So there's no point in using a more complex algorithm.
1572 for (j
=0; j
<i
; j
++) {
1573 if (entry_end
> header
->entries
[j
].offset
&&
1574 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1586 * Retrieve the data of an extended attribute.
1589 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1590 __unused
int options
, vfs_context_t context
)
1594 attr_header_t
*header
;
1595 attr_entry_t
*entry
;
1605 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1608 * Open the file locked (shared) since the Carbon
1609 * File Manager may have the Apple Double file open
1610 * and could be changing the resource fork.
1612 fileflags
|= O_SHLOCK
;
1617 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1620 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1621 close_xattrfile(xvp
, fileflags
, context
);
1625 /* Get the Finder Info. */
1626 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1628 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1630 } else if (uio
== NULL
) {
1631 *size
= FINDERINFOSIZE
;
1633 } else if (uio_offset(uio
) != 0) {
1635 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1638 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1639 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1644 /* Read the Resource Fork. */
1646 if (!vnode_isreg(vp
)) {
1648 } else if (ainfo
.rsrcfork
== NULL
) {
1650 } else if (uio
== NULL
) {
1651 *size
= (size_t)ainfo
.rsrcfork
->length
;
1653 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1654 error
= VNOP_READ(xvp
, uio
, 0, context
);
1656 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1661 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1665 if (uio_offset(uio
) != 0) {
1670 namelen
= strlen(name
) + 1;
1671 header
= ainfo
.attrhdr
;
1672 entry
= ainfo
.attr_entry
;
1674 * Search for attribute name in the header.
1676 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1677 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1678 datalen
= (size_t)entry
->length
;
1684 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1688 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1689 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1690 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1692 uio_setoffset(uio
, entry
->offset
);
1693 error
= VNOP_READ(xvp
, uio
, 0, context
);
1694 uio_setoffset(uio
, 0);
1698 entry
= ATTR_NEXT(entry
);
1701 rel_xattrinfo(&ainfo
);
1702 close_xattrfile(xvp
, fileflags
, context
);
1708 * Set the data of an extended attribute.
1711 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1715 attr_header_t
*header
;
1716 attr_entry_t
*entry
;
1717 attr_entry_t
*lastentry
;
1721 size_t datafreespace
;
1728 char finfo
[FINDERINFOSIZE
];
1730 datalen
= uio_resid(uio
);
1731 namelen
= strlen(name
) + 1;
1732 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1735 * By convention, Finder Info that is all zeroes is equivalent to not
1736 * having a Finder Info EA. So if we're trying to set the Finder Info
1737 * to all zeroes, then delete it instead. If a file didn't have an
1738 * AppleDouble file before, this prevents creating an AppleDouble file
1739 * with no useful content.
1741 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1742 * for all zeroes Finder Info before opening the AppleDouble file.
1743 * But if either of those options were specified, we need to open the
1744 * AppleDouble file to see whether there was already Finder Info (so we
1745 * can return an error if needed); this case is handled further below.
1747 * NOTE: this copies the Finder Info data into the "finfo" local.
1749 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1751 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1752 * That means we probably have to open_xattrfile and get_xattrinfo.
1754 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1757 error
= uiomove(finfo
, datalen
, uio
);
1760 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1761 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1762 error
= default_removexattr(vp
, name
, 0, context
);
1763 if (error
== ENOATTR
)
1771 * Open the file locked since setting an attribute
1772 * can change the layout of the Apple Double file.
1774 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1775 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1778 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1779 close_xattrfile(xvp
, fileflags
, context
);
1783 /* Set the Finder Info. */
1784 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1785 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1786 /* attr exists and "create" was specified? */
1787 if (options
& XATTR_CREATE
) {
1792 /* attr doesn't exists and "replace" was specified? */
1793 if (options
& XATTR_REPLACE
) {
1798 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1800 * Setting the Finder Info to all zeroes is equivalent to
1801 * removing it. Close the xattr file and let
1802 * default_removexattr do the work (including deleting
1803 * the xattr file if there are no other xattrs).
1805 * Note that we have to handle the case where the
1806 * Finder Info was already all zeroes, and we ignore
1809 * The common case where options == 0 was handled above.
1811 rel_xattrinfo(&ainfo
);
1812 close_xattrfile(xvp
, fileflags
, context
);
1813 error
= default_removexattr(vp
, name
, 0, context
);
1814 if (error
== ENOATTR
)
1818 if (ainfo
.finderinfo
) {
1819 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1820 bcopy(finfo
, attrdata
, datalen
);
1821 ainfo
.iosize
= sizeof(attr_header_t
);
1822 error
= write_xattrinfo(&ainfo
);
1829 /* Write the Resource Fork. */
1830 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1831 u_int32_t endoffset
;
1833 if (!vnode_isreg(vp
)) {
1837 /* Make sure we have a rsrc fork pointer.. */
1838 if (ainfo
.rsrcfork
== NULL
) {
1842 if (ainfo
.rsrcfork
) {
1843 if (ainfo
.rsrcfork
->length
!= 0) {
1844 if (options
& XATTR_CREATE
) {
1845 /* attr exists, and create specified ? */
1851 /* Zero length AD rsrc fork */
1852 if (options
& XATTR_REPLACE
) {
1853 /* attr doesn't exist (0-length), but replace specified ? */
1860 /* We can't do much if we somehow didn't get an AD rsrc pointer */
1865 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1866 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1867 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1870 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1871 if (endoffset
> ainfo
.rsrcfork
->length
) {
1872 ainfo
.rsrcfork
->length
= endoffset
;
1873 ainfo
.iosize
= sizeof(attr_header_t
);
1874 error
= write_xattrinfo(&ainfo
);
1880 if (datalen
> ATTR_MAX_SIZE
) {
1881 return (E2BIG
); /* EINVAL instead ? */
1884 if (ainfo
.attrhdr
== NULL
) {
1888 header
= ainfo
.attrhdr
;
1889 entry
= ainfo
.attr_entry
;
1891 /* Check if data area crosses the maximum header size. */
1892 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1893 splitdata
= 1; /* do data I/O separately */
1898 * See if attribute already exists.
1900 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1901 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1905 entry
= ATTR_NEXT(entry
);
1909 if (options
& XATTR_CREATE
) {
1913 if (datalen
== entry
->length
) {
1915 uio_setoffset(uio
, entry
->offset
);
1916 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1917 uio_setoffset(uio
, 0);
1919 printf("setxattr: VNOP_WRITE error %d\n", error
);
1922 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1923 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1926 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1927 error
= write_xattrinfo(&ainfo
);
1929 printf("setxattr: write_xattrinfo error %d\n", error
);
1935 * Brute force approach - just remove old entry and set new entry.
1938 rel_xattrinfo(&ainfo
);
1939 close_xattrfile(xvp
, fileflags
, context
);
1940 error
= default_removexattr(vp
, name
, options
, context
);
1944 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1945 options
&= ~XATTR_REPLACE
;
1946 goto start
; /* start over */
1951 if (options
& XATTR_REPLACE
) {
1952 error
= ENOATTR
; /* nothing there to replace */
1955 /* Check if header size limit has been reached. */
1956 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1961 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1963 /* Check if we need more space. */
1964 if ((datalen
+ entrylen
) > datafreespace
) {
1967 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1969 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1970 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1971 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1974 ainfo
.filesize
+= growsize
;
1975 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1977 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1983 * Move the resource fork out of the way.
1985 if (ainfo
.rsrcfork
) {
1986 if (ainfo
.rsrcfork
->length
!= 0) {
1987 shift_data_down(xvp
,
1988 ainfo
.rsrcfork
->offset
,
1989 ainfo
.rsrcfork
->length
,
1992 ainfo
.rsrcfork
->offset
+= growsize
;
1994 ainfo
.finderinfo
->length
+= growsize
;
1995 header
->total_size
+= growsize
;
1998 /* Make space for a new entry. */
2000 shift_data_down(xvp
,
2002 header
->data_length
,
2005 bcopy((u_int8_t
*)header
+ header
->data_start
,
2006 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
2007 header
->data_length
);
2009 header
->data_start
+= entrylen
;
2011 /* Fix up entry data offsets. */
2013 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
2014 entry
->offset
+= entrylen
;
2018 * If the attribute data area is entirely within
2019 * the header buffer, then just update the buffer,
2020 * otherwise we'll write it separately to the file.
2025 /* Write new attribute data after the end of existing data. */
2026 offset
= header
->data_start
+ header
->data_length
;
2027 uio_setoffset(uio
, offset
);
2028 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
2029 uio_setoffset(uio
, 0);
2031 printf("setxattr: VNOP_WRITE error %d\n", error
);
2035 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
2037 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
2039 printf("setxattr: uiomove error %d\n", error
);
2044 /* Create the attribute entry. */
2045 lastentry
->length
= datalen
;
2046 lastentry
->offset
= header
->data_start
+ header
->data_length
;
2047 lastentry
->namelen
= namelen
;
2048 lastentry
->flags
= 0;
2049 bcopy(name
, &lastentry
->name
[0], namelen
);
2051 /* Update the attributes header. */
2052 header
->num_attrs
++;
2053 header
->data_length
+= datalen
;
2056 /* Only write the entries, since the data was written separately. */
2057 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
2059 /* The entry and data are both in the header; write them together. */
2060 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2062 error
= write_xattrinfo(&ainfo
);
2064 printf("setxattr: write_xattrinfo error %d\n", error
);
2068 rel_xattrinfo(&ainfo
);
2069 close_xattrfile(xvp
, fileflags
, context
);
2071 /* Touch the change time if we changed an attribute. */
2073 struct vnode_attr va
;
2075 /* Re-write the mtime to cause a ctime change. */
2077 VATTR_WANTED(&va
, va_modify_time
);
2078 if (vnode_getattr(vp
, &va
, context
) == 0) {
2080 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2081 (void) vnode_setattr(vp
, &va
, context
);
2085 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2092 * Remove an extended attribute.
2095 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
2099 attr_header_t
*header
;
2100 attr_entry_t
*entry
;
2101 attr_entry_t
*oldslot
;
2107 int found
= 0, lastone
= 0;
2115 fileflags
= FREAD
| FWRITE
;
2116 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
2119 * Open the file locked (exclusive) since the Carbon
2120 * File Manager may have the Apple Double file open
2121 * and could be changing the resource fork.
2123 fileflags
|= O_EXLOCK
;
2128 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
2131 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2132 close_xattrfile(xvp
, fileflags
, context
);
2136 attrcount
+= ainfo
.attrhdr
->num_attrs
;
2139 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
2142 /* Clear the Finder Info. */
2143 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
2144 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
2148 /* On removal of last attribute the ._ file is removed. */
2149 if (--attrcount
== 0)
2151 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
2152 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
2153 ainfo
.iosize
= sizeof(attr_header_t
);
2154 error
= write_xattrinfo(&ainfo
);
2158 /* Clear the Resource Fork. */
2160 if (!vnode_isreg(vp
)) {
2164 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
2168 /* On removal of last attribute the ._ file is removed. */
2169 if (--attrcount
== 0)
2173 * If the resource fork isn't the last AppleDouble
2174 * entry then the space needs to be reclaimed by
2175 * shifting the entries after the resource fork.
2177 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
2178 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
2179 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2182 ainfo
.rsrcfork
->length
= 0;
2183 ainfo
.iosize
= sizeof(attr_header_t
);
2184 error
= write_xattrinfo(&ainfo
);
2189 if (ainfo
.attrhdr
== NULL
) {
2193 namelen
= strlen(name
) + 1;
2194 header
= ainfo
.attrhdr
;
2195 entry
= ainfo
.attr_entry
;
2198 * See if this attribute exists.
2200 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2201 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2203 if ((i
+1) == header
->num_attrs
)
2207 entry
= ATTR_NEXT(entry
);
2213 /* On removal of last attribute the ._ file is removed. */
2214 if (--attrcount
== 0)
2217 datalen
= entry
->length
;
2218 dataoff
= entry
->offset
;
2219 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2220 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
2225 /* Remove the attribute entry. */
2227 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2228 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2231 /* Adjust the attribute data. */
2235 dataoff
- header
->data_start
,
2241 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2245 /* XXX write zeros to freed space ? */
2246 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2250 bcopy((u_int8_t
*)header
+ header
->data_start
,
2251 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2252 dataoff
- header
->data_start
);
2254 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2255 (u_int8_t
*)header
+ dataoff
- entrylen
,
2256 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2258 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2259 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2262 /* Adjust the header values and entry offsets. */
2263 header
->num_attrs
--;
2264 header
->data_start
-= entrylen
;
2265 header
->data_length
-= datalen
;
2268 entry
= ainfo
.attr_entry
;
2269 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2270 entry
->offset
-= entrylen
;
2271 if (entry
>= oldslot
)
2272 entry
->offset
-= datalen
;
2273 entry
= ATTR_NEXT(entry
);
2275 error
= write_xattrinfo(&ainfo
);
2277 printf("removexattr: write_xattrinfo error %d\n", error
);
2280 rel_xattrinfo(&ainfo
);
2282 /* When there are no more attributes remove the ._ file. */
2283 if (attrcount
== 0) {
2284 if (fileflags
& O_EXLOCK
)
2285 (void) unlock_xattrfile(xvp
, context
);
2286 VNOP_CLOSE(xvp
, fileflags
, context
);
2288 error
= remove_xattrfile(xvp
, context
);
2291 close_xattrfile(xvp
, fileflags
, context
);
2293 /* Touch the change time if we changed an attribute. */
2295 struct vnode_attr va
;
2297 /* Re-write the mtime to cause a ctime change. */
2299 VATTR_WANTED(&va
, va_modify_time
);
2300 if (vnode_getattr(vp
, &va
, context
) == 0) {
2302 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2303 (void) vnode_setattr(vp
, &va
, context
);
2307 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2315 * Retrieve the list of extended attribute names.
2318 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2322 attr_entry_t
*entry
;
2327 * We do not zero "*size" here as we don't want to stomp a size set when
2328 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2329 * system call layer, up in listxattr or flistxattr.
2332 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2333 if (error
== ENOATTR
)
2337 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2338 if (error
== ENOATTR
)
2340 close_xattrfile(xvp
, FREAD
, context
);
2344 /* Check for Finder Info. */
2345 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2347 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2348 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2352 error
= uiomove(XATTR_FINDERINFO_NAME
,
2353 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2361 /* Check for Resource Fork. */
2362 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2364 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2365 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2369 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2370 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2378 /* Check for attributes. */
2379 if (ainfo
.attrhdr
) {
2380 count
= ainfo
.attrhdr
->num_attrs
;
2381 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2382 if (xattr_protected((const char *)entry
->name
) ||
2383 xattr_validatename((const char *)entry
->name
) != 0) {
2384 entry
= ATTR_NEXT(entry
);
2388 *size
+= entry
->namelen
;
2389 entry
= ATTR_NEXT(entry
);
2392 if (uio_resid(uio
) < entry
->namelen
) {
2396 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2398 if (error
!= EFAULT
)
2402 entry
= ATTR_NEXT(entry
);
2406 rel_xattrinfo(&ainfo
);
2407 close_xattrfile(xvp
, FREAD
, context
);
2413 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2415 vnode_t xvp
= NULLVP
;
2416 vnode_t dvp
= NULLVP
;
2417 struct vnode_attr va
;
2418 struct nameidata nd
;
2420 char *filename
= NULL
;
2421 const char *basename
= NULL
;
2427 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2429 * For the root directory use "._." to hold the attributes.
2431 filename
= &smallname
[0];
2432 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2433 dvp
= vp
; /* the "._." file resides in the root dir */
2436 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2440 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2445 /* "._" Attribute files cannot have attributes */
2446 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2447 basename
[0] == '.' && basename
[1] == '_') {
2451 filename
= &smallname
[0];
2452 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2453 if (len
>= sizeof(smallname
)) {
2454 len
++; /* snprintf result doesn't include '\0' */
2455 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2456 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2459 * Note that the lookup here does not authorize. Since we are looking
2460 * up in the same directory that we already have the file vnode in,
2461 * we must have been given the file vnode legitimately. Read/write
2462 * access has already been authorized in layers above for calls from
2463 * userspace, and the authorization code using this path to read
2464 * file security from the EA must always get access
2467 NDINIT(&nd
, LOOKUP
, OP_OPEN
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
,
2468 UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), context
);
2471 if (fileflags
& O_CREAT
) {
2472 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2477 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2479 if ( (error
= namei(&nd
))) {
2484 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2490 * Pick up uid/gid/mode from target file.
2493 VATTR_WANTED(&va
, va_uid
);
2494 VATTR_WANTED(&va
, va_gid
);
2495 VATTR_WANTED(&va
, va_mode
);
2496 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2497 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2498 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2499 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2502 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2503 } else /* fallback values */ {
2504 uid
= KAUTH_UID_NONE
;
2505 gid
= KAUTH_GID_NONE
;
2506 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2510 VATTR_SET(&va
, va_type
, VREG
);
2511 VATTR_SET(&va
, va_mode
, umode
);
2512 if (uid
!= KAUTH_UID_NONE
)
2513 VATTR_SET(&va
, va_uid
, uid
);
2514 if (gid
!= KAUTH_GID_NONE
)
2515 VATTR_SET(&va
, va_gid
, gid
);
2517 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
, &va
,
2518 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2528 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2533 if ((error
= namei(&nd
))) {
2543 if (xvp
->v_type
!= VREG
) {
2548 * Owners must match.
2551 VATTR_WANTED(&va
, va_uid
);
2552 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2553 uid_t owner
= va
.va_uid
;
2556 VATTR_WANTED(&va
, va_uid
);
2557 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2558 error
= ENOATTR
; /* don't use this "._" file */
2563 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2569 if ((error
= vnode_ref(xvp
))) {
2574 /* If create was requested, make sure file header exists. */
2575 if (fileflags
& O_CREAT
) {
2577 VATTR_WANTED(&va
, va_data_size
);
2578 VATTR_WANTED(&va
, va_fileid
);
2579 VATTR_WANTED(&va
, va_nlink
);
2580 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2585 /* If the file is empty then add a default header. */
2586 if (va
.va_data_size
== 0) {
2587 /* Don't adopt hard-linked "._" files. */
2588 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2592 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2596 /* Apply file locking if requested. */
2597 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2600 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2601 error
= lock_xattrfile(xvp
, locktype
, context
);
2607 if (xvp
!= NULLVP
) {
2609 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2612 if (fileflags
& O_CREAT
) {
2613 /* Delete the xattr file if we encountered any errors */
2614 (void) remove_xattrfile (xvp
, context
);
2618 (void) vnode_rele(xvp
);
2620 (void) vnode_put(xvp
);
2623 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2627 /* Release resources after error-handling */
2628 if (dvp
&& (dvp
!= vp
)) {
2632 vnode_putname(basename
);
2634 if (filename
&& filename
!= &smallname
[0]) {
2635 FREE(filename
, M_TEMP
);
2638 *xvpp
= xvp
; /* return a referenced vnode */
2643 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2645 // if (fileflags & FWRITE)
2646 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2648 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2649 (void) unlock_xattrfile(xvp
, context
);
2651 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2652 (void) vnode_rele(xvp
);
2653 (void) vnode_put(xvp
);
2657 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2660 struct nameidata nd
;
2665 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2669 pathlen
= MAXPATHLEN
;
2670 error
= vn_getpath(xvp
, path
, &pathlen
);
2672 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2676 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2677 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2679 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2686 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2695 * Read in and parse the AppleDouble header and entries, and the extended
2696 * attribute header and entries if any. Populates the fields of ainfop
2697 * based on the headers and entries found.
2699 * The basic idea is to:
2700 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2701 * AppleDouble entries, the extended attribute header, and extended
2702 * attribute entries must lie within this part of the file; the rest of
2703 * the AppleDouble handling code assumes this. Plus it allows us to
2704 * somewhat optimize by doing a smaller number of larger I/Os.
2705 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2707 * - Find the Finder Info and Resource Fork entries, if any.
2708 * - If we're going to be writing, try to make sure the Finder Info entry has
2709 * room to store the extended attribute header, plus some space for extended
2711 * - Swap and sanity check the extended attribute header and entries (if any).
2714 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2717 void * buffer
= NULL
;
2718 apple_double_header_t
*filehdr
;
2719 struct vnode_attr va
;
2724 bzero(ainfop
, sizeof(attr_info_t
));
2725 ainfop
->filevp
= xvp
;
2726 ainfop
->context
= context
;
2728 VATTR_WANTED(&va
, va_data_size
);
2729 VATTR_WANTED(&va
, va_fileid
);
2730 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2733 ainfop
->filesize
= va
.va_data_size
;
2735 /* When setting attributes, allow room for the header to grow. */
2737 iosize
= ATTR_MAX_HDR_SIZE
;
2739 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2745 ainfop
->iosize
= iosize
;
2746 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2747 if (buffer
== NULL
){
2752 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2753 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2755 /* Read the file header. */
2756 error
= VNOP_READ(xvp
, auio
, 0, context
);
2760 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2761 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2763 filehdr
= (apple_double_header_t
*)buffer
;
2765 error
= check_and_swap_apple_double_header(ainfop
);
2769 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2771 /* rel_xattrinfo is responsible for freeing the header buffer */
2774 /* Find the Finder Info and Resource Fork entries, if any */
2775 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2776 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2777 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2778 /* We found the Finder Info entry. */
2779 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2782 * Is the Finder Info "empty" (all zeroes)? If so,
2783 * we'll pretend like the Finder Info extended attribute
2786 * Note: we have to make sure the Finder Info is
2787 * contained within the buffer we have already read,
2788 * to avoid accidentally accessing a bogus address.
2789 * If it is outside the buffer, we just assume the
2790 * Finder Info is non-empty.
2792 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2793 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2794 ainfop
->emptyfinderinfo
= 1;
2797 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2799 * Ignore zero-length resource forks when getting. If setting,
2800 * we need to remember the resource fork entry so it can be
2801 * updated once the new content has been written.
2803 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2807 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2809 * The "empty" resource headers we created have a system data tag of:
2810 * "This resource fork intentionally left blank "
2812 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2814 u_int8_t systemData
[64];
2818 /* Read the system data which starts at byte 16 */
2819 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2820 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2821 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2822 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2826 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2827 continue; /* skip this resource fork */
2830 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2831 if (i
!= (filehdr
->numEntries
- 1)) {
2832 printf("get_xattrinfo: resource fork not last entry\n");
2833 ainfop
->readonly
= 1;
2840 * See if this file looks like it is laid out correctly to contain
2841 * extended attributes. If so, then do the following:
2843 * - If we're going to be writing, try to make sure the Finder Info
2844 * entry has room to store the extended attribute header, plus some
2845 * space for extended attributes.
2847 * - Swap and sanity check the extended attribute header and entries
2850 if (filehdr
->numEntries
== 2 &&
2851 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2852 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2853 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2854 attr_header_t
*attrhdr
;
2855 attrhdr
= (attr_header_t
*)filehdr
;
2857 * If we're going to be writing, try to make sure the Finder
2858 * Info entry has room to store the extended attribute header,
2859 * plus some space for extended attributes.
2861 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2865 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2866 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2867 /* Make some room before existing resource fork. */
2868 shift_data_down(xvp
,
2869 filehdr
->entries
[1].offset
,
2870 filehdr
->entries
[1].length
,
2872 writesize
= sizeof(attr_header_t
);
2874 /* Create a new, empty resource fork. */
2875 rsrcfork_header_t
*rsrcforkhdr
;
2877 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2879 /* Steal some space for an empty RF header. */
2880 delta
-= sizeof(rsrcfork_header_t
);
2882 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2883 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2885 /* Fill in Empty Resource Fork Header. */
2886 init_empty_resource_fork(rsrcforkhdr
);
2888 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2889 writesize
= ATTR_BUF_SIZE
;
2891 filehdr
->entries
[0].length
+= delta
;
2892 filehdr
->entries
[1].offset
+= delta
;
2894 /* Fill in Attribute Header. */
2895 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2896 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2897 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2898 attrhdr
->data_start
= sizeof(attr_header_t
);
2899 attrhdr
->data_length
= 0;
2900 attrhdr
->reserved
[0] = 0;
2901 attrhdr
->reserved
[1] = 0;
2902 attrhdr
->reserved
[2] = 0;
2904 attrhdr
->num_attrs
= 0;
2906 /* Push out new header */
2907 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2908 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2910 swap_adhdr(filehdr
); /* to big endian */
2911 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2912 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2913 swap_adhdr(filehdr
); /* back to native */
2914 /* The attribute header gets swapped below. */
2918 * Swap and sanity check the extended attribute header and
2919 * entries (if any). The Finder Info content must be big enough
2920 * to include the extended attribute header; if not, we just
2923 * Note that we're passing the offset + length (i.e. the end)
2924 * of the Finder Info instead of rawsize to validate_attrhdr.
2925 * This ensures that all extended attributes lie within the
2926 * Finder Info content according to the AppleDouble entry.
2928 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2931 if (ainfop
->finderinfo
&&
2932 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2933 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2934 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2936 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2937 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2938 /* First attr_entry starts immediately following attribute header */
2939 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2948 FREE(buffer
, M_TEMP
);
2954 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2957 rsrcfork_header_t
*rsrcforkhdr
;
2963 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2964 bzero(buffer
, ATTR_BUF_SIZE
);
2966 xah
= (attr_header_t
*)buffer
;
2967 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2968 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2969 rsrcforksize
= sizeof(rsrcfork_header_t
);
2970 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2972 /* Fill in Apple Double Header. */
2973 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2974 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2975 xah
->appledouble
.numEntries
= SWAP16 (2);
2976 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2977 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2978 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2979 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2980 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2981 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2982 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2984 /* Fill in Attribute Header. */
2985 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2986 xah
->debug_tag
= SWAP32 (fileid
);
2987 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2988 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2990 /* Fill in Empty Resource Fork Header. */
2991 init_empty_resource_fork(rsrcforkhdr
);
2994 error
= VNOP_WRITE(xvp
, auio
, IO_UNIT
, context
);
2996 /* Did we write out the full uio? */
2997 if (uio_resid(auio
) > 0) {
3002 FREE(buffer
, M_TEMP
);
3008 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
3010 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
3011 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3012 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3013 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
3014 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3015 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3016 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
3017 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
3018 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
3019 rsrcforkhdr
->typeCount
= SWAP16 (-1);
3020 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
3024 rel_xattrinfo(attr_info_t
*ainfop
)
3026 FREE(ainfop
->filehdr
, M_TEMP
);
3027 bzero(ainfop
, sizeof(attr_info_t
));
3031 write_xattrinfo(attr_info_t
*ainfop
)
3036 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3037 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
3039 swap_adhdr(ainfop
->filehdr
);
3040 if (ainfop
->attrhdr
!= NULL
) {
3041 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3044 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
3046 swap_adhdr(ainfop
->filehdr
);
3047 if (ainfop
->attrhdr
!= NULL
) {
3048 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3055 #if BYTE_ORDER == LITTLE_ENDIAN
3057 * Endian swap apple double header
3060 swap_adhdr(apple_double_header_t
*adh
)
3065 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
3067 adh
->magic
= SWAP32 (adh
->magic
);
3068 adh
->version
= SWAP32 (adh
->version
);
3069 adh
->numEntries
= SWAP16 (adh
->numEntries
);
3071 for (i
= 0; i
< count
; i
++) {
3072 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
3073 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
3074 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
3079 * Endian swap extended attributes header
3082 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
3088 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
3090 ah
->magic
= SWAP32 (ah
->magic
);
3091 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3092 ah
->total_size
= SWAP32 (ah
->total_size
);
3093 ah
->data_start
= SWAP32 (ah
->data_start
);
3094 ah
->data_length
= SWAP32 (ah
->data_length
);
3095 ah
->flags
= SWAP16 (ah
->flags
);
3096 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3098 ae
= (attr_entry_t
*)(&ah
[1]);
3099 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
3100 ae
->offset
= SWAP32 (ae
->offset
);
3101 ae
->length
= SWAP32 (ae
->length
);
3102 ae
->flags
= SWAP16 (ae
->flags
);
3108 * Validate and swap the attributes header contents, and each attribute's
3111 * Note: Assumes the caller has verified that the Finder Info content is large
3112 * enough to contain the attr_header structure itself. Therefore, we can
3113 * swap the header fields before sanity checking them.
3116 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
3127 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
3130 /* Swap the basic header fields */
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
);
3140 * Make sure the total_size fits within the Finder Info area, and the
3141 * extended attribute data area fits within total_size.
3143 end
= ah
->data_start
+ ah
->data_length
;
3144 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3145 end
< ah
->data_start
||
3146 end
> ah
->total_size
) {
3151 * Make sure each of the attr_entry_t's fits within total_size.
3153 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3154 count
= ah
->num_attrs
;
3155 ae
= (attr_entry_t
*)(&ah
[1]);
3157 for (i
=0; i
<count
; i
++) {
3158 /* Make sure the fixed-size part of this attr_entry_t fits. */
3159 if ((u_int8_t
*) &ae
[1] > buf_end
)
3162 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3163 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3164 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
3167 /* Swap the attribute entry fields */
3168 ae
->offset
= SWAP32(ae
->offset
);
3169 ae
->length
= SWAP32(ae
->length
);
3170 ae
->flags
= SWAP16(ae
->flags
);
3172 /* Make sure the attribute content fits. */
3173 end
= ae
->offset
+ ae
->length
;
3174 if (end
< ae
->offset
|| end
> ah
->total_size
)
3181 * TODO: Make sure the contents of attributes don't overlap the header
3182 * and don't overlap each other. The hard part is that we don't know
3183 * what the actual header size is until we have looped over all of the
3184 * variable-sized attribute entries.
3186 * XXX Is there any guarantee that attribute entries are stored in
3187 * XXX order sorted by the contents' file offset? If so, that would
3188 * XXX make the pairwise overlap check much easier.
3195 // "start" & "end" are byte offsets in the file.
3196 // "to" is the byte offset we want to move the
3197 // data to. "to" should be > "start".
3199 // we do the copy backwards to avoid problems if
3200 // there's an overlap.
3203 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3206 size_t chunk
, orig_chunk
;
3209 kauth_cred_t ucred
= vfs_context_ucred(context
);
3210 proc_t p
= vfs_context_proc(context
);
3212 if (delta
== 0 || len
== 0) {
3222 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3226 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
3227 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3229 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3230 pos
, ret
, chunk
, ret
);
3234 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3236 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3237 pos
+delta
, ret
, chunk
, ret
);
3241 if ((pos
- (off_t
)chunk
) < start
) {
3242 chunk
= pos
- start
;
3244 if (chunk
== 0) { // we're all done
3249 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3256 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3259 size_t chunk
, orig_chunk
;
3263 kauth_cred_t ucred
= vfs_context_ucred(context
);
3264 proc_t p
= vfs_context_proc(context
);
3266 if (delta
== 0 || len
== 0) {
3277 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
, VM_KERN_MEMORY_FILE
)) {
3281 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3282 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3284 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3285 pos
, ret
, chunk
, ret
);
3289 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3291 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3292 pos
+delta
, ret
, chunk
, ret
);
3296 if ((pos
+ (off_t
)chunk
) > end
) {
3299 if (chunk
== 0) { // we're all done
3304 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3310 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3315 lf
.l_whence
= SEEK_SET
;
3318 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3319 /* Note: id is just a kernel address that's not a proc */
3320 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
, NULL
);
3321 return (error
== ENOTSUP
? 0 : error
);
3325 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3330 lf
.l_whence
= SEEK_SET
;
3333 lf
.l_type
= F_UNLCK
;
3334 /* Note: id is just a kernel address that's not a proc */
3335 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
, NULL
);
3336 return (error
== ENOTSUP
? 0 : error
);
3339 #else /* CONFIG_APPLEDOUBLE */
3343 default_getxattr(__unused vnode_t vp
, __unused
const char *name
,
3344 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3345 __unused vfs_context_t context
)
3351 default_setxattr(__unused vnode_t vp
, __unused
const char *name
,
3352 __unused uio_t uio
, __unused
int options
, __unused vfs_context_t context
)
3358 default_listxattr(__unused vnode_t vp
,
3359 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3360 __unused vfs_context_t context
)
3366 default_removexattr(__unused vnode_t vp
, __unused
const char *name
,
3367 __unused
int options
, __unused vfs_context_t context
)
3372 #endif /* CONFIG_APPLEDOUBLE */