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>
59 #if !CONFIG_APPLEDOUBLE
60 #define PANIC_ON_NOAPPLEDOUBLE 1
65 static int shadow_sequence
;
68 * We use %p to prevent loss of precision for pointers on varying architectures.
71 #define SHADOW_NAME_FMT ".vfs_rsrc_stream_%p%08x%p"
72 #define SHADOW_DIR_FMT ".vfs_rsrc_streams_%p%x"
73 #define SHADOW_DIR_CONTAINER "/var/run"
75 #define MAKE_SHADOW_NAME(VP, NAME) \
76 snprintf((NAME), sizeof((NAME)), (SHADOW_NAME_FMT), \
77 ((void*)(VM_KERNEL_ADDRPERM(VP))), \
79 ((void*)(VM_KERNEL_ADDRPERM((VP)->v_data))))
81 /* The full path to the shadow directory */
82 #define MAKE_SHADOW_DIRNAME(VP, NAME) \
83 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_CONTAINER "/" SHADOW_DIR_FMT), \
84 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
86 /* The shadow directory as a 'leaf' entry */
87 #define MAKE_SHADOW_DIR_LEAF(VP, NAME) \
88 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_FMT), \
89 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
91 static int default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
);
93 static int default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
);
95 static int default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
);
97 static int getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
, int *creator
, vfs_context_t context
);
99 static int get_shadow_dir(vnode_t
*sdvpp
);
101 #endif /* NAMEDSTREAMS */
104 * Default xattr support routines.
107 static int default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
, int options
,
108 vfs_context_t context
);
109 static int default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
,
110 vfs_context_t context
);
111 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
112 vfs_context_t context
);
113 static int default_removexattr(vnode_t vp
, const char *name
, int options
,
114 vfs_context_t context
);
117 * Retrieve the data of an extended attribute.
120 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
121 int options
, vfs_context_t context
)
125 if (!XATTR_VNODE_SUPPORTED(vp
)) {
129 /* getxattr calls are not allowed for streams. */
130 if (vp
->v_flag
& VISNAMEDSTREAM
) {
136 * Non-kernel request need extra checks performed.
138 * The XATTR_NOSECURITY flag implies a kernel request.
140 if (!(options
& XATTR_NOSECURITY
)) {
142 error
= mac_vnode_check_getextattr(context
, vp
, name
, uio
);
146 if ((error
= xattr_validatename(name
))) {
149 if ((error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
))) {
152 /* The offset can only be non-zero for resource forks. */
153 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
154 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
160 /* The offset can only be non-zero for resource forks. */
161 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
162 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
167 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
168 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
170 * A filesystem may keep some EAs natively and return ENOTSUP for others.
172 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
179 * Set the data of an extended attribute.
182 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
186 if (!XATTR_VNODE_SUPPORTED(vp
)) {
190 /* setxattr calls are not allowed for streams. */
191 if (vp
->v_flag
& VISNAMEDSTREAM
) {
196 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
199 if ((error
= xattr_validatename(name
))) {
202 if (!(options
& XATTR_NOSECURITY
)) {
204 error
= mac_vnode_check_setextattr(context
, vp
, name
, uio
);
208 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
212 /* The offset can only be non-zero for resource forks. */
213 if (uio_offset(uio
) != 0 &&
214 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0 ) {
219 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
222 * An EJUSTRETURN is from a filesystem which keeps this xattr
223 * natively as well as in a dot-underscore file. In this case the
224 * EJUSTRETURN means the filesytem has done nothing, but identifies the
225 * EA as one which may be represented natively and/or in a DU, and
226 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
227 * in vn_setxattr can we do the getxattrs needed to ascertain whether
228 * the XATTR_{CREATE,REPLACE} should yield an error.
230 if (error
== EJUSTRETURN
) {
231 int native
= 0, dufile
= 0;
232 size_t sz
; /* not used */
234 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
235 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
236 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
240 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
245 * Having determined no CREATE/REPLACE error should result, we
246 * zero those bits, so both backing stores get written to.
248 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
249 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
250 /* the mainline path here is to have error==ENOTSUP ... */
252 #endif /* DUAL_EAS */
253 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
255 * A filesystem may keep some EAs natively and return ENOTSUP for others.
257 error
= default_setxattr(vp
, name
, uio
, options
, context
);
260 if ((error
== 0) && !(options
& XATTR_NOSECURITY
) &&
261 (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
))
262 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
269 * Remove an extended attribute.
272 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
276 if (!XATTR_VNODE_SUPPORTED(vp
)) {
280 /* removexattr calls are not allowed for streams. */
281 if (vp
->v_flag
& VISNAMEDSTREAM
) {
286 if ((error
= xattr_validatename(name
))) {
289 if (!(options
& XATTR_NOSECURITY
)) {
291 error
= mac_vnode_check_deleteextattr(context
, vp
, name
);
295 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
299 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
300 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
302 * A filesystem may keep some EAs natively and return ENOTSUP for others.
304 error
= default_removexattr(vp
, name
, options
, context
);
306 } else if (error
== EJUSTRETURN
) {
308 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
309 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
310 * a native xattr, so failure to find it in a DU file during
311 * default_removexattr should not be considered an error.
313 error
= default_removexattr(vp
, name
, options
, context
);
314 if (error
== ENOATTR
)
316 #endif /* DUAL_EAS */
319 if ((error
== 0) && !(options
& XATTR_NOSECURITY
) &&
320 (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
))
321 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
328 * Retrieve the list of extended attribute names.
331 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
335 if (!XATTR_VNODE_SUPPORTED(vp
)) {
339 /* listxattr calls are not allowed for streams. */
340 if (vp
->v_flag
& VISNAMEDSTREAM
) {
345 if (!(options
& XATTR_NOSECURITY
)) {
347 error
= mac_vnode_check_listextattr(context
, vp
);
352 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
);
357 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
358 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
360 * A filesystem may keep some but not all EAs natively, in which case
361 * the native EA names will have been uiomove-d out (or *size updated)
362 * and the default_listxattr here will finish the job.
364 error
= default_listxattr(vp
, uio
, size
, options
, context
);
371 xattr_validatename(const char *name
)
375 if (name
== NULL
|| name
[0] == '\0') {
378 namelen
= strnlen(name
, XATTR_MAXNAMELEN
);
379 if (name
[namelen
] != '\0')
380 return (ENAMETOOLONG
);
382 if (utf8_validatestr((const unsigned char *)name
, namelen
) != 0)
390 * Determine whether an EA is a protected system attribute.
393 xattr_protected(const char *attrname
)
395 return(!strncmp(attrname
, "com.apple.system.", 17));
402 * Obtain a named stream from vnode vp.
405 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
409 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
410 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
412 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
415 uint32_t streamflags
= VISNAMEDSTREAM
;
418 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
419 streamflags
|= VISSHADOW
;
423 vnode_lock_spin(svp
);
424 svp
->v_flag
|= streamflags
;
427 /* Tag the parent so we know to flush credentials for streams on setattr */
429 vp
->v_lflag
|= VL_HASSTREAMS
;
432 /* Make the file it's parent.
433 * Note: This parent link helps us distinguish vnodes for
434 * shadow stream files from vnodes for resource fork on file
435 * systems that support namedstream natively (both have
436 * VISNAMEDSTREAM set) by allowing access to mount structure
437 * for checking MNTK_NAMED_STREAMS bit at many places in the
440 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
447 * Make a named stream for vnode vp.
450 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
454 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
455 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
457 error
= default_makenamedstream(vp
, svpp
, name
, context
);
460 uint32_t streamflags
= VISNAMEDSTREAM
;
464 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
465 streamflags
|= VISSHADOW
;
469 vnode_lock_spin(svp
);
470 svp
->v_flag
|= streamflags
;
473 /* Tag the parent so we know to flush credentials for streams on setattr */
475 vp
->v_lflag
|= VL_HASSTREAMS
;
478 /* Make the file it's parent.
479 * Note: This parent link helps us distinguish vnodes for
480 * shadow stream files from vnodes for resource fork on file
481 * systems that support namedstream natively (both have
482 * VISNAMEDSTREAM set) by allowing access to mount structure
483 * for checking MNTK_NAMED_STREAMS bit at many places in the
486 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
492 * Remove a named stream from vnode vp.
495 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
499 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
500 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
502 error
= default_removenamedstream(vp
, name
, context
);
507 #define NS_IOBUFSIZE (128 * 1024)
510 * Release a named stream shadow file.
512 * Note: This function is called from two places where we do not need
513 * to check if the vnode has any references held before deleting the
514 * shadow file. Once from vclean() when the vnode is being reclaimed
515 * and we do not hold any references on the vnode. Second time from
516 * default_getnamedstream() when we get an error during shadow stream
517 * file initialization so that other processes who are waiting for the
518 * shadow stream file initialization by the creator will get opportunity
519 * to create and initialize the file again.
522 vnode_relenamedstream(vnode_t vp
, vnode_t svp
) {
524 struct componentname cn
;
529 * We need to use the kernel context here. If we used the supplied
530 * VFS context we have no clue whether or not it originated from userland
531 * where it could be subject to a chroot jail. We need to ensure that all
532 * filesystem access to shadow files is done on the same FS regardless of
533 * userland process restrictions.
535 vfs_context_t kernelctx
= vfs_context_kernel();
540 MAKE_SHADOW_NAME(vp
, tmpname
);
543 cn
.cn_nameiop
= DELETE
;
544 cn
.cn_flags
= ISLASTCN
;
545 cn
.cn_context
= kernelctx
;
546 cn
.cn_pnbuf
= tmpname
;
547 cn
.cn_pnlen
= sizeof(tmpname
);
548 cn
.cn_nameptr
= cn
.cn_pnbuf
;
549 cn
.cn_namelen
= strlen(tmpname
);
552 * Obtain the vnode for the shadow files directory. Make sure to
553 * use the kernel ctx as described above.
555 err
= get_shadow_dir(&dvp
);
560 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, kernelctx
);
567 * Flush a named stream shadow file.
569 * 'vp' represents the AppleDouble file.
570 * 'svp' represents the shadow file.
573 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
575 struct vnode_attr va
;
577 caddr_t bufptr
= NULL
;
584 * The kernel context must be used for all I/O to the shadow file
585 * and its namespace operations
587 vfs_context_t kernelctx
= vfs_context_kernel();
589 /* The supplied context is used for access to the AD file itself */
592 VATTR_WANTED(&va
, va_data_size
);
593 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
594 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
597 datasize
= va
.va_data_size
;
599 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
603 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
604 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
607 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
611 * Copy the shadow stream file data into the resource fork.
613 error
= VNOP_OPEN(svp
, 0, kernelctx
);
615 printf("vnode_flushnamedstream: err %d opening file\n", error
);
618 while (offset
< datasize
) {
619 iosize
= MIN(datasize
- offset
, iosize
);
621 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
622 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
623 error
= VNOP_READ(svp
, auio
, 0, kernelctx
);
627 /* Since there's no truncate xattr we must remove the resource fork. */
629 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
630 if ((error
!= 0) && (error
!= ENOATTR
)) {
634 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
635 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
636 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
643 /* close shadowfile */
644 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
647 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
657 * Verify that the vnode 'vp' is a vnode that lives in the shadow
658 * directory. We can't just query the parent pointer directly since
659 * the shadowfile is hooked up to the actual file it's a stream for.
661 errno_t
vnode_verifynamedstream(vnode_t vp
) {
663 struct vnode
*shadow_dvp
= NULL
;
664 struct vnode
*shadowfile
= NULL
;
665 struct componentname cn
;
668 * We need to use the kernel context here. If we used the supplied
669 * VFS context we have no clue whether or not it originated from userland
670 * where it could be subject to a chroot jail. We need to ensure that all
671 * filesystem access to shadow files is done on the same FS regardless of
672 * userland process restrictions.
674 vfs_context_t kernelctx
= vfs_context_kernel();
678 /* Get the shadow directory vnode */
679 error
= get_shadow_dir(&shadow_dvp
);
684 /* Re-generate the shadow name in the buffer */
685 MAKE_SHADOW_NAME (vp
, tmpname
);
687 /* Look up item in shadow dir */
688 bzero(&cn
, sizeof(cn
));
689 cn
.cn_nameiop
= LOOKUP
;
690 cn
.cn_flags
= ISLASTCN
| CN_ALLOWRSRCFORK
;
691 cn
.cn_context
= kernelctx
;
692 cn
.cn_pnbuf
= tmpname
;
693 cn
.cn_pnlen
= sizeof(tmpname
);
694 cn
.cn_nameptr
= cn
.cn_pnbuf
;
695 cn
.cn_namelen
= strlen(tmpname
);
697 if (VNOP_LOOKUP (shadow_dvp
, &shadowfile
, &cn
, kernelctx
) == 0) {
698 /* is the pointer the same? */
699 if (shadowfile
== vp
) {
705 /* drop the iocount acquired */
706 vnode_put (shadowfile
);
709 /* Drop iocount on shadow dir */
710 vnode_put (shadow_dvp
);
715 * Access or create the shadow file as needed.
717 * 'makestream' with non-zero value means that we need to guarantee we were the
718 * creator of the shadow file.
720 * 'context' is the user supplied context for the original VFS operation that
721 * caused us to need a shadow file.
723 * int pointed to by 'creator' is nonzero if we created the shadowfile.
726 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
727 int *creator
, vfs_context_t context
)
729 vnode_t dvp
= NULLVP
;
730 vnode_t svp
= NULLVP
;
731 struct componentname cn
;
732 struct vnode_attr va
;
737 vfs_context_t kernelctx
= vfs_context_kernel();
741 /* Establish a unique file name. */
742 MAKE_SHADOW_NAME(vp
, tmpname
);
743 bzero(&cn
, sizeof(cn
));
744 cn
.cn_nameiop
= LOOKUP
;
745 cn
.cn_flags
= ISLASTCN
;
746 cn
.cn_context
= context
;
747 cn
.cn_pnbuf
= tmpname
;
748 cn
.cn_pnlen
= sizeof(tmpname
);
749 cn
.cn_nameptr
= cn
.cn_pnbuf
;
750 cn
.cn_namelen
= strlen(tmpname
);
752 /* Pick up uid, gid, mode and date from original file. */
754 VATTR_WANTED(&va
, va_uid
);
755 VATTR_WANTED(&va
, va_gid
);
756 VATTR_WANTED(&va
, va_mode
);
757 VATTR_WANTED(&va
, va_create_time
);
758 VATTR_WANTED(&va
, va_modify_time
);
759 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
760 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
761 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
762 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
763 va
.va_uid
= KAUTH_UID_NONE
;
764 va
.va_gid
= KAUTH_GID_NONE
;
765 va
.va_mode
= S_IRUSR
| S_IWUSR
;
767 va
.va_vaflags
= VA_EXCLUSIVE
;
768 VATTR_SET(&va
, va_type
, VREG
);
769 /* We no longer change the access, but we still hide it. */
770 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
772 /* Obtain the vnode for the shadow files directory. */
773 if (get_shadow_dir(&dvp
) != 0) {
778 /* See if someone else already has it open. */
779 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
) == 0) {
780 /* Double check existence by asking for size. */
782 VATTR_WANTED(&va
, va_data_size
);
783 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
784 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
785 goto out
; /* OK to use. */
790 * Otherwise make sure the resource fork data exists.
791 * Use the supplied context for accessing the AD file.
793 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
794 XATTR_NOSECURITY
, context
);
796 * To maintain binary compatibility with legacy Carbon
797 * emulated resource fork support, if the resource fork
798 * doesn't exist but the Finder Info does, then act as
799 * if an empty resource fork is present (see 4724359).
801 if ((error
== ENOATTR
) &&
802 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
803 XATTR_NOSECURITY
, context
) == 0)) {
811 /* If the resource fork exists, its size is expected to be non-zero. */
818 /* Create the shadow stream file. */
819 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, kernelctx
);
824 else if ((error
== EEXIST
) && !makestream
) {
825 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, kernelctx
);
827 else if ((error
== ENOENT
) && !makestream
) {
829 * We could have raced with a rmdir on the shadow directory
830 * post-lookup. Retry from the beginning, 1x only, to
831 * try and see if we need to re-create the shadow directory
846 /* Otherwise, just error out normally below */
854 /* On errors, clean up shadow stream file. */
862 *rsrcsize
= datasize
;
869 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
871 vnode_t svp
= NULLVP
;
873 caddr_t bufptr
= NULL
;
879 /* need the kernel context for accessing the shadowfile */
880 vfs_context_t kernelctx
= vfs_context_kernel();
883 * Only the "com.apple.ResourceFork" stream is supported here.
885 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
891 * Obtain a shadow file for the resource fork I/O.
893 * Need to pass along the supplied context so that getshadowfile
894 * can access the AD file as needed, using it.
896 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
903 * The creator of the shadow file provides its file data,
904 * all other threads should wait until its ready. In order to
905 * prevent a deadlock during error codepaths, we need to check if the
906 * vnode is being created, or if it has failed out. Regardless of success or
907 * failure, we set the VISSHADOW bit on the vnode, so we check that
908 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
909 * then we can infer the creator isn't done yet. If it's there, but
910 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
915 if (svp
->v_flag
& VISNAMEDSTREAM
) {
916 /* data is ready, go use it */
920 /* It's not ready, wait for it (sleep using v_parent as channel) */
921 if ((svp
->v_flag
& VISSHADOW
)) {
923 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
924 * thread is done with this vnode. Just unlock the vnode and try again
929 /* Otherwise, sleep if the shadow file is not created yet */
930 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
931 "getnamedstream", NULL
);
940 * Copy the real resource fork data into shadow stream file.
942 if (op
== NS_OPEN
&& datasize
!= 0) {
946 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
947 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
952 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
955 /* open the shadow file */
956 error
= VNOP_OPEN(svp
, 0, kernelctx
);
960 while (offset
< datasize
) {
963 iosize
= MIN(datasize
- offset
, iosize
);
965 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
966 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
967 /* use supplied ctx for AD file */
968 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
969 XATTR_NOSECURITY
, context
);
974 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
975 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
976 /* kernel context for writing shadowfile */
977 error
= VNOP_WRITE(svp
, auio
, 0, kernelctx
);
984 /* close shadow file */
985 (void) VNOP_CLOSE(svp
, 0, kernelctx
);
988 /* Wake up anyone waiting for svp file content */
992 /* VISSHADOW would be set later on anyway, so we set it now */
993 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
994 wakeup((caddr_t
)&svp
->v_parent
);
997 /* On post create errors, get rid of the shadow file. This
998 * way if there is another process waiting for initialization
999 * of the shadowfile by the current process will wake up and
1000 * retry by creating and initializing the shadow file again.
1001 * Also add the VISSHADOW bit here to indicate we're done operating
1004 (void)vnode_relenamedstream(vp
, svp
);
1006 svp
->v_flag
|= VISSHADOW
;
1007 wakeup((caddr_t
)&svp
->v_parent
);
1013 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
1019 /* On errors, clean up shadow stream file. */
1030 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
1036 * Only the "com.apple.ResourceFork" stream is supported here.
1038 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1043 /* Supply the context to getshadowfile so it can manipulate the AD file */
1044 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
1047 * Wake up any waiters over in default_getnamedstream().
1049 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
1050 vnode_t svp
= *svpp
;
1053 /* If we're the creator, mark it as a named stream */
1054 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
1055 /* Wakeup any waiters on the v_parent channel */
1056 wakeup((caddr_t
)&svp
->v_parent
);
1065 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
1068 * Only the "com.apple.ResourceFork" stream is supported here.
1070 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1074 * XXX - what about other opened instances?
1076 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
1080 get_shadow_dir(vnode_t
*sdvpp
) {
1081 vnode_t dvp
= NULLVP
;
1082 vnode_t sdvp
= NULLVP
;
1083 struct componentname cn
;
1084 struct vnode_attr va
;
1088 vfs_context_t kernelctx
= vfs_context_kernel();
1090 bzero(tmpname
, sizeof(tmpname
));
1091 MAKE_SHADOW_DIRNAME(rootvnode
, tmpname
);
1093 * Look up the shadow directory to ensure that it still exists.
1094 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1095 * in caching it when multiple threads may be trying to manipulate the pointers.
1097 * Make sure to use the kernel context. We want a singular view of
1098 * the shadow dir regardless of chrooted processes.
1100 error
= vnode_lookup(tmpname
, 0, &sdvp
, kernelctx
);
1103 * If we get here, then we have successfully looked up the shadow dir,
1104 * and it has an iocount from the lookup. Return the vp in the output argument.
1109 /* In the failure case, no iocount is acquired */
1111 bzero (tmpname
, sizeof(tmpname
));
1114 * Obtain the vnode for "/var/run" directory using the kernel
1117 * This is defined in the SHADOW_DIR_CONTAINER macro
1119 if (vnode_lookup(SHADOW_DIR_CONTAINER
, 0, &dvp
, kernelctx
) != 0) {
1125 * Create the shadow stream directory.
1126 * 'dvp' below suggests the parent directory so
1127 * we only need to provide the leaf entry name
1129 MAKE_SHADOW_DIR_LEAF(rootvnode
, tmpname
);
1130 bzero(&cn
, sizeof(cn
));
1131 cn
.cn_nameiop
= LOOKUP
;
1132 cn
.cn_flags
= ISLASTCN
;
1133 cn
.cn_context
= kernelctx
;
1134 cn
.cn_pnbuf
= tmpname
;
1135 cn
.cn_pnlen
= sizeof(tmpname
);
1136 cn
.cn_nameptr
= cn
.cn_pnbuf
;
1137 cn
.cn_namelen
= strlen(tmpname
);
1140 * owned by root, only readable by root, hidden
1143 VATTR_SET(&va
, va_uid
, 0);
1144 VATTR_SET(&va
, va_gid
, 0);
1145 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
1146 VATTR_SET(&va
, va_type
, VDIR
);
1147 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
1148 va
.va_vaflags
= VA_EXCLUSIVE
;
1150 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, kernelctx
);
1153 * There can be only one winner for an exclusive create.
1155 if (error
== EEXIST
) {
1156 /* loser has to look up directory */
1157 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, kernelctx
);
1159 /* Make sure its in fact a directory */
1160 if (sdvp
->v_type
!= VDIR
) {
1163 /* Obtain the fsid for /var/run directory */
1165 VATTR_WANTED(&va
, va_fsid
);
1166 if (VNOP_GETATTR(dvp
, &va
, kernelctx
) != 0 ||
1167 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1170 tmp_fsid
= va
.va_fsid
;
1173 VATTR_WANTED(&va
, va_uid
);
1174 VATTR_WANTED(&va
, va_gid
);
1175 VATTR_WANTED(&va
, va_mode
);
1176 VATTR_WANTED(&va
, va_fsid
);
1177 VATTR_WANTED(&va
, va_dirlinkcount
);
1178 VATTR_WANTED(&va
, va_acl
);
1179 /* Provide defaults for attrs that may not be supported */
1180 va
.va_dirlinkcount
= 1;
1181 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1183 if (VNOP_GETATTR(sdvp
, &va
, kernelctx
) != 0 ||
1184 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1185 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1186 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1187 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1191 * Make sure its what we want:
1193 * - not writable by anyone
1194 * - on same file system as /var/run
1195 * - not a hard-linked directory
1196 * - no ACLs (they might grant write access)
1198 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1199 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1200 (va
.va_fsid
!= tmp_fsid
) ||
1201 (va
.va_dirlinkcount
!= 1) ||
1202 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1212 /* On errors, clean up shadow stream directory. */
1222 /* This is not the dir we're looking for, move along */
1223 ++shadow_sequence
; /* try something else next time */
1227 #endif /* NAMEDSTREAMS */
1230 #if CONFIG_APPLEDOUBLE
1232 * Default Implementation (Non-native EA)
1237 Typical "._" AppleDouble Header File layout:
1238 ------------------------------------------------------------
1243 .-- AD ENTRY[0] Finder Info Entry (must be first)
1244 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1246 | ///////////// Fixed Size Data (32 bytes)
1250 | ATTR ENTRY[1] --+--.
1251 | ATTR ENTRY[2] --+--+--.
1253 | ATTR ENTRY[N] --+--+--+--.
1254 | ATTR DATA 0 <-' | | |
1255 | //////////// | | |
1256 | ATTR DATA 1 <----' | |
1258 | ATTR DATA 2 <-------' |
1261 | ATTR DATA N <----------'
1263 | Attribute Free Space
1265 '----> RESOURCE FORK
1266 ///////////// Variable Sized Data
1275 ------------------------------------------------------------
1277 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1278 stored as part of the Finder Info. The length in the Finder
1279 Info AppleDouble entry includes the length of the extended
1280 attribute header, attribute entries, and attribute data.
1285 * On Disk Data Structures
1287 * Note: Motorola 68K alignment and big-endian.
1289 * See RFC 1740 for additional information about the AppleDouble file format.
1293 #define ADH_MAGIC 0x00051607
1294 #define ADH_VERSION 0x00020000
1295 #define ADH_MACOSX "Mac OS X "
1298 * AppleDouble Entry ID's
1300 #define AD_DATA 1 /* Data fork */
1301 #define AD_RESOURCE 2 /* Resource fork */
1302 #define AD_REALNAME 3 /* FileÕs name on home file system */
1303 #define AD_COMMENT 4 /* Standard Mac comment */
1304 #define AD_ICONBW 5 /* Mac black & white icon */
1305 #define AD_ICONCOLOR 6 /* Mac color icon */
1306 #define AD_UNUSED 7 /* Not used */
1307 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1308 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1309 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1310 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1311 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1312 #define AD_AFPNAME 13 /* Short name on AFP server */
1313 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1314 #define AD_AFPDIRID 15 /* AFP directory ID */
1315 #define AD_ATTRIBUTES AD_FINDERINFO
1318 #define ATTR_FILE_PREFIX "._"
1319 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1321 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1323 /* Implementation Limits */
1324 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1325 #define ATTR_MAX_HDR_SIZE 65536
1327 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1328 * size supported (including the attribute entries). All of
1329 * the attribute entries must reside within this limit. If
1330 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1331 * boundry, then all of the attribute data I/O is performed
1332 * separately from the attribute header I/O.
1334 * In particular, all of the attr_entry structures must lie
1335 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1336 * AppleDouble file. However, the attribute data (i.e. the
1337 * contents of the extended attributes) may extend beyond the
1338 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1339 * limit is to allow the implementation to optimize by reading
1340 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1344 #define FINDERINFOSIZE 32
1346 typedef struct apple_double_entry
{
1347 u_int32_t type
; /* entry type: see list, 0 invalid */
1348 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1349 u_int32_t length
; /* entry data length in bytes. */
1350 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1353 typedef struct apple_double_header
{
1354 u_int32_t magic
; /* == ADH_MAGIC */
1355 u_int32_t version
; /* format version: 2 = 0x00020000 */
1356 u_int32_t filler
[4];
1357 u_int16_t numEntries
; /* number of entries which follow */
1358 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1359 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1360 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1361 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1363 #define ADHDRSIZE (4+4+16+2)
1365 /* Entries are aligned on 4 byte boundaries */
1366 typedef struct attr_entry
{
1367 u_int32_t offset
; /* file offset to data */
1368 u_int32_t length
; /* size of attribute data */
1371 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1372 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1375 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1376 typedef struct attr_header
{
1377 apple_double_header_t appledouble
;
1378 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1379 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1380 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1381 u_int32_t data_start
; /* file offset to attribute data area */
1382 u_int32_t data_length
; /* length of attribute data area */
1383 u_int32_t reserved
[3];
1385 u_int16_t num_attrs
;
1386 } __attribute__((aligned(2), packed
)) attr_header_t
;
1389 /* Empty Resource Fork Header */
1390 typedef struct rsrcfork_header
{
1391 u_int32_t fh_DataOffset
;
1392 u_int32_t fh_MapOffset
;
1393 u_int32_t fh_DataLength
;
1394 u_int32_t fh_MapLength
;
1395 u_int8_t systemData
[112];
1396 u_int8_t appData
[128];
1397 u_int32_t mh_DataOffset
;
1398 u_int32_t mh_MapOffset
;
1399 u_int32_t mh_DataLength
;
1400 u_int32_t mh_MapLength
;
1402 u_int16_t mh_RefNum
;
1404 u_int8_t mh_InMemoryAttr
;
1407 u_int16_t typeCount
;
1408 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1410 #define RF_FIRST_RESOURCE 256
1411 #define RF_NULL_MAP_LENGTH 30
1412 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1414 /* Runtime information about the attribute file. */
1415 typedef struct attr_info
{
1416 vfs_context_t context
;
1421 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1422 apple_double_header_t
*filehdr
;
1423 apple_double_entry_t
*finderinfo
;
1424 apple_double_entry_t
*rsrcfork
;
1425 attr_header_t
*attrhdr
;
1426 attr_entry_t
*attr_entry
;
1428 u_int8_t emptyfinderinfo
;
1432 #define ATTR_SETTING 1
1434 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1436 #define ATTR_ENTRY_LENGTH(namelen) \
1437 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1439 #define ATTR_NEXT(ae) \
1440 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1442 #define ATTR_VALID(ae, ai) \
1443 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1445 #define SWAP16(x) OSSwapBigToHostInt16((x))
1446 #define SWAP32(x) OSSwapBigToHostInt32((x))
1447 #define SWAP64(x) OSSwapBigToHostInt64((x))
1450 static u_int32_t emptyfinfo
[8] = {0};
1454 * Local support routines
1456 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1458 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1460 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1462 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1464 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1466 static void rel_xattrinfo(attr_info_t
*ainfop
);
1468 static int write_xattrinfo(attr_info_t
*ainfop
);
1470 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1472 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1474 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1477 #if BYTE_ORDER == LITTLE_ENDIAN
1478 static void swap_adhdr(apple_double_header_t
*adh
);
1479 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1482 #define swap_adhdr(x)
1483 #define swap_attrhdr(x, y)
1486 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1487 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1488 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1492 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1493 * is in big endian (as it would exist on disk). Verifies the following:
1496 * - number of entries
1497 * - that each entry fits within the file size
1499 * If the header is invalid, ENOATTR is returned.
1501 * NOTE: Does not attempt to validate the extended attributes header that
1502 * may be embedded in the Finder Info entry.
1504 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1507 u_int32_t header_end
;
1508 u_int32_t entry_end
;
1510 apple_double_header_t
*header
;
1512 rawsize
= ainfop
->rawsize
;
1513 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1515 /* Is the file big enough to contain an AppleDouble header? */
1516 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1519 /* Swap the AppleDouble header fields to native order */
1520 header
->magic
= SWAP32(header
->magic
);
1521 header
->version
= SWAP32(header
->version
);
1522 header
->numEntries
= SWAP16(header
->numEntries
);
1524 /* Sanity check the AppleDouble header fields */
1525 if (header
->magic
!= ADH_MAGIC
||
1526 header
->version
!= ADH_VERSION
||
1527 header
->numEntries
< 1 ||
1528 header
->numEntries
> 15) {
1532 /* Calculate where the entries[] array ends */
1533 header_end
= offsetof(apple_double_header_t
, entries
) +
1534 header
->numEntries
* sizeof(apple_double_entry_t
);
1536 /* Is the file big enough to contain the AppleDouble entries? */
1537 if (rawsize
< header_end
) {
1541 /* Swap and sanity check each AppleDouble entry */
1542 for (i
=0; i
<header
->numEntries
; i
++) {
1543 /* Swap the per-entry fields to native order */
1544 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1545 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1546 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1548 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1551 * Does the entry's content start within the header itself,
1552 * did the addition overflow, or does the entry's content
1553 * extend past the end of the file?
1555 if (header
->entries
[i
].offset
< header_end
||
1556 entry_end
< header
->entries
[i
].offset
||
1557 entry_end
> ainfop
->filesize
) {
1562 * Does the current entry's content overlap with a previous
1565 * Yes, this is O(N**2), and there are more efficient algorithms
1566 * for testing pairwise overlap of N ranges when N is large.
1567 * But we have already ensured N < 16, and N is almost always 2.
1568 * So there's no point in using a more complex algorithm.
1571 for (j
=0; j
<i
; j
++) {
1572 if (entry_end
> header
->entries
[j
].offset
&&
1573 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1585 * Retrieve the data of an extended attribute.
1588 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1589 __unused
int options
, vfs_context_t context
)
1593 attr_header_t
*header
;
1594 attr_entry_t
*entry
;
1604 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1607 * Open the file locked (shared) since the Carbon
1608 * File Manager may have the Apple Double file open
1609 * and could be changing the resource fork.
1611 fileflags
|= O_SHLOCK
;
1616 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1619 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1620 close_xattrfile(xvp
, fileflags
, context
);
1624 /* Get the Finder Info. */
1625 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1627 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1629 } else if (uio
== NULL
) {
1630 *size
= FINDERINFOSIZE
;
1632 } else if (uio_offset(uio
) != 0) {
1634 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1637 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1638 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1643 /* Read the Resource Fork. */
1645 if (!vnode_isreg(vp
)) {
1647 } else if (ainfo
.rsrcfork
== NULL
) {
1649 } else if (uio
== NULL
) {
1650 *size
= (size_t)ainfo
.rsrcfork
->length
;
1652 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1653 error
= VNOP_READ(xvp
, uio
, 0, context
);
1655 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1660 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1664 if (uio_offset(uio
) != 0) {
1669 namelen
= strlen(name
) + 1;
1670 header
= ainfo
.attrhdr
;
1671 entry
= ainfo
.attr_entry
;
1673 * Search for attribute name in the header.
1675 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1676 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1677 datalen
= (size_t)entry
->length
;
1683 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1687 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1688 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1689 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1691 uio_setoffset(uio
, entry
->offset
);
1692 error
= VNOP_READ(xvp
, uio
, 0, context
);
1693 uio_setoffset(uio
, 0);
1697 entry
= ATTR_NEXT(entry
);
1700 rel_xattrinfo(&ainfo
);
1701 close_xattrfile(xvp
, fileflags
, context
);
1707 * Set the data of an extended attribute.
1710 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1714 attr_header_t
*header
;
1715 attr_entry_t
*entry
;
1716 attr_entry_t
*lastentry
;
1720 size_t datafreespace
;
1727 char finfo
[FINDERINFOSIZE
];
1729 datalen
= uio_resid(uio
);
1730 namelen
= strlen(name
) + 1;
1731 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1734 * By convention, Finder Info that is all zeroes is equivalent to not
1735 * having a Finder Info EA. So if we're trying to set the Finder Info
1736 * to all zeroes, then delete it instead. If a file didn't have an
1737 * AppleDouble file before, this prevents creating an AppleDouble file
1738 * with no useful content.
1740 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1741 * for all zeroes Finder Info before opening the AppleDouble file.
1742 * But if either of those options were specified, we need to open the
1743 * AppleDouble file to see whether there was already Finder Info (so we
1744 * can return an error if needed); this case is handled further below.
1746 * NOTE: this copies the Finder Info data into the "finfo" local.
1748 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1750 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1751 * That means we probably have to open_xattrfile and get_xattrinfo.
1753 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1756 error
= uiomove(finfo
, datalen
, uio
);
1759 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1760 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1761 error
= default_removexattr(vp
, name
, 0, context
);
1762 if (error
== ENOATTR
)
1770 * Open the file locked since setting an attribute
1771 * can change the layout of the Apple Double file.
1773 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1774 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1777 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1778 close_xattrfile(xvp
, fileflags
, context
);
1782 /* Set the Finder Info. */
1783 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1784 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1785 /* attr exists and "create" was specified? */
1786 if (options
& XATTR_CREATE
) {
1791 /* attr doesn't exists and "replace" was specified? */
1792 if (options
& XATTR_REPLACE
) {
1797 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1799 * Setting the Finder Info to all zeroes is equivalent to
1800 * removing it. Close the xattr file and let
1801 * default_removexattr do the work (including deleting
1802 * the xattr file if there are no other xattrs).
1804 * Note that we have to handle the case where the
1805 * Finder Info was already all zeroes, and we ignore
1808 * The common case where options == 0 was handled above.
1810 rel_xattrinfo(&ainfo
);
1811 close_xattrfile(xvp
, fileflags
, context
);
1812 error
= default_removexattr(vp
, name
, 0, context
);
1813 if (error
== ENOATTR
)
1817 if (ainfo
.finderinfo
) {
1818 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1819 bcopy(finfo
, attrdata
, datalen
);
1820 ainfo
.iosize
= sizeof(attr_header_t
);
1821 error
= write_xattrinfo(&ainfo
);
1828 /* Write the Resource Fork. */
1829 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1830 u_int32_t endoffset
;
1832 if (!vnode_isreg(vp
)) {
1836 /* Make sure we have a rsrc fork pointer.. */
1837 if (ainfo
.rsrcfork
== NULL
) {
1841 if (ainfo
.rsrcfork
) {
1842 if (ainfo
.rsrcfork
->length
!= 0) {
1843 if (options
& XATTR_CREATE
) {
1844 /* attr exists, and create specified ? */
1850 /* Zero length AD rsrc fork */
1851 if (options
& XATTR_REPLACE
) {
1852 /* attr doesn't exist (0-length), but replace specified ? */
1859 /* We can't do much if we somehow didn't get an AD rsrc pointer */
1864 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1865 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1866 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1869 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1870 if (endoffset
> ainfo
.rsrcfork
->length
) {
1871 ainfo
.rsrcfork
->length
= endoffset
;
1872 ainfo
.iosize
= sizeof(attr_header_t
);
1873 error
= write_xattrinfo(&ainfo
);
1879 if (datalen
> ATTR_MAX_SIZE
) {
1880 return (E2BIG
); /* EINVAL instead ? */
1883 if (ainfo
.attrhdr
== NULL
) {
1887 header
= ainfo
.attrhdr
;
1888 entry
= ainfo
.attr_entry
;
1890 /* Check if data area crosses the maximum header size. */
1891 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1892 splitdata
= 1; /* do data I/O separately */
1897 * See if attribute already exists.
1899 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1900 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1904 entry
= ATTR_NEXT(entry
);
1908 if (options
& XATTR_CREATE
) {
1912 if (datalen
== entry
->length
) {
1914 uio_setoffset(uio
, entry
->offset
);
1915 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1916 uio_setoffset(uio
, 0);
1918 printf("setxattr: VNOP_WRITE error %d\n", error
);
1921 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1922 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1925 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1926 error
= write_xattrinfo(&ainfo
);
1928 printf("setxattr: write_xattrinfo error %d\n", error
);
1934 * Brute force approach - just remove old entry and set new entry.
1937 rel_xattrinfo(&ainfo
);
1938 close_xattrfile(xvp
, fileflags
, context
);
1939 error
= default_removexattr(vp
, name
, options
, context
);
1943 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1944 options
&= ~XATTR_REPLACE
;
1945 goto start
; /* start over */
1950 if (options
& XATTR_REPLACE
) {
1951 error
= ENOATTR
; /* nothing there to replace */
1954 /* Check if header size limit has been reached. */
1955 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1960 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1962 /* Check if we need more space. */
1963 if ((datalen
+ entrylen
) > datafreespace
) {
1966 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1968 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1969 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1970 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1973 ainfo
.filesize
+= growsize
;
1974 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1976 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1982 * Move the resource fork out of the way.
1984 if (ainfo
.rsrcfork
) {
1985 if (ainfo
.rsrcfork
->length
!= 0) {
1986 shift_data_down(xvp
,
1987 ainfo
.rsrcfork
->offset
,
1988 ainfo
.rsrcfork
->length
,
1991 ainfo
.rsrcfork
->offset
+= growsize
;
1993 ainfo
.finderinfo
->length
+= growsize
;
1994 header
->total_size
+= growsize
;
1997 /* Make space for a new entry. */
1999 shift_data_down(xvp
,
2001 header
->data_length
,
2004 bcopy((u_int8_t
*)header
+ header
->data_start
,
2005 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
2006 header
->data_length
);
2008 header
->data_start
+= entrylen
;
2010 /* Fix up entry data offsets. */
2012 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
2013 entry
->offset
+= entrylen
;
2017 * If the attribute data area is entirely within
2018 * the header buffer, then just update the buffer,
2019 * otherwise we'll write it separately to the file.
2024 /* Write new attribute data after the end of existing data. */
2025 offset
= header
->data_start
+ header
->data_length
;
2026 uio_setoffset(uio
, offset
);
2027 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
2028 uio_setoffset(uio
, 0);
2030 printf("setxattr: VNOP_WRITE error %d\n", error
);
2034 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
2036 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
2038 printf("setxattr: uiomove error %d\n", error
);
2043 /* Create the attribute entry. */
2044 lastentry
->length
= datalen
;
2045 lastentry
->offset
= header
->data_start
+ header
->data_length
;
2046 lastentry
->namelen
= namelen
;
2047 lastentry
->flags
= 0;
2048 bcopy(name
, &lastentry
->name
[0], namelen
);
2050 /* Update the attributes header. */
2051 header
->num_attrs
++;
2052 header
->data_length
+= datalen
;
2055 /* Only write the entries, since the data was written separately. */
2056 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
2058 /* The entry and data are both in the header; write them together. */
2059 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2061 error
= write_xattrinfo(&ainfo
);
2063 printf("setxattr: write_xattrinfo error %d\n", error
);
2067 rel_xattrinfo(&ainfo
);
2068 close_xattrfile(xvp
, fileflags
, context
);
2070 /* Touch the change time if we changed an attribute. */
2072 struct vnode_attr va
;
2074 /* Re-write the mtime to cause a ctime change. */
2076 VATTR_WANTED(&va
, va_modify_time
);
2077 if (vnode_getattr(vp
, &va
, context
) == 0) {
2079 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2080 (void) vnode_setattr(vp
, &va
, context
);
2084 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2091 * Remove an extended attribute.
2094 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
2098 attr_header_t
*header
;
2099 attr_entry_t
*entry
;
2100 attr_entry_t
*oldslot
;
2106 int found
= 0, lastone
= 0;
2114 fileflags
= FREAD
| FWRITE
;
2115 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
2118 * Open the file locked (exclusive) since the Carbon
2119 * File Manager may have the Apple Double file open
2120 * and could be changing the resource fork.
2122 fileflags
|= O_EXLOCK
;
2127 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
2130 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2131 close_xattrfile(xvp
, fileflags
, context
);
2135 attrcount
+= ainfo
.attrhdr
->num_attrs
;
2138 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
2141 /* Clear the Finder Info. */
2142 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
2143 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
2147 /* On removal of last attribute the ._ file is removed. */
2148 if (--attrcount
== 0)
2150 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
2151 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
2152 ainfo
.iosize
= sizeof(attr_header_t
);
2153 error
= write_xattrinfo(&ainfo
);
2157 /* Clear the Resource Fork. */
2159 if (!vnode_isreg(vp
)) {
2163 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
2167 /* On removal of last attribute the ._ file is removed. */
2168 if (--attrcount
== 0)
2172 * If the resource fork isn't the last AppleDouble
2173 * entry then the space needs to be reclaimed by
2174 * shifting the entries after the resource fork.
2176 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
2177 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
2178 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2181 ainfo
.rsrcfork
->length
= 0;
2182 ainfo
.iosize
= sizeof(attr_header_t
);
2183 error
= write_xattrinfo(&ainfo
);
2188 if (ainfo
.attrhdr
== NULL
) {
2192 namelen
= strlen(name
) + 1;
2193 header
= ainfo
.attrhdr
;
2194 entry
= ainfo
.attr_entry
;
2197 * See if this attribute exists.
2199 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2200 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2202 if ((i
+1) == header
->num_attrs
)
2206 entry
= ATTR_NEXT(entry
);
2212 /* On removal of last attribute the ._ file is removed. */
2213 if (--attrcount
== 0)
2216 datalen
= entry
->length
;
2217 dataoff
= entry
->offset
;
2218 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2219 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
2224 /* Remove the attribute entry. */
2226 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2227 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2230 /* Adjust the attribute data. */
2234 dataoff
- header
->data_start
,
2240 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2244 /* XXX write zeros to freed space ? */
2245 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2249 bcopy((u_int8_t
*)header
+ header
->data_start
,
2250 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2251 dataoff
- header
->data_start
);
2253 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2254 (u_int8_t
*)header
+ dataoff
- entrylen
,
2255 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2257 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2258 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2261 /* Adjust the header values and entry offsets. */
2262 header
->num_attrs
--;
2263 header
->data_start
-= entrylen
;
2264 header
->data_length
-= datalen
;
2267 entry
= ainfo
.attr_entry
;
2268 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2269 entry
->offset
-= entrylen
;
2270 if (entry
>= oldslot
)
2271 entry
->offset
-= datalen
;
2272 entry
= ATTR_NEXT(entry
);
2274 error
= write_xattrinfo(&ainfo
);
2276 printf("removexattr: write_xattrinfo error %d\n", error
);
2279 rel_xattrinfo(&ainfo
);
2281 /* When there are no more attributes remove the ._ file. */
2282 if (attrcount
== 0) {
2283 if (fileflags
& O_EXLOCK
)
2284 (void) unlock_xattrfile(xvp
, context
);
2285 VNOP_CLOSE(xvp
, fileflags
, context
);
2287 error
= remove_xattrfile(xvp
, context
);
2290 close_xattrfile(xvp
, fileflags
, context
);
2292 /* Touch the change time if we changed an attribute. */
2294 struct vnode_attr va
;
2296 /* Re-write the mtime to cause a ctime change. */
2298 VATTR_WANTED(&va
, va_modify_time
);
2299 if (vnode_getattr(vp
, &va
, context
) == 0) {
2301 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2302 (void) vnode_setattr(vp
, &va
, context
);
2306 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2314 * Retrieve the list of extended attribute names.
2317 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2321 attr_entry_t
*entry
;
2326 * We do not zero "*size" here as we don't want to stomp a size set when
2327 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2328 * system call layer, up in listxattr or flistxattr.
2331 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2332 if (error
== ENOATTR
)
2336 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2337 if (error
== ENOATTR
)
2339 close_xattrfile(xvp
, FREAD
, context
);
2343 /* Check for Finder Info. */
2344 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2346 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2347 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2351 error
= uiomove(XATTR_FINDERINFO_NAME
,
2352 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2360 /* Check for Resource Fork. */
2361 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2363 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2364 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2368 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2369 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2377 /* Check for attributes. */
2378 if (ainfo
.attrhdr
) {
2379 count
= ainfo
.attrhdr
->num_attrs
;
2380 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2381 if (xattr_protected((const char *)entry
->name
) ||
2382 xattr_validatename((const char *)entry
->name
) != 0) {
2383 entry
= ATTR_NEXT(entry
);
2387 *size
+= entry
->namelen
;
2388 entry
= ATTR_NEXT(entry
);
2391 if (uio_resid(uio
) < entry
->namelen
) {
2395 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2397 if (error
!= EFAULT
)
2401 entry
= ATTR_NEXT(entry
);
2405 rel_xattrinfo(&ainfo
);
2406 close_xattrfile(xvp
, FREAD
, context
);
2412 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2414 vnode_t xvp
= NULLVP
;
2415 vnode_t dvp
= NULLVP
;
2416 struct vnode_attr va
;
2417 struct nameidata nd
;
2419 char *filename
= NULL
;
2420 const char *basename
= NULL
;
2426 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2428 * For the root directory use "._." to hold the attributes.
2430 filename
= &smallname
[0];
2431 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2432 dvp
= vp
; /* the "._." file resides in the root dir */
2435 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2439 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2444 /* "._" Attribute files cannot have attributes */
2445 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2446 basename
[0] == '.' && basename
[1] == '_') {
2450 filename
= &smallname
[0];
2451 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2452 if (len
>= sizeof(smallname
)) {
2453 len
++; /* snprintf result doesn't include '\0' */
2454 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2455 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2458 * Note that the lookup here does not authorize. Since we are looking
2459 * up in the same directory that we already have the file vnode in,
2460 * we must have been given the file vnode legitimately. Read/write
2461 * access has already been authorized in layers above for calls from
2462 * userspace, and the authorization code using this path to read
2463 * file security from the EA must always get access
2466 NDINIT(&nd
, LOOKUP
, OP_OPEN
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
,
2467 UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), context
);
2470 if (fileflags
& O_CREAT
) {
2471 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2476 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2478 if ( (error
= namei(&nd
))) {
2483 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2489 * Pick up uid/gid/mode from target file.
2492 VATTR_WANTED(&va
, va_uid
);
2493 VATTR_WANTED(&va
, va_gid
);
2494 VATTR_WANTED(&va
, va_mode
);
2495 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2496 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2497 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2498 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2501 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2502 } else /* fallback values */ {
2503 uid
= KAUTH_UID_NONE
;
2504 gid
= KAUTH_GID_NONE
;
2505 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2509 VATTR_SET(&va
, va_type
, VREG
);
2510 VATTR_SET(&va
, va_mode
, umode
);
2511 if (uid
!= KAUTH_UID_NONE
)
2512 VATTR_SET(&va
, va_uid
, uid
);
2513 if (gid
!= KAUTH_GID_NONE
)
2514 VATTR_SET(&va
, va_gid
, gid
);
2516 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
, &va
,
2517 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2527 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2532 if ((error
= namei(&nd
))) {
2542 if (xvp
->v_type
!= VREG
) {
2547 * Owners must match.
2550 VATTR_WANTED(&va
, va_uid
);
2551 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2552 uid_t owner
= va
.va_uid
;
2555 VATTR_WANTED(&va
, va_uid
);
2556 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2557 error
= ENOATTR
; /* don't use this "._" file */
2562 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2568 if ((error
= vnode_ref(xvp
))) {
2573 /* If create was requested, make sure file header exists. */
2574 if (fileflags
& O_CREAT
) {
2576 VATTR_WANTED(&va
, va_data_size
);
2577 VATTR_WANTED(&va
, va_fileid
);
2578 VATTR_WANTED(&va
, va_nlink
);
2579 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2584 /* If the file is empty then add a default header. */
2585 if (va
.va_data_size
== 0) {
2586 /* Don't adopt hard-linked "._" files. */
2587 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2591 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2595 /* Apply file locking if requested. */
2596 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2599 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2600 error
= lock_xattrfile(xvp
, locktype
, context
);
2606 if (xvp
!= NULLVP
) {
2608 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2611 if (fileflags
& O_CREAT
) {
2612 /* Delete the xattr file if we encountered any errors */
2613 (void) remove_xattrfile (xvp
, context
);
2617 (void) vnode_rele(xvp
);
2619 (void) vnode_put(xvp
);
2622 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2626 /* Release resources after error-handling */
2627 if (dvp
&& (dvp
!= vp
)) {
2631 vnode_putname(basename
);
2633 if (filename
&& filename
!= &smallname
[0]) {
2634 FREE(filename
, M_TEMP
);
2637 *xvpp
= xvp
; /* return a referenced vnode */
2642 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2644 // if (fileflags & FWRITE)
2645 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2647 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2648 (void) unlock_xattrfile(xvp
, context
);
2650 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2651 (void) vnode_rele(xvp
);
2652 (void) vnode_put(xvp
);
2656 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2659 struct nameidata nd
;
2664 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2668 pathlen
= MAXPATHLEN
;
2669 error
= vn_getpath(xvp
, path
, &pathlen
);
2671 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2675 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2676 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2678 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2685 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2694 * Read in and parse the AppleDouble header and entries, and the extended
2695 * attribute header and entries if any. Populates the fields of ainfop
2696 * based on the headers and entries found.
2698 * The basic idea is to:
2699 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2700 * AppleDouble entries, the extended attribute header, and extended
2701 * attribute entries must lie within this part of the file; the rest of
2702 * the AppleDouble handling code assumes this. Plus it allows us to
2703 * somewhat optimize by doing a smaller number of larger I/Os.
2704 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2706 * - Find the Finder Info and Resource Fork entries, if any.
2707 * - If we're going to be writing, try to make sure the Finder Info entry has
2708 * room to store the extended attribute header, plus some space for extended
2710 * - Swap and sanity check the extended attribute header and entries (if any).
2713 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2716 void * buffer
= NULL
;
2717 apple_double_header_t
*filehdr
;
2718 struct vnode_attr va
;
2723 bzero(ainfop
, sizeof(attr_info_t
));
2724 ainfop
->filevp
= xvp
;
2725 ainfop
->context
= context
;
2727 VATTR_WANTED(&va
, va_data_size
);
2728 VATTR_WANTED(&va
, va_fileid
);
2729 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2732 ainfop
->filesize
= va
.va_data_size
;
2734 /* When setting attributes, allow room for the header to grow. */
2736 iosize
= ATTR_MAX_HDR_SIZE
;
2738 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2744 ainfop
->iosize
= iosize
;
2745 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2746 if (buffer
== NULL
){
2751 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2752 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2754 /* Read the file header. */
2755 error
= VNOP_READ(xvp
, auio
, 0, context
);
2759 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2760 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2762 filehdr
= (apple_double_header_t
*)buffer
;
2764 error
= check_and_swap_apple_double_header(ainfop
);
2768 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2770 /* rel_xattrinfo is responsible for freeing the header buffer */
2773 /* Find the Finder Info and Resource Fork entries, if any */
2774 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2775 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2776 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2777 /* We found the Finder Info entry. */
2778 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2781 * Is the Finder Info "empty" (all zeroes)? If so,
2782 * we'll pretend like the Finder Info extended attribute
2785 * Note: we have to make sure the Finder Info is
2786 * contained within the buffer we have already read,
2787 * to avoid accidentally accessing a bogus address.
2788 * If it is outside the buffer, we just assume the
2789 * Finder Info is non-empty.
2791 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2792 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2793 ainfop
->emptyfinderinfo
= 1;
2796 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2798 * Ignore zero-length resource forks when getting. If setting,
2799 * we need to remember the resource fork entry so it can be
2800 * updated once the new content has been written.
2802 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2806 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2808 * The "empty" resource headers we created have a system data tag of:
2809 * "This resource fork intentionally left blank "
2811 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2813 u_int8_t systemData
[64];
2817 /* Read the system data which starts at byte 16 */
2818 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2819 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2820 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2821 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2825 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2826 continue; /* skip this resource fork */
2829 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2830 if (i
!= (filehdr
->numEntries
- 1)) {
2831 printf("get_xattrinfo: resource fork not last entry\n");
2832 ainfop
->readonly
= 1;
2839 * See if this file looks like it is laid out correctly to contain
2840 * extended attributes. If so, then do the following:
2842 * - If we're going to be writing, try to make sure the Finder Info
2843 * entry has room to store the extended attribute header, plus some
2844 * space for extended attributes.
2846 * - Swap and sanity check the extended attribute header and entries
2849 if (filehdr
->numEntries
== 2 &&
2850 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2851 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2852 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2853 attr_header_t
*attrhdr
;
2854 attrhdr
= (attr_header_t
*)filehdr
;
2856 * If we're going to be writing, try to make sure the Finder
2857 * Info entry has room to store the extended attribute header,
2858 * plus some space for extended attributes.
2860 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2864 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2865 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2866 /* Make some room before existing resource fork. */
2867 shift_data_down(xvp
,
2868 filehdr
->entries
[1].offset
,
2869 filehdr
->entries
[1].length
,
2871 writesize
= sizeof(attr_header_t
);
2873 /* Create a new, empty resource fork. */
2874 rsrcfork_header_t
*rsrcforkhdr
;
2876 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2878 /* Steal some space for an empty RF header. */
2879 delta
-= sizeof(rsrcfork_header_t
);
2881 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2882 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2884 /* Fill in Empty Resource Fork Header. */
2885 init_empty_resource_fork(rsrcforkhdr
);
2887 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2888 writesize
= ATTR_BUF_SIZE
;
2890 filehdr
->entries
[0].length
+= delta
;
2891 filehdr
->entries
[1].offset
+= delta
;
2893 /* Fill in Attribute Header. */
2894 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2895 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2896 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2897 attrhdr
->data_start
= sizeof(attr_header_t
);
2898 attrhdr
->data_length
= 0;
2899 attrhdr
->reserved
[0] = 0;
2900 attrhdr
->reserved
[1] = 0;
2901 attrhdr
->reserved
[2] = 0;
2903 attrhdr
->num_attrs
= 0;
2905 /* Push out new header */
2906 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2907 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2909 swap_adhdr(filehdr
); /* to big endian */
2910 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2911 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2912 swap_adhdr(filehdr
); /* back to native */
2913 /* The attribute header gets swapped below. */
2917 * Swap and sanity check the extended attribute header and
2918 * entries (if any). The Finder Info content must be big enough
2919 * to include the extended attribute header; if not, we just
2922 * Note that we're passing the offset + length (i.e. the end)
2923 * of the Finder Info instead of rawsize to validate_attrhdr.
2924 * This ensures that all extended attributes lie within the
2925 * Finder Info content according to the AppleDouble entry.
2927 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2930 if (ainfop
->finderinfo
&&
2931 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2932 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2933 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2935 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2936 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2937 /* First attr_entry starts immediately following attribute header */
2938 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2947 FREE(buffer
, M_TEMP
);
2953 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2956 rsrcfork_header_t
*rsrcforkhdr
;
2962 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2963 bzero(buffer
, ATTR_BUF_SIZE
);
2965 xah
= (attr_header_t
*)buffer
;
2966 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2967 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2968 rsrcforksize
= sizeof(rsrcfork_header_t
);
2969 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2971 /* Fill in Apple Double Header. */
2972 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2973 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2974 xah
->appledouble
.numEntries
= SWAP16 (2);
2975 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2976 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2977 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2978 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2979 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2980 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2981 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2983 /* Fill in Attribute Header. */
2984 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2985 xah
->debug_tag
= SWAP32 (fileid
);
2986 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2987 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2989 /* Fill in Empty Resource Fork Header. */
2990 init_empty_resource_fork(rsrcforkhdr
);
2993 error
= VNOP_WRITE(xvp
, auio
, IO_UNIT
, context
);
2995 /* Did we write out the full uio? */
2996 if (uio_resid(auio
) > 0) {
3001 FREE(buffer
, M_TEMP
);
3007 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
3009 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
3010 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3011 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3012 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
3013 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3014 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
3015 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
3016 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
3017 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
3018 rsrcforkhdr
->typeCount
= SWAP16 (-1);
3019 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
3023 rel_xattrinfo(attr_info_t
*ainfop
)
3025 FREE(ainfop
->filehdr
, M_TEMP
);
3026 bzero(ainfop
, sizeof(attr_info_t
));
3030 write_xattrinfo(attr_info_t
*ainfop
)
3035 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3036 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
3038 swap_adhdr(ainfop
->filehdr
);
3039 if (ainfop
->attrhdr
!= NULL
) {
3040 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3043 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
3045 swap_adhdr(ainfop
->filehdr
);
3046 if (ainfop
->attrhdr
!= NULL
) {
3047 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3054 #if BYTE_ORDER == LITTLE_ENDIAN
3056 * Endian swap apple double header
3059 swap_adhdr(apple_double_header_t
*adh
)
3064 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
3066 adh
->magic
= SWAP32 (adh
->magic
);
3067 adh
->version
= SWAP32 (adh
->version
);
3068 adh
->numEntries
= SWAP16 (adh
->numEntries
);
3070 for (i
= 0; i
< count
; i
++) {
3071 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
3072 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
3073 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
3078 * Endian swap extended attributes header
3081 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
3087 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
3089 ah
->magic
= SWAP32 (ah
->magic
);
3090 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3091 ah
->total_size
= SWAP32 (ah
->total_size
);
3092 ah
->data_start
= SWAP32 (ah
->data_start
);
3093 ah
->data_length
= SWAP32 (ah
->data_length
);
3094 ah
->flags
= SWAP16 (ah
->flags
);
3095 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3097 ae
= (attr_entry_t
*)(&ah
[1]);
3098 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
3099 ae
->offset
= SWAP32 (ae
->offset
);
3100 ae
->length
= SWAP32 (ae
->length
);
3101 ae
->flags
= SWAP16 (ae
->flags
);
3107 * Validate and swap the attributes header contents, and each attribute's
3110 * Note: Assumes the caller has verified that the Finder Info content is large
3111 * enough to contain the attr_header structure itself. Therefore, we can
3112 * swap the header fields before sanity checking them.
3115 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
3126 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
3129 /* Swap the basic header fields */
3130 ah
->magic
= SWAP32(ah
->magic
);
3131 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3132 ah
->total_size
= SWAP32 (ah
->total_size
);
3133 ah
->data_start
= SWAP32 (ah
->data_start
);
3134 ah
->data_length
= SWAP32 (ah
->data_length
);
3135 ah
->flags
= SWAP16 (ah
->flags
);
3136 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3139 * Make sure the total_size fits within the Finder Info area, and the
3140 * extended attribute data area fits within total_size.
3142 end
= ah
->data_start
+ ah
->data_length
;
3143 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3144 end
< ah
->data_start
||
3145 end
> ah
->total_size
) {
3150 * Make sure each of the attr_entry_t's fits within total_size.
3152 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3153 count
= ah
->num_attrs
;
3154 ae
= (attr_entry_t
*)(&ah
[1]);
3156 for (i
=0; i
<count
; i
++) {
3157 /* Make sure the fixed-size part of this attr_entry_t fits. */
3158 if ((u_int8_t
*) &ae
[1] > buf_end
)
3161 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3162 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3163 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
3166 /* Swap the attribute entry fields */
3167 ae
->offset
= SWAP32(ae
->offset
);
3168 ae
->length
= SWAP32(ae
->length
);
3169 ae
->flags
= SWAP16(ae
->flags
);
3171 /* Make sure the attribute content fits. */
3172 end
= ae
->offset
+ ae
->length
;
3173 if (end
< ae
->offset
|| end
> ah
->total_size
)
3180 * TODO: Make sure the contents of attributes don't overlap the header
3181 * and don't overlap each other. The hard part is that we don't know
3182 * what the actual header size is until we have looped over all of the
3183 * variable-sized attribute entries.
3185 * XXX Is there any guarantee that attribute entries are stored in
3186 * XXX order sorted by the contents' file offset? If so, that would
3187 * XXX make the pairwise overlap check much easier.
3194 // "start" & "end" are byte offsets in the file.
3195 // "to" is the byte offset we want to move the
3196 // data to. "to" should be > "start".
3198 // we do the copy backwards to avoid problems if
3199 // there's an overlap.
3202 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3205 size_t chunk
, orig_chunk
;
3208 kauth_cred_t ucred
= vfs_context_ucred(context
);
3209 proc_t p
= vfs_context_proc(context
);
3211 if (delta
== 0 || len
== 0) {
3221 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3225 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
3226 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3228 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3229 pos
, ret
, chunk
, ret
);
3233 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3235 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3236 pos
+delta
, ret
, chunk
, ret
);
3240 if ((pos
- (off_t
)chunk
) < start
) {
3241 chunk
= pos
- start
;
3243 if (chunk
== 0) { // we're all done
3248 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3255 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3258 size_t chunk
, orig_chunk
;
3262 kauth_cred_t ucred
= vfs_context_ucred(context
);
3263 proc_t p
= vfs_context_proc(context
);
3265 if (delta
== 0 || len
== 0) {
3276 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3280 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3281 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3283 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3284 pos
, ret
, chunk
, ret
);
3288 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3290 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3291 pos
+delta
, ret
, chunk
, ret
);
3295 if ((pos
+ (off_t
)chunk
) > end
) {
3298 if (chunk
== 0) { // we're all done
3303 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3309 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3314 lf
.l_whence
= SEEK_SET
;
3317 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3318 /* Note: id is just a kernel address that's not a proc */
3319 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
, NULL
);
3320 return (error
== ENOTSUP
? 0 : error
);
3324 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3329 lf
.l_whence
= SEEK_SET
;
3332 lf
.l_type
= F_UNLCK
;
3333 /* Note: id is just a kernel address that's not a proc */
3334 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
, NULL
);
3335 return (error
== ENOTSUP
? 0 : error
);
3338 #else /* CONFIG_APPLEDOUBLE */
3341 #define panic printf
3344 default_getxattr(vnode_t vp
, const char *name
,
3345 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3346 __unused vfs_context_t context
)
3348 #if PANIC_ON_NOAPPLEDOUBLE
3349 panic("%s: no AppleDouble support, vp %p name %s", __func__
, vp
, name
);
3355 default_setxattr(vnode_t vp
, const char *name
,
3356 __unused uio_t uio
, __unused
int options
, __unused vfs_context_t context
)
3358 #if PANIC_ON_NOAPPLEDOUBLE
3359 panic("%s: no AppleDouble support, vp %p name %s", __func__
, vp
, name
);
3365 default_listxattr(vnode_t vp
,
3366 __unused uio_t uio
, __unused
size_t *size
, __unused
int options
,
3367 __unused vfs_context_t context
)
3369 #if PANIC_ON_NOAPPLEDOUBLE
3370 panic("%s: no AppleDouble support, vp %p name %s", __func__
, vp
, ".");
3376 default_removexattr(vnode_t vp
, const char *name
,
3377 __unused
int options
, __unused vfs_context_t context
)
3379 #if PANIC_ON_NOAPPLEDOUBLE
3380 panic("%s: no AppleDouble support, vp %p name %s", __func__
, vp
, name
);
3385 #endif /* CONFIG_APPLEDOUBLE */