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)
89 static int default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
);
91 static int default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
);
93 static int default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
);
95 static int getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
, int *creator
, vfs_context_t context
);
97 static int get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
);
103 * Default xattr support routines.
106 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
107 vfs_context_t context
);
112 * Retrieve the data of an extended attribute.
115 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
116 int options
, vfs_context_t context
)
120 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
124 /* getxattr calls are not allowed for streams. */
125 if (vp
->v_flag
& VISNAMEDSTREAM
) {
131 * Non-kernel request need extra checks performed.
133 * The XATTR_NOSECURITY flag implies a kernel request.
135 if (!(options
& XATTR_NOSECURITY
)) {
137 error
= mac_vnode_check_getextattr(context
, vp
, name
, uio
);
141 if ((error
= xattr_validatename(name
))) {
144 if ((error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
))) {
147 /* The offset can only be non-zero for resource forks. */
148 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
149 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
155 /* The offset can only be non-zero for resource forks. */
156 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
157 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
162 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
163 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
165 * A filesystem may keep some EAs natively and return ENOTSUP for others.
166 * SMB returns ENOTSUP for finderinfo and resource forks.
168 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
175 * Set the data of an extended attribute.
178 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
182 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
186 /* setxattr calls are not allowed for streams. */
187 if (vp
->v_flag
& VISNAMEDSTREAM
) {
192 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
195 if ((error
= xattr_validatename(name
))) {
198 if (!(options
& XATTR_NOSECURITY
)) {
200 error
= mac_vnode_check_setextattr(context
, vp
, name
, uio
);
204 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
208 /* The offset can only be non-zero for resource forks. */
209 if (uio_offset(uio
) != 0 &&
210 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0 ) {
215 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
218 * An EJUSTRETURN is from a filesystem which keeps this xattr
219 * natively as well as in a dot-underscore file. In this case the
220 * EJUSTRETURN means the filesytem has done nothing, but identifies the
221 * EA as one which may be represented natively and/or in a DU, and
222 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
223 * in vn_setxattr can we do the getxattrs needed to ascertain whether
224 * the XATTR_{CREATE,REPLACE} should yield an error.
226 if (error
== EJUSTRETURN
) {
227 int native
= 0, dufile
= 0;
228 size_t sz
; /* not used */
230 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
231 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
232 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
236 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
241 * Having determined no CREATE/REPLACE error should result, we
242 * zero those bits, so both backing stores get written to.
244 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
245 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
246 /* the mainline path here is to have error==ENOTSUP ... */
248 #endif /* DUAL_EAS */
249 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
251 * A filesystem may keep some EAs natively and return ENOTSUP for others.
252 * SMB returns ENOTSUP for finderinfo and resource forks.
254 error
= default_setxattr(vp
, name
, uio
, options
, context
);
257 if ((error
== 0) && !(options
& XATTR_NOSECURITY
) &&
258 (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
))
259 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
266 * Remove an extended attribute.
269 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
273 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
277 /* removexattr calls are not allowed for streams. */
278 if (vp
->v_flag
& VISNAMEDSTREAM
) {
283 if ((error
= xattr_validatename(name
))) {
286 if (!(options
& XATTR_NOSECURITY
)) {
288 error
= mac_vnode_check_deleteextattr(context
, vp
, name
);
292 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
296 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
297 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
299 * A filesystem may keep some EAs natively and return ENOTSUP for others.
300 * SMB returns ENOTSUP for finderinfo and resource forks.
302 error
= default_removexattr(vp
, name
, options
, context
);
304 } else if (error
== EJUSTRETURN
) {
306 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
307 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
308 * a native xattr, so failure to find it in a DU file during
309 * default_removexattr should not be considered an error.
311 error
= default_removexattr(vp
, name
, options
, context
);
312 if (error
== ENOATTR
)
314 #endif /* DUAL_EAS */
317 if ((error
== 0) && !(options
& XATTR_NOSECURITY
) &&
318 (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
))
319 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
326 * Retrieve the list of extended attribute names.
329 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
333 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
337 /* listxattr calls are not allowed for streams. */
338 if (vp
->v_flag
& VISNAMEDSTREAM
) {
343 if (!(options
& XATTR_NOSECURITY
)) {
345 error
= mac_vnode_check_listextattr(context
, vp
);
350 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
);
355 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
356 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
358 * A filesystem may keep some but not all EAs natively, in which case
359 * the native EA names will have been uiomove-d out (or *size updated)
360 * and the default_listxattr here will finish the job. Note SMB takes
361 * advantage of this for its finder-info and resource forks.
363 error
= default_listxattr(vp
, uio
, size
, options
, context
);
370 xattr_validatename(const char *name
)
374 if (name
== NULL
|| name
[0] == '\0') {
377 namelen
= strnlen(name
, XATTR_MAXNAMELEN
);
378 if (name
[namelen
] != '\0')
379 return (ENAMETOOLONG
);
381 if (utf8_validatestr((const unsigned char *)name
, namelen
) != 0)
389 * Determine whether an EA is a protected system attribute.
392 xattr_protected(const char *attrname
)
394 return(!strncmp(attrname
, "com.apple.system.", 17));
400 * Obtain a named stream from vnode vp.
403 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
407 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
408 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
410 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
413 uint32_t streamflags
= VISNAMEDSTREAM
;
416 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
417 streamflags
|= VISSHADOW
;
421 vnode_lock_spin(svp
);
422 svp
->v_flag
|= streamflags
;
425 /* Tag the parent so we know to flush credentials for streams on setattr */
427 vp
->v_lflag
|= VL_HASSTREAMS
;
430 /* Make the file it's parent.
431 * Note: This parent link helps us distinguish vnodes for
432 * shadow stream files from vnodes for resource fork on file
433 * systems that support namedstream natively (both have
434 * VISNAMEDSTREAM set) by allowing access to mount structure
435 * for checking MNTK_NAMED_STREAMS bit at many places in the
438 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
445 * Make a named stream for vnode vp.
448 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
452 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
453 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
455 error
= default_makenamedstream(vp
, svpp
, name
, context
);
458 uint32_t streamflags
= VISNAMEDSTREAM
;
462 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
463 streamflags
|= VISSHADOW
;
467 vnode_lock_spin(svp
);
468 svp
->v_flag
|= streamflags
;
471 /* Tag the parent so we know to flush credentials for streams on setattr */
473 vp
->v_lflag
|= VL_HASSTREAMS
;
476 /* Make the file it's parent.
477 * Note: This parent link helps us distinguish vnodes for
478 * shadow stream files from vnodes for resource fork on file
479 * systems that support namedstream natively (both have
480 * VISNAMEDSTREAM set) by allowing access to mount structure
481 * for checking MNTK_NAMED_STREAMS bit at many places in the
484 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
490 * Remove a named stream from vnode vp.
493 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
497 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
498 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
500 error
= default_removenamedstream(vp
, name
, context
);
505 #define NS_IOBUFSIZE (128 * 1024)
508 * Release a named stream shadow file.
510 * Note: This function is called from two places where we do not need
511 * to check if the vnode has any references held before deleting the
512 * shadow file. Once from vclean() when the vnode is being reclaimed
513 * and we do not hold any references on the vnode. Second time from
514 * default_getnamedstream() when we get an error during shadow stream
515 * file initialization so that other processes who are waiting for the
516 * shadow stream file initialization by the creator will get opportunity
517 * to create and initialize the file again.
520 vnode_relenamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
523 struct componentname cn
;
530 MAKE_SHADOW_NAME(vp
, tmpname
);
533 cn
.cn_nameiop
= DELETE
;
534 cn
.cn_flags
= ISLASTCN
;
535 cn
.cn_context
= context
;
536 cn
.cn_pnbuf
= tmpname
;
537 cn
.cn_pnlen
= sizeof(tmpname
);
538 cn
.cn_nameptr
= cn
.cn_pnbuf
;
539 cn
.cn_namelen
= strlen(tmpname
);
541 /* Obtain the vnode for the shadow files directory. */
542 err
= get_shadow_dir(&dvp
, context
);
547 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, context
);
554 * Flush a named stream shadow file.
557 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
559 struct vnode_attr va
;
561 caddr_t bufptr
= NULL
;
569 VATTR_WANTED(&va
, va_data_size
);
570 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
571 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
574 datasize
= va
.va_data_size
;
576 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
580 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
581 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
584 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
588 * Copy the shadow stream file data into the resource fork.
590 error
= VNOP_OPEN(svp
, 0, context
);
592 printf("vnode_flushnamedstream: err %d opening file\n", error
);
595 while (offset
< datasize
) {
596 iosize
= MIN(datasize
- offset
, iosize
);
598 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
599 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
600 error
= VNOP_READ(svp
, auio
, 0, context
);
604 /* Since there's no truncate xattr we must remove the resource fork. */
606 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
607 if ((error
!= 0) && (error
!= ENOATTR
)) {
611 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
612 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
613 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
619 (void) VNOP_CLOSE(svp
, 0, context
);
622 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
631 * Verify that the vnode 'vp' is a vnode that lives in the shadow
632 * directory. We can't just query the parent pointer directly since
633 * the shadowfile is hooked up to the actual file it's a stream for.
635 errno_t
vnode_verifynamedstream(vnode_t vp
, vfs_context_t context
) {
637 struct vnode
*shadow_dvp
= NULL
;
638 struct vnode
*shadowfile
= NULL
;
639 struct componentname cn
;
643 /* Get the shadow directory vnode */
644 error
= get_shadow_dir(&shadow_dvp
, context
);
649 /* Re-generate the shadow name in the buffer */
650 MAKE_SHADOW_NAME (vp
, tmpname
);
652 /* Look up item in shadow dir */
653 bzero(&cn
, sizeof(cn
));
654 cn
.cn_nameiop
= LOOKUP
;
655 cn
.cn_flags
= ISLASTCN
| CN_ALLOWRSRCFORK
;
656 cn
.cn_context
= context
;
657 cn
.cn_pnbuf
= tmpname
;
658 cn
.cn_pnlen
= sizeof(tmpname
);
659 cn
.cn_nameptr
= cn
.cn_pnbuf
;
660 cn
.cn_namelen
= strlen(tmpname
);
662 if (VNOP_LOOKUP (shadow_dvp
, &shadowfile
, &cn
, context
) == 0) {
663 /* is the pointer the same? */
664 if (shadowfile
== vp
) {
670 /* drop the iocount acquired */
671 vnode_put (shadowfile
);
674 /* Drop iocount on shadow dir */
675 vnode_put (shadow_dvp
);
680 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
681 int *creator
, vfs_context_t context
)
683 vnode_t dvp
= NULLVP
;
684 vnode_t svp
= NULLVP
;
685 struct componentname cn
;
686 struct vnode_attr va
;
694 /* Establish a unique file name. */
695 MAKE_SHADOW_NAME(vp
, tmpname
);
696 bzero(&cn
, sizeof(cn
));
697 cn
.cn_nameiop
= LOOKUP
;
698 cn
.cn_flags
= ISLASTCN
;
699 cn
.cn_context
= context
;
700 cn
.cn_pnbuf
= tmpname
;
701 cn
.cn_pnlen
= sizeof(tmpname
);
702 cn
.cn_nameptr
= cn
.cn_pnbuf
;
703 cn
.cn_namelen
= strlen(tmpname
);
705 /* Pick up uid, gid, mode and date from original file. */
707 VATTR_WANTED(&va
, va_uid
);
708 VATTR_WANTED(&va
, va_gid
);
709 VATTR_WANTED(&va
, va_mode
);
710 VATTR_WANTED(&va
, va_create_time
);
711 VATTR_WANTED(&va
, va_modify_time
);
712 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
713 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
714 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
715 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
716 va
.va_uid
= KAUTH_UID_NONE
;
717 va
.va_gid
= KAUTH_GID_NONE
;
718 va
.va_mode
= S_IRUSR
| S_IWUSR
;
720 va
.va_vaflags
= VA_EXCLUSIVE
;
721 VATTR_SET(&va
, va_type
, VREG
);
722 /* We no longer change the access, but we still hide it. */
723 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
725 /* Obtain the vnode for the shadow files directory. */
726 if (get_shadow_dir(&dvp
, context
) != 0) {
731 /* See if someone else already has it open. */
732 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, context
) == 0) {
733 /* Double check existence by asking for size. */
735 VATTR_WANTED(&va
, va_data_size
);
736 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
737 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
738 goto out
; /* OK to use. */
742 /* Otherwise make sure the resource fork data exists. */
743 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
744 XATTR_NOSECURITY
, context
);
746 * To maintain binary compatibility with legacy Carbon
747 * emulated resource fork support, if the resource fork
748 * doesn't exist but the Finder Info does, then act as
749 * if an empty resource fork is present (see 4724359).
751 if ((error
== ENOATTR
) &&
752 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
753 XATTR_NOSECURITY
, context
) == 0)) {
761 /* If the resource fork exists, its size is expected to be non-zero. */
768 /* Create the shadow stream file. */
769 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, context
);
774 else if ((error
== EEXIST
) && !makestream
) {
775 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, context
);
777 else if ((error
== ENOENT
) && !makestream
) {
779 * We could have raced with a rmdir on the shadow directory
780 * post-lookup. Retry from the beginning, 1x only, to
781 * try and see if we need to re-create the shadow directory
796 /* Otherwise, just error out normally below */
804 /* On errors, clean up shadow stream file. */
812 *rsrcsize
= datasize
;
819 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
821 vnode_t svp
= NULLVP
;
823 caddr_t bufptr
= NULL
;
830 * Only the "com.apple.ResourceFork" stream is supported here.
832 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
838 * Obtain a shadow file for the resource fork I/O.
840 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
847 * The creator of the shadow file provides its file data,
848 * all other threads should wait until its ready. In order to
849 * prevent a deadlock during error codepaths, we need to check if the
850 * vnode is being created, or if it has failed out. Regardless of success or
851 * failure, we set the VISSHADOW bit on the vnode, so we check that
852 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
853 * then we can infer the creator isn't done yet. If it's there, but
854 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
859 if (svp
->v_flag
& VISNAMEDSTREAM
) {
860 /* data is ready, go use it */
864 /* It's not ready, wait for it (sleep using v_parent as channel) */
865 if ((svp
->v_flag
& VISSHADOW
)) {
867 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
868 * thread is done with this vnode. Just unlock the vnode and try again
873 /* Otherwise, sleep if the shadow file is not created yet */
874 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
875 "getnamedstream", NULL
);
884 * Copy the real resource fork data into shadow stream file.
886 if (op
== NS_OPEN
&& datasize
!= 0) {
890 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
891 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
896 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
899 error
= VNOP_OPEN(svp
, 0, context
);
903 while (offset
< datasize
) {
906 iosize
= MIN(datasize
- offset
, iosize
);
908 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
909 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
910 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
911 XATTR_NOSECURITY
, context
);
916 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
917 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
918 error
= VNOP_WRITE(svp
, auio
, 0, context
);
924 (void) VNOP_CLOSE(svp
, 0, context
);
927 /* Wake up anyone waiting for svp file content */
931 /* VISSHADOW would be set later on anyway, so we set it now */
932 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
933 wakeup((caddr_t
)&svp
->v_parent
);
936 /* On post create errors, get rid of the shadow file. This
937 * way if there is another process waiting for initialization
938 * of the shadowfile by the current process will wake up and
939 * retry by creating and initializing the shadow file again.
940 * Also add the VISSHADOW bit here to indicate we're done operating
943 (void)vnode_relenamedstream(vp
, svp
, context
);
945 svp
->v_flag
|= VISSHADOW
;
946 wakeup((caddr_t
)&svp
->v_parent
);
952 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
958 /* On errors, clean up shadow stream file. */
969 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
975 * Only the "com.apple.ResourceFork" stream is supported here.
977 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
981 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
984 * Wake up any waiters over in default_getnamedstream().
986 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
990 /* If we're the creator, mark it as a named stream */
991 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
992 /* Wakeup any waiters on the v_parent channel */
993 wakeup((caddr_t
)&svp
->v_parent
);
1002 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
1005 * Only the "com.apple.ResourceFork" stream is supported here.
1007 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
1011 * XXX - what about other opened instances?
1013 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
1017 get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
)
1019 vnode_t dvp
= NULLVP
;
1020 vnode_t sdvp
= NULLVP
;
1021 struct componentname cn
;
1022 struct vnode_attr va
;
1028 bzero(tmpname
, sizeof(tmpname
));
1029 MAKE_SHADOW_DIRNAME(rootvnode
, tmpname
);
1031 * Look up the shadow directory to ensure that it still exists.
1032 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1033 * in caching it when multiple threads may be trying to manipulate the pointers.
1035 error
= vnode_lookup(tmpname
, 0, &sdvp
, context
);
1038 * If we get here, then we have successfully looked up the shadow dir,
1039 * and it has an iocount from the lookup. Return the vp in the output argument.
1044 /* In the failure case, no iocount is acquired */
1046 bzero (tmpname
, sizeof(tmpname
));
1049 * Obtain the vnode for "/var/run" directory.
1050 * This is defined in the SHADOW_DIR_CONTAINER macro
1052 if (vnode_lookup(SHADOW_DIR_CONTAINER
, 0, &dvp
, context
) != 0) {
1058 * Create the shadow stream directory.
1059 * 'dvp' below suggests the parent directory so
1060 * we only need to provide the leaf entry name
1062 MAKE_SHADOW_DIR_LEAF(rootvnode
, tmpname
);
1063 bzero(&cn
, sizeof(cn
));
1064 cn
.cn_nameiop
= LOOKUP
;
1065 cn
.cn_flags
= ISLASTCN
;
1066 cn
.cn_context
= context
;
1067 cn
.cn_pnbuf
= tmpname
;
1068 cn
.cn_pnlen
= sizeof(tmpname
);
1069 cn
.cn_nameptr
= cn
.cn_pnbuf
;
1070 cn
.cn_namelen
= strlen(tmpname
);
1073 * owned by root, only readable by root, hidden
1076 VATTR_SET(&va
, va_uid
, 0);
1077 VATTR_SET(&va
, va_gid
, 0);
1078 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
1079 VATTR_SET(&va
, va_type
, VDIR
);
1080 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
1081 va
.va_vaflags
= VA_EXCLUSIVE
;
1083 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, context
);
1086 * There can be only one winner for an exclusive create.
1088 if (error
== EEXIST
) {
1089 /* loser has to look up directory */
1090 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, context
);
1092 /* Make sure its in fact a directory */
1093 if (sdvp
->v_type
!= VDIR
) {
1096 /* Obtain the fsid for /tmp directory */
1098 VATTR_WANTED(&va
, va_fsid
);
1099 if (VNOP_GETATTR(dvp
, &va
, context
) != 0 ||
1100 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1103 tmp_fsid
= va
.va_fsid
;
1106 VATTR_WANTED(&va
, va_uid
);
1107 VATTR_WANTED(&va
, va_gid
);
1108 VATTR_WANTED(&va
, va_mode
);
1109 VATTR_WANTED(&va
, va_fsid
);
1110 VATTR_WANTED(&va
, va_dirlinkcount
);
1111 VATTR_WANTED(&va
, va_acl
);
1112 /* Provide defaults for attrs that may not be supported */
1113 va
.va_dirlinkcount
= 1;
1114 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1116 if (VNOP_GETATTR(sdvp
, &va
, context
) != 0 ||
1117 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1118 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1119 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1120 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1124 * Make sure its what we want:
1126 * - not writable by anyone
1127 * - on same file system as /tmp
1128 * - not a hard-linked directory
1129 * - no ACLs (they might grant write access)
1131 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1132 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1133 (va
.va_fsid
!= tmp_fsid
) ||
1134 (va
.va_dirlinkcount
!= 1) ||
1135 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1145 /* On errors, clean up shadow stream directory. */
1155 /* This is not the dir we're looking for, move along */
1156 ++shadow_sequence
; /* try something else next time */
1165 * Default Implementation (Non-native EA)
1170 Typical "._" AppleDouble Header File layout:
1171 ------------------------------------------------------------
1176 .-- AD ENTRY[0] Finder Info Entry (must be first)
1177 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1179 | ///////////// Fixed Size Data (32 bytes)
1183 | ATTR ENTRY[1] --+--.
1184 | ATTR ENTRY[2] --+--+--.
1186 | ATTR ENTRY[N] --+--+--+--.
1187 | ATTR DATA 0 <-' | | |
1188 | //////////// | | |
1189 | ATTR DATA 1 <----' | |
1191 | ATTR DATA 2 <-------' |
1194 | ATTR DATA N <----------'
1196 | Attribute Free Space
1198 '----> RESOURCE FORK
1199 ///////////// Variable Sized Data
1208 ------------------------------------------------------------
1210 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1211 stored as part of the Finder Info. The length in the Finder
1212 Info AppleDouble entry includes the length of the extended
1213 attribute header, attribute entries, and attribute data.
1218 * On Disk Data Structures
1220 * Note: Motorola 68K alignment and big-endian.
1222 * See RFC 1740 for additional information about the AppleDouble file format.
1226 #define ADH_MAGIC 0x00051607
1227 #define ADH_VERSION 0x00020000
1228 #define ADH_MACOSX "Mac OS X "
1231 * AppleDouble Entry ID's
1233 #define AD_DATA 1 /* Data fork */
1234 #define AD_RESOURCE 2 /* Resource fork */
1235 #define AD_REALNAME 3 /* FileÕs name on home file system */
1236 #define AD_COMMENT 4 /* Standard Mac comment */
1237 #define AD_ICONBW 5 /* Mac black & white icon */
1238 #define AD_ICONCOLOR 6 /* Mac color icon */
1239 #define AD_UNUSED 7 /* Not used */
1240 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1241 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1242 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1243 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1244 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1245 #define AD_AFPNAME 13 /* Short name on AFP server */
1246 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1247 #define AD_AFPDIRID 15 /* AFP directory ID */
1248 #define AD_ATTRIBUTES AD_FINDERINFO
1251 #define ATTR_FILE_PREFIX "._"
1252 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1254 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1256 /* Implementation Limits */
1257 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1258 #define ATTR_MAX_HDR_SIZE 65536
1260 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1261 * size supported (including the attribute entries). All of
1262 * the attribute entries must reside within this limit. If
1263 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1264 * boundry, then all of the attribute data I/O is performed
1265 * separately from the attribute header I/O.
1267 * In particular, all of the attr_entry structures must lie
1268 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1269 * AppleDouble file. However, the attribute data (i.e. the
1270 * contents of the extended attributes) may extend beyond the
1271 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1272 * limit is to allow the implementation to optimize by reading
1273 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1277 #define FINDERINFOSIZE 32
1279 typedef struct apple_double_entry
{
1280 u_int32_t type
; /* entry type: see list, 0 invalid */
1281 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1282 u_int32_t length
; /* entry data length in bytes. */
1283 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1286 typedef struct apple_double_header
{
1287 u_int32_t magic
; /* == ADH_MAGIC */
1288 u_int32_t version
; /* format version: 2 = 0x00020000 */
1289 u_int32_t filler
[4];
1290 u_int16_t numEntries
; /* number of entries which follow */
1291 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1292 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1293 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1294 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1296 #define ADHDRSIZE (4+4+16+2)
1298 /* Entries are aligned on 4 byte boundaries */
1299 typedef struct attr_entry
{
1300 u_int32_t offset
; /* file offset to data */
1301 u_int32_t length
; /* size of attribute data */
1304 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1305 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1308 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1309 typedef struct attr_header
{
1310 apple_double_header_t appledouble
;
1311 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1312 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1313 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1314 u_int32_t data_start
; /* file offset to attribute data area */
1315 u_int32_t data_length
; /* length of attribute data area */
1316 u_int32_t reserved
[3];
1318 u_int16_t num_attrs
;
1319 } __attribute__((aligned(2), packed
)) attr_header_t
;
1322 /* Empty Resource Fork Header */
1323 typedef struct rsrcfork_header
{
1324 u_int32_t fh_DataOffset
;
1325 u_int32_t fh_MapOffset
;
1326 u_int32_t fh_DataLength
;
1327 u_int32_t fh_MapLength
;
1328 u_int8_t systemData
[112];
1329 u_int8_t appData
[128];
1330 u_int32_t mh_DataOffset
;
1331 u_int32_t mh_MapOffset
;
1332 u_int32_t mh_DataLength
;
1333 u_int32_t mh_MapLength
;
1335 u_int16_t mh_RefNum
;
1337 u_int8_t mh_InMemoryAttr
;
1340 u_int16_t typeCount
;
1341 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1343 #define RF_FIRST_RESOURCE 256
1344 #define RF_NULL_MAP_LENGTH 30
1345 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1347 /* Runtime information about the attribute file. */
1348 typedef struct attr_info
{
1349 vfs_context_t context
;
1354 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1355 apple_double_header_t
*filehdr
;
1356 apple_double_entry_t
*finderinfo
;
1357 apple_double_entry_t
*rsrcfork
;
1358 attr_header_t
*attrhdr
;
1359 attr_entry_t
*attr_entry
;
1361 u_int8_t emptyfinderinfo
;
1365 #define ATTR_SETTING 1
1367 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1369 #define ATTR_ENTRY_LENGTH(namelen) \
1370 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1372 #define ATTR_NEXT(ae) \
1373 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1375 #define ATTR_VALID(ae, ai) \
1376 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1378 #define SWAP16(x) OSSwapBigToHostInt16((x))
1379 #define SWAP32(x) OSSwapBigToHostInt32((x))
1380 #define SWAP64(x) OSSwapBigToHostInt64((x))
1383 static u_int32_t emptyfinfo
[8] = {0};
1387 * Local support routines
1389 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1391 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1393 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1395 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1397 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1399 static void rel_xattrinfo(attr_info_t
*ainfop
);
1401 static int write_xattrinfo(attr_info_t
*ainfop
);
1403 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1405 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1407 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1410 #if BYTE_ORDER == LITTLE_ENDIAN
1411 static void swap_adhdr(apple_double_header_t
*adh
);
1412 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1415 #define swap_adhdr(x)
1416 #define swap_attrhdr(x, y)
1419 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1420 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1421 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1425 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1426 * is in big endian (as it would exist on disk). Verifies the following:
1429 * - number of entries
1430 * - that each entry fits within the file size
1432 * If the header is invalid, ENOATTR is returned.
1434 * NOTE: Does not attempt to validate the extended attributes header that
1435 * may be embedded in the Finder Info entry.
1437 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1440 u_int32_t header_end
;
1441 u_int32_t entry_end
;
1443 apple_double_header_t
*header
;
1445 rawsize
= ainfop
->rawsize
;
1446 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1448 /* Is the file big enough to contain an AppleDouble header? */
1449 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1452 /* Swap the AppleDouble header fields to native order */
1453 header
->magic
= SWAP32(header
->magic
);
1454 header
->version
= SWAP32(header
->version
);
1455 header
->numEntries
= SWAP16(header
->numEntries
);
1457 /* Sanity check the AppleDouble header fields */
1458 if (header
->magic
!= ADH_MAGIC
||
1459 header
->version
!= ADH_VERSION
||
1460 header
->numEntries
< 1 ||
1461 header
->numEntries
> 15) {
1465 /* Calculate where the entries[] array ends */
1466 header_end
= offsetof(apple_double_header_t
, entries
) +
1467 header
->numEntries
* sizeof(apple_double_entry_t
);
1469 /* Is the file big enough to contain the AppleDouble entries? */
1470 if (rawsize
< header_end
) {
1474 /* Swap and sanity check each AppleDouble entry */
1475 for (i
=0; i
<header
->numEntries
; i
++) {
1476 /* Swap the per-entry fields to native order */
1477 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1478 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1479 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1481 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1484 * Does the entry's content start within the header itself,
1485 * did the addition overflow, or does the entry's content
1486 * extend past the end of the file?
1488 if (header
->entries
[i
].offset
< header_end
||
1489 entry_end
< header
->entries
[i
].offset
||
1490 entry_end
> ainfop
->filesize
) {
1495 * Does the current entry's content overlap with a previous
1498 * Yes, this is O(N**2), and there are more efficient algorithms
1499 * for testing pairwise overlap of N ranges when N is large.
1500 * But we have already ensured N < 16, and N is almost always 2.
1501 * So there's no point in using a more complex algorithm.
1504 for (j
=0; j
<i
; j
++) {
1505 if (entry_end
> header
->entries
[j
].offset
&&
1506 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1518 * Retrieve the data of an extended attribute.
1521 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1522 __unused
int options
, vfs_context_t context
)
1526 attr_header_t
*header
;
1527 attr_entry_t
*entry
;
1537 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1540 * Open the file locked (shared) since the Carbon
1541 * File Manager may have the Apple Double file open
1542 * and could be changing the resource fork.
1544 fileflags
|= O_SHLOCK
;
1549 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1552 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1553 close_xattrfile(xvp
, fileflags
, context
);
1557 /* Get the Finder Info. */
1558 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1560 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1562 } else if (uio
== NULL
) {
1563 *size
= FINDERINFOSIZE
;
1565 } else if (uio_offset(uio
) != 0) {
1567 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1570 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1571 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1576 /* Read the Resource Fork. */
1578 if (!vnode_isreg(vp
)) {
1580 } else if (ainfo
.rsrcfork
== NULL
) {
1582 } else if (uio
== NULL
) {
1583 *size
= (size_t)ainfo
.rsrcfork
->length
;
1585 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1586 error
= VNOP_READ(xvp
, uio
, 0, context
);
1588 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1593 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1597 if (uio_offset(uio
) != 0) {
1602 namelen
= strlen(name
) + 1;
1603 header
= ainfo
.attrhdr
;
1604 entry
= ainfo
.attr_entry
;
1606 * Search for attribute name in the header.
1608 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1609 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1610 datalen
= (size_t)entry
->length
;
1616 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1620 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1621 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1622 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1624 uio_setoffset(uio
, entry
->offset
);
1625 error
= VNOP_READ(xvp
, uio
, 0, context
);
1626 uio_setoffset(uio
, 0);
1630 entry
= ATTR_NEXT(entry
);
1633 rel_xattrinfo(&ainfo
);
1634 close_xattrfile(xvp
, fileflags
, context
);
1640 * Set the data of an extended attribute.
1643 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1647 attr_header_t
*header
;
1648 attr_entry_t
*entry
;
1649 attr_entry_t
*lastentry
;
1653 size_t datafreespace
;
1660 char finfo
[FINDERINFOSIZE
];
1662 datalen
= uio_resid(uio
);
1663 namelen
= strlen(name
) + 1;
1664 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1667 * By convention, Finder Info that is all zeroes is equivalent to not
1668 * having a Finder Info EA. So if we're trying to set the Finder Info
1669 * to all zeroes, then delete it instead. If a file didn't have an
1670 * AppleDouble file before, this prevents creating an AppleDouble file
1671 * with no useful content.
1673 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1674 * for all zeroes Finder Info before opening the AppleDouble file.
1675 * But if either of those options were specified, we need to open the
1676 * AppleDouble file to see whether there was already Finder Info (so we
1677 * can return an error if needed); this case is handled further below.
1679 * NOTE: this copies the Finder Info data into the "finfo" local.
1681 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1683 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1684 * That means we probably have to open_xattrfile and get_xattrinfo.
1686 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1689 error
= uiomove(finfo
, datalen
, uio
);
1692 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1693 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1694 error
= default_removexattr(vp
, name
, 0, context
);
1695 if (error
== ENOATTR
)
1703 * Open the file locked since setting an attribute
1704 * can change the layout of the Apple Double file.
1706 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1707 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1710 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1711 close_xattrfile(xvp
, fileflags
, context
);
1715 /* Set the Finder Info. */
1716 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1717 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1718 /* attr exists and "create" was specified? */
1719 if (options
& XATTR_CREATE
) {
1724 /* attr doesn't exists and "replace" was specified? */
1725 if (options
& XATTR_REPLACE
) {
1730 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1732 * Setting the Finder Info to all zeroes is equivalent to
1733 * removing it. Close the xattr file and let
1734 * default_removexattr do the work (including deleting
1735 * the xattr file if there are no other xattrs).
1737 * Note that we have to handle the case where the
1738 * Finder Info was already all zeroes, and we ignore
1741 * The common case where options == 0 was handled above.
1743 rel_xattrinfo(&ainfo
);
1744 close_xattrfile(xvp
, fileflags
, context
);
1745 error
= default_removexattr(vp
, name
, 0, context
);
1746 if (error
== ENOATTR
)
1750 if (ainfo
.finderinfo
) {
1751 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1752 bcopy(finfo
, attrdata
, datalen
);
1753 ainfo
.iosize
= sizeof(attr_header_t
);
1754 error
= write_xattrinfo(&ainfo
);
1761 /* Write the Resource Fork. */
1762 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1763 u_int32_t endoffset
;
1765 if (!vnode_isreg(vp
)) {
1769 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
1770 /* attr exists and "create" was specified? */
1771 if (options
& XATTR_CREATE
) {
1776 /* attr doesn't exists and "replace" was specified? */
1777 if (options
& XATTR_REPLACE
) {
1782 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1783 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1784 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1787 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1788 if (endoffset
> ainfo
.rsrcfork
->length
) {
1789 ainfo
.rsrcfork
->length
= endoffset
;
1790 ainfo
.iosize
= sizeof(attr_header_t
);
1791 error
= write_xattrinfo(&ainfo
);
1797 if (datalen
> ATTR_MAX_SIZE
) {
1798 return (E2BIG
); /* EINVAL instead ? */
1801 if (ainfo
.attrhdr
== NULL
) {
1805 header
= ainfo
.attrhdr
;
1806 entry
= ainfo
.attr_entry
;
1808 /* Check if data area crosses the maximum header size. */
1809 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1810 splitdata
= 1; /* do data I/O separately */
1815 * See if attribute already exists.
1817 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1818 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1822 entry
= ATTR_NEXT(entry
);
1826 if (options
& XATTR_CREATE
) {
1830 if (datalen
== entry
->length
) {
1832 uio_setoffset(uio
, entry
->offset
);
1833 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1834 uio_setoffset(uio
, 0);
1836 printf("setxattr: VNOP_WRITE error %d\n", error
);
1839 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1840 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1843 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1844 error
= write_xattrinfo(&ainfo
);
1846 printf("setxattr: write_xattrinfo error %d\n", error
);
1852 * Brute force approach - just remove old entry and set new entry.
1855 rel_xattrinfo(&ainfo
);
1856 close_xattrfile(xvp
, fileflags
, context
);
1857 error
= default_removexattr(vp
, name
, options
, context
);
1861 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1862 options
&= ~XATTR_REPLACE
;
1863 goto start
; /* start over */
1868 if (options
& XATTR_REPLACE
) {
1869 error
= ENOATTR
; /* nothing there to replace */
1872 /* Check if header size limit has been reached. */
1873 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1878 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1880 /* Check if we need more space. */
1881 if ((datalen
+ entrylen
) > datafreespace
) {
1884 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1886 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1887 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1888 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1891 ainfo
.filesize
+= growsize
;
1892 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1894 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1900 * Move the resource fork out of the way.
1902 if (ainfo
.rsrcfork
) {
1903 if (ainfo
.rsrcfork
->length
!= 0) {
1904 shift_data_down(xvp
,
1905 ainfo
.rsrcfork
->offset
,
1906 ainfo
.rsrcfork
->length
,
1909 ainfo
.rsrcfork
->offset
+= growsize
;
1911 ainfo
.finderinfo
->length
+= growsize
;
1912 header
->total_size
+= growsize
;
1915 /* Make space for a new entry. */
1917 shift_data_down(xvp
,
1919 header
->data_length
,
1922 bcopy((u_int8_t
*)header
+ header
->data_start
,
1923 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
1924 header
->data_length
);
1926 header
->data_start
+= entrylen
;
1928 /* Fix up entry data offsets. */
1930 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
1931 entry
->offset
+= entrylen
;
1935 * If the attribute data area is entirely within
1936 * the header buffer, then just update the buffer,
1937 * otherwise we'll write it separately to the file.
1942 /* Write new attribute data after the end of existing data. */
1943 offset
= header
->data_start
+ header
->data_length
;
1944 uio_setoffset(uio
, offset
);
1945 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1946 uio_setoffset(uio
, 0);
1948 printf("setxattr: VNOP_WRITE error %d\n", error
);
1952 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
1954 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1956 printf("setxattr: uiomove error %d\n", error
);
1961 /* Create the attribute entry. */
1962 lastentry
->length
= datalen
;
1963 lastentry
->offset
= header
->data_start
+ header
->data_length
;
1964 lastentry
->namelen
= namelen
;
1965 lastentry
->flags
= 0;
1966 bcopy(name
, &lastentry
->name
[0], namelen
);
1968 /* Update the attributes header. */
1969 header
->num_attrs
++;
1970 header
->data_length
+= datalen
;
1973 /* Only write the entries, since the data was written separately. */
1974 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
1976 /* The entry and data are both in the header; write them together. */
1977 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1979 error
= write_xattrinfo(&ainfo
);
1981 printf("setxattr: write_xattrinfo error %d\n", error
);
1985 rel_xattrinfo(&ainfo
);
1986 close_xattrfile(xvp
, fileflags
, context
);
1988 /* Touch the change time if we changed an attribute. */
1990 struct vnode_attr va
;
1992 /* Re-write the mtime to cause a ctime change. */
1994 VATTR_WANTED(&va
, va_modify_time
);
1995 if (vnode_getattr(vp
, &va
, context
) == 0) {
1997 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1998 (void) vnode_setattr(vp
, &va
, context
);
2002 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2009 * Remove an extended attribute.
2012 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
2016 attr_header_t
*header
;
2017 attr_entry_t
*entry
;
2018 attr_entry_t
*oldslot
;
2024 int found
= 0, lastone
= 0;
2032 fileflags
= FREAD
| FWRITE
;
2033 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
2036 * Open the file locked (exclusive) since the Carbon
2037 * File Manager may have the Apple Double file open
2038 * and could be changing the resource fork.
2040 fileflags
|= O_EXLOCK
;
2045 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
2048 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2049 close_xattrfile(xvp
, fileflags
, context
);
2053 attrcount
+= ainfo
.attrhdr
->num_attrs
;
2056 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
2059 /* Clear the Finder Info. */
2060 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
2061 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
2065 /* On removal of last attribute the ._ file is removed. */
2066 if (--attrcount
== 0)
2068 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
2069 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
2070 ainfo
.iosize
= sizeof(attr_header_t
);
2071 error
= write_xattrinfo(&ainfo
);
2075 /* Clear the Resource Fork. */
2077 if (!vnode_isreg(vp
)) {
2081 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
2085 /* On removal of last attribute the ._ file is removed. */
2086 if (--attrcount
== 0)
2090 * If the resource fork isn't the last AppleDouble
2091 * entry then the space needs to be reclaimed by
2092 * shifting the entries after the resource fork.
2094 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
2095 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
2096 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2099 ainfo
.rsrcfork
->length
= 0;
2100 ainfo
.iosize
= sizeof(attr_header_t
);
2101 error
= write_xattrinfo(&ainfo
);
2106 if (ainfo
.attrhdr
== NULL
) {
2110 namelen
= strlen(name
) + 1;
2111 header
= ainfo
.attrhdr
;
2112 entry
= ainfo
.attr_entry
;
2115 * See if this attribute exists.
2117 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2118 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2120 if ((i
+1) == header
->num_attrs
)
2124 entry
= ATTR_NEXT(entry
);
2130 /* On removal of last attribute the ._ file is removed. */
2131 if (--attrcount
== 0)
2134 datalen
= entry
->length
;
2135 dataoff
= entry
->offset
;
2136 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2137 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
2142 /* Remove the attribute entry. */
2144 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2145 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2148 /* Adjust the attribute data. */
2152 dataoff
- header
->data_start
,
2158 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2162 /* XXX write zeros to freed space ? */
2163 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2167 bcopy((u_int8_t
*)header
+ header
->data_start
,
2168 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2169 dataoff
- header
->data_start
);
2171 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2172 (u_int8_t
*)header
+ dataoff
- entrylen
,
2173 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2175 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2176 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2179 /* Adjust the header values and entry offsets. */
2180 header
->num_attrs
--;
2181 header
->data_start
-= entrylen
;
2182 header
->data_length
-= datalen
;
2185 entry
= ainfo
.attr_entry
;
2186 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2187 entry
->offset
-= entrylen
;
2188 if (entry
>= oldslot
)
2189 entry
->offset
-= datalen
;
2190 entry
= ATTR_NEXT(entry
);
2192 error
= write_xattrinfo(&ainfo
);
2194 printf("removexattr: write_xattrinfo error %d\n", error
);
2197 rel_xattrinfo(&ainfo
);
2199 /* When there are no more attributes remove the ._ file. */
2200 if (attrcount
== 0) {
2201 if (fileflags
& O_EXLOCK
)
2202 (void) unlock_xattrfile(xvp
, context
);
2203 VNOP_CLOSE(xvp
, fileflags
, context
);
2205 error
= remove_xattrfile(xvp
, context
);
2208 close_xattrfile(xvp
, fileflags
, context
);
2210 /* Touch the change time if we changed an attribute. */
2212 struct vnode_attr va
;
2214 /* Re-write the mtime to cause a ctime change. */
2216 VATTR_WANTED(&va
, va_modify_time
);
2217 if (vnode_getattr(vp
, &va
, context
) == 0) {
2219 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2220 (void) vnode_setattr(vp
, &va
, context
);
2224 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2232 * Retrieve the list of extended attribute names.
2235 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2239 attr_entry_t
*entry
;
2244 * We do not zero "*size" here as we don't want to stomp a size set when
2245 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2246 * system call layer, up in listxattr or flistxattr.
2249 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2250 if (error
== ENOATTR
)
2254 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2255 if (error
== ENOATTR
)
2257 close_xattrfile(xvp
, FREAD
, context
);
2261 /* Check for Finder Info. */
2262 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2264 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2265 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2269 error
= uiomove(XATTR_FINDERINFO_NAME
,
2270 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2278 /* Check for Resource Fork. */
2279 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2281 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2282 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2286 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2287 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2295 /* Check for attributes. */
2296 if (ainfo
.attrhdr
) {
2297 count
= ainfo
.attrhdr
->num_attrs
;
2298 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2299 if (xattr_protected((const char *)entry
->name
) ||
2300 xattr_validatename((const char *)entry
->name
) != 0) {
2301 entry
= ATTR_NEXT(entry
);
2305 *size
+= entry
->namelen
;
2306 entry
= ATTR_NEXT(entry
);
2309 if (uio_resid(uio
) < entry
->namelen
) {
2313 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2315 if (error
!= EFAULT
)
2319 entry
= ATTR_NEXT(entry
);
2323 rel_xattrinfo(&ainfo
);
2324 close_xattrfile(xvp
, FREAD
, context
);
2330 * Check the header of a ._ file to verify that it is in fact an Apple Double
2331 * file. Returns 0 if the header is valid, non-zero if invalid.
2333 int check_appledouble_header(vnode_t vp
, vfs_context_t ctx
)
2337 struct vnode_attr va
;
2339 void *buffer
= NULL
;
2343 ainfo
.context
= ctx
;
2345 VATTR_WANTED(&va
, va_data_size
);
2346 if ((error
= vnode_getattr(vp
, &va
, ctx
))) {
2349 ainfo
.filesize
= va
.va_data_size
;
2351 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfo
.filesize
);
2356 ainfo
.iosize
= iosize
;
2358 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2359 if (buffer
== NULL
) {
2364 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2365 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2367 /* Read the header */
2368 error
= VNOP_READ(vp
, auio
, 0, ctx
);
2372 ainfo
.rawsize
= iosize
- uio_resid(auio
);
2373 ainfo
.rawdata
= (u_int8_t
*)buffer
;
2375 error
= check_and_swap_apple_double_header(&ainfo
);
2380 /* If we made it here, then the header is ok */
2387 FREE(buffer
, M_TEMP
);
2394 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2396 vnode_t xvp
= NULLVP
;
2397 vnode_t dvp
= NULLVP
;
2398 struct vnode_attr va
;
2399 struct nameidata nd
;
2401 char *filename
= NULL
;
2402 const char *basename
= NULL
;
2408 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2410 * For the root directory use "._." to hold the attributes.
2412 filename
= &smallname
[0];
2413 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2414 dvp
= vp
; /* the "._." file resides in the root dir */
2417 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2421 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2426 /* "._" Attribute files cannot have attributes */
2427 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2428 basename
[0] == '.' && basename
[1] == '_') {
2432 filename
= &smallname
[0];
2433 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2434 if (len
>= sizeof(smallname
)) {
2435 len
++; /* snprintf result doesn't include '\0' */
2436 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2437 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2440 * Note that the lookup here does not authorize. Since we are looking
2441 * up in the same directory that we already have the file vnode in,
2442 * we must have been given the file vnode legitimately. Read/write
2443 * access has already been authorized in layers above for calls from
2444 * userspace, and the authorization code using this path to read
2445 * file security from the EA must always get access
2448 NDINIT(&nd
, LOOKUP
, OP_OPEN
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
,
2449 UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), context
);
2452 if (fileflags
& O_CREAT
) {
2453 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2458 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2460 if ( (error
= namei(&nd
))) {
2465 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2471 * Pick up uid/gid/mode from target file.
2474 VATTR_WANTED(&va
, va_uid
);
2475 VATTR_WANTED(&va
, va_gid
);
2476 VATTR_WANTED(&va
, va_mode
);
2477 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2478 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2479 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2480 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2483 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2484 } else /* fallback values */ {
2485 uid
= KAUTH_UID_NONE
;
2486 gid
= KAUTH_GID_NONE
;
2487 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2491 VATTR_SET(&va
, va_type
, VREG
);
2492 VATTR_SET(&va
, va_mode
, umode
);
2493 if (uid
!= KAUTH_UID_NONE
)
2494 VATTR_SET(&va
, va_uid
, uid
);
2495 if (gid
!= KAUTH_GID_NONE
)
2496 VATTR_SET(&va
, va_gid
, gid
);
2498 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
, &va
,
2499 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2509 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2514 if ((error
= namei(&nd
))) {
2524 if (xvp
->v_type
!= VREG
) {
2529 * Owners must match.
2532 VATTR_WANTED(&va
, va_uid
);
2533 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2534 uid_t owner
= va
.va_uid
;
2537 VATTR_WANTED(&va
, va_uid
);
2538 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2539 error
= ENOATTR
; /* don't use this "._" file */
2544 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2550 if ((error
= vnode_ref(xvp
))) {
2555 /* If create was requested, make sure file header exists. */
2556 if (fileflags
& O_CREAT
) {
2558 VATTR_WANTED(&va
, va_data_size
);
2559 VATTR_WANTED(&va
, va_fileid
);
2560 VATTR_WANTED(&va
, va_nlink
);
2561 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2566 /* If the file is empty then add a default header. */
2567 if (va
.va_data_size
== 0) {
2568 /* Don't adopt hard-linked "._" files. */
2569 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2573 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2577 /* Apply file locking if requested. */
2578 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2581 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2582 error
= lock_xattrfile(xvp
, locktype
, context
);
2587 if (dvp
&& (dvp
!= vp
)) {
2591 vnode_putname(basename
);
2593 if (filename
&& filename
!= &smallname
[0]) {
2594 FREE(filename
, M_TEMP
);
2597 if (xvp
!= NULLVP
) {
2599 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2602 (void) vnode_rele(xvp
);
2604 (void) vnode_put(xvp
);
2607 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2611 *xvpp
= xvp
; /* return a referenced vnode */
2616 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2618 // if (fileflags & FWRITE)
2619 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2621 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2622 (void) unlock_xattrfile(xvp
, context
);
2624 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2625 (void) vnode_rele(xvp
);
2626 (void) vnode_put(xvp
);
2630 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2633 struct nameidata nd
;
2638 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2642 pathlen
= MAXPATHLEN
;
2643 error
= vn_getpath(xvp
, path
, &pathlen
);
2645 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2649 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2650 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2652 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2659 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2668 * Read in and parse the AppleDouble header and entries, and the extended
2669 * attribute header and entries if any. Populates the fields of ainfop
2670 * based on the headers and entries found.
2672 * The basic idea is to:
2673 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2674 * AppleDouble entries, the extended attribute header, and extended
2675 * attribute entries must lie within this part of the file; the rest of
2676 * the AppleDouble handling code assumes this. Plus it allows us to
2677 * somewhat optimize by doing a smaller number of larger I/Os.
2678 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2680 * - Find the Finder Info and Resource Fork entries, if any.
2681 * - If we're going to be writing, try to make sure the Finder Info entry has
2682 * room to store the extended attribute header, plus some space for extended
2684 * - Swap and sanity check the extended attribute header and entries (if any).
2687 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2690 void * buffer
= NULL
;
2691 apple_double_header_t
*filehdr
;
2692 struct vnode_attr va
;
2697 bzero(ainfop
, sizeof(attr_info_t
));
2698 ainfop
->filevp
= xvp
;
2699 ainfop
->context
= context
;
2701 VATTR_WANTED(&va
, va_data_size
);
2702 VATTR_WANTED(&va
, va_fileid
);
2703 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2706 ainfop
->filesize
= va
.va_data_size
;
2708 /* When setting attributes, allow room for the header to grow. */
2710 iosize
= ATTR_MAX_HDR_SIZE
;
2712 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2718 ainfop
->iosize
= iosize
;
2719 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2720 if (buffer
== NULL
){
2725 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2726 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2728 /* Read the file header. */
2729 error
= VNOP_READ(xvp
, auio
, 0, context
);
2733 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2734 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2736 filehdr
= (apple_double_header_t
*)buffer
;
2738 error
= check_and_swap_apple_double_header(ainfop
);
2742 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2744 /* rel_xattrinfo is responsible for freeing the header buffer */
2747 /* Find the Finder Info and Resource Fork entries, if any */
2748 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2749 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2750 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2751 /* We found the Finder Info entry. */
2752 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2755 * Is the Finder Info "empty" (all zeroes)? If so,
2756 * we'll pretend like the Finder Info extended attribute
2759 * Note: we have to make sure the Finder Info is
2760 * contained within the buffer we have already read,
2761 * to avoid accidentally accessing a bogus address.
2762 * If it is outside the buffer, we just assume the
2763 * Finder Info is non-empty.
2765 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2766 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2767 ainfop
->emptyfinderinfo
= 1;
2770 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2772 * Ignore zero-length resource forks when getting. If setting,
2773 * we need to remember the resource fork entry so it can be
2774 * updated once the new content has been written.
2776 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2780 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2782 * The "empty" resource headers we created have a system data tag of:
2783 * "This resource fork intentionally left blank "
2785 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2787 u_int8_t systemData
[64];
2791 /* Read the system data which starts at byte 16 */
2792 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2793 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2794 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2795 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2799 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2800 continue; /* skip this resource fork */
2803 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2804 if (i
!= (filehdr
->numEntries
- 1)) {
2805 printf("get_xattrinfo: resource fork not last entry\n");
2806 ainfop
->readonly
= 1;
2813 * See if this file looks like it is laid out correctly to contain
2814 * extended attributes. If so, then do the following:
2816 * - If we're going to be writing, try to make sure the Finder Info
2817 * entry has room to store the extended attribute header, plus some
2818 * space for extended attributes.
2820 * - Swap and sanity check the extended attribute header and entries
2823 if (filehdr
->numEntries
== 2 &&
2824 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2825 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2826 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2827 attr_header_t
*attrhdr
;
2828 attrhdr
= (attr_header_t
*)filehdr
;
2830 * If we're going to be writing, try to make sure the Finder
2831 * Info entry has room to store the extended attribute header,
2832 * plus some space for extended attributes.
2834 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2838 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2839 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2840 /* Make some room before existing resource fork. */
2841 shift_data_down(xvp
,
2842 filehdr
->entries
[1].offset
,
2843 filehdr
->entries
[1].length
,
2845 writesize
= sizeof(attr_header_t
);
2847 /* Create a new, empty resource fork. */
2848 rsrcfork_header_t
*rsrcforkhdr
;
2850 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2852 /* Steal some space for an empty RF header. */
2853 delta
-= sizeof(rsrcfork_header_t
);
2855 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2856 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2858 /* Fill in Empty Resource Fork Header. */
2859 init_empty_resource_fork(rsrcforkhdr
);
2861 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2862 writesize
= ATTR_BUF_SIZE
;
2864 filehdr
->entries
[0].length
+= delta
;
2865 filehdr
->entries
[1].offset
+= delta
;
2867 /* Fill in Attribute Header. */
2868 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2869 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2870 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2871 attrhdr
->data_start
= sizeof(attr_header_t
);
2872 attrhdr
->data_length
= 0;
2873 attrhdr
->reserved
[0] = 0;
2874 attrhdr
->reserved
[1] = 0;
2875 attrhdr
->reserved
[2] = 0;
2877 attrhdr
->num_attrs
= 0;
2879 /* Push out new header */
2880 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2881 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2883 swap_adhdr(filehdr
); /* to big endian */
2884 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2885 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2886 swap_adhdr(filehdr
); /* back to native */
2887 /* The attribute header gets swapped below. */
2891 * Swap and sanity check the extended attribute header and
2892 * entries (if any). The Finder Info content must be big enough
2893 * to include the extended attribute header; if not, we just
2896 * Note that we're passing the offset + length (i.e. the end)
2897 * of the Finder Info instead of rawsize to validate_attrhdr.
2898 * This ensures that all extended attributes lie within the
2899 * Finder Info content according to the AppleDouble entry.
2901 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2904 if (ainfop
->finderinfo
&&
2905 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2906 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2907 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2909 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2910 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2911 /* First attr_entry starts immediately following attribute header */
2912 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2921 FREE(buffer
, M_TEMP
);
2927 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2930 rsrcfork_header_t
*rsrcforkhdr
;
2936 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2937 bzero(buffer
, ATTR_BUF_SIZE
);
2939 xah
= (attr_header_t
*)buffer
;
2940 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2941 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2942 rsrcforksize
= sizeof(rsrcfork_header_t
);
2943 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2945 /* Fill in Apple Double Header. */
2946 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2947 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2948 xah
->appledouble
.numEntries
= SWAP16 (2);
2949 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2950 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2951 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2952 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2953 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2954 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2955 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2957 /* Fill in Attribute Header. */
2958 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2959 xah
->debug_tag
= SWAP32 (fileid
);
2960 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2961 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2963 /* Fill in Empty Resource Fork Header. */
2964 init_empty_resource_fork(rsrcforkhdr
);
2967 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2970 FREE(buffer
, M_TEMP
);
2976 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
2978 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
2979 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2980 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2981 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2982 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2983 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2984 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2985 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
2986 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
2987 rsrcforkhdr
->typeCount
= SWAP16 (-1);
2988 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
2992 rel_xattrinfo(attr_info_t
*ainfop
)
2994 FREE(ainfop
->filehdr
, M_TEMP
);
2995 bzero(ainfop
, sizeof(attr_info_t
));
2999 write_xattrinfo(attr_info_t
*ainfop
)
3004 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3005 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
3007 swap_adhdr(ainfop
->filehdr
);
3008 if (ainfop
->attrhdr
!= NULL
) {
3009 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3012 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
3014 swap_adhdr(ainfop
->filehdr
);
3015 if (ainfop
->attrhdr
!= NULL
) {
3016 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
3023 #if BYTE_ORDER == LITTLE_ENDIAN
3025 * Endian swap apple double header
3028 swap_adhdr(apple_double_header_t
*adh
)
3033 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
3035 adh
->magic
= SWAP32 (adh
->magic
);
3036 adh
->version
= SWAP32 (adh
->version
);
3037 adh
->numEntries
= SWAP16 (adh
->numEntries
);
3039 for (i
= 0; i
< count
; i
++) {
3040 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
3041 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
3042 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
3047 * Endian swap extended attributes header
3050 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
3056 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
3058 ah
->magic
= SWAP32 (ah
->magic
);
3059 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3060 ah
->total_size
= SWAP32 (ah
->total_size
);
3061 ah
->data_start
= SWAP32 (ah
->data_start
);
3062 ah
->data_length
= SWAP32 (ah
->data_length
);
3063 ah
->flags
= SWAP16 (ah
->flags
);
3064 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3066 ae
= (attr_entry_t
*)(&ah
[1]);
3067 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
3068 ae
->offset
= SWAP32 (ae
->offset
);
3069 ae
->length
= SWAP32 (ae
->length
);
3070 ae
->flags
= SWAP16 (ae
->flags
);
3076 * Validate and swap the attributes header contents, and each attribute's
3079 * Note: Assumes the caller has verified that the Finder Info content is large
3080 * enough to contain the attr_header structure itself. Therefore, we can
3081 * swap the header fields before sanity checking them.
3084 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
3095 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
3098 /* Swap the basic header fields */
3099 ah
->magic
= SWAP32(ah
->magic
);
3100 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3101 ah
->total_size
= SWAP32 (ah
->total_size
);
3102 ah
->data_start
= SWAP32 (ah
->data_start
);
3103 ah
->data_length
= SWAP32 (ah
->data_length
);
3104 ah
->flags
= SWAP16 (ah
->flags
);
3105 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3108 * Make sure the total_size fits within the Finder Info area, and the
3109 * extended attribute data area fits within total_size.
3111 end
= ah
->data_start
+ ah
->data_length
;
3112 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3113 end
< ah
->data_start
||
3114 end
> ah
->total_size
) {
3119 * Make sure each of the attr_entry_t's fits within total_size.
3121 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3122 count
= ah
->num_attrs
;
3123 ae
= (attr_entry_t
*)(&ah
[1]);
3125 for (i
=0; i
<count
; i
++) {
3126 /* Make sure the fixed-size part of this attr_entry_t fits. */
3127 if ((u_int8_t
*) &ae
[1] > buf_end
)
3130 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3131 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3132 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
3135 /* Swap the attribute entry fields */
3136 ae
->offset
= SWAP32(ae
->offset
);
3137 ae
->length
= SWAP32(ae
->length
);
3138 ae
->flags
= SWAP16(ae
->flags
);
3140 /* Make sure the attribute content fits. */
3141 end
= ae
->offset
+ ae
->length
;
3142 if (end
< ae
->offset
|| end
> ah
->total_size
)
3149 * TODO: Make sure the contents of attributes don't overlap the header
3150 * and don't overlap each other. The hard part is that we don't know
3151 * what the actual header size is until we have looped over all of the
3152 * variable-sized attribute entries.
3154 * XXX Is there any guarantee that attribute entries are stored in
3155 * XXX order sorted by the contents' file offset? If so, that would
3156 * XXX make the pairwise overlap check much easier.
3163 // "start" & "end" are byte offsets in the file.
3164 // "to" is the byte offset we want to move the
3165 // data to. "to" should be > "start".
3167 // we do the copy backwards to avoid problems if
3168 // there's an overlap.
3171 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3174 size_t chunk
, orig_chunk
;
3177 kauth_cred_t ucred
= vfs_context_ucred(context
);
3178 proc_t p
= vfs_context_proc(context
);
3180 if (delta
== 0 || len
== 0) {
3190 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3194 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
3195 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3197 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3198 pos
, ret
, chunk
, ret
);
3202 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3204 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3205 pos
+delta
, ret
, chunk
, ret
);
3209 if ((pos
- (off_t
)chunk
) < start
) {
3210 chunk
= pos
- start
;
3212 if (chunk
== 0) { // we're all done
3217 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3224 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3227 size_t chunk
, orig_chunk
;
3231 kauth_cred_t ucred
= vfs_context_ucred(context
);
3232 proc_t p
= vfs_context_proc(context
);
3234 if (delta
== 0 || len
== 0) {
3245 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3249 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3250 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3252 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3253 pos
, ret
, chunk
, ret
);
3257 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3259 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3260 pos
+delta
, ret
, chunk
, ret
);
3264 if ((pos
+ (off_t
)chunk
) > end
) {
3267 if (chunk
== 0) { // we're all done
3272 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3278 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3283 lf
.l_whence
= SEEK_SET
;
3286 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3287 /* Note: id is just a kernel address that's not a proc */
3288 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
);
3289 return (error
== ENOTSUP
? 0 : error
);
3293 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3298 lf
.l_whence
= SEEK_SET
;
3301 lf
.l_type
= F_UNLCK
;
3302 /* Note: id is just a kernel address that's not a proc */
3303 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);
3304 return (error
== ENOTSUP
? 0 : error
);