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
);
632 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
633 int *creator
, vfs_context_t context
)
635 vnode_t dvp
= NULLVP
;
636 vnode_t svp
= NULLVP
;
637 struct componentname cn
;
638 struct vnode_attr va
;
646 /* Establish a unique file name. */
647 MAKE_SHADOW_NAME(vp
, tmpname
);
648 bzero(&cn
, sizeof(cn
));
649 cn
.cn_nameiop
= LOOKUP
;
650 cn
.cn_flags
= ISLASTCN
;
651 cn
.cn_context
= context
;
652 cn
.cn_pnbuf
= tmpname
;
653 cn
.cn_pnlen
= sizeof(tmpname
);
654 cn
.cn_nameptr
= cn
.cn_pnbuf
;
655 cn
.cn_namelen
= strlen(tmpname
);
657 /* Pick up uid, gid, mode and date from original file. */
659 VATTR_WANTED(&va
, va_uid
);
660 VATTR_WANTED(&va
, va_gid
);
661 VATTR_WANTED(&va
, va_mode
);
662 VATTR_WANTED(&va
, va_create_time
);
663 VATTR_WANTED(&va
, va_modify_time
);
664 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
665 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
666 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
667 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
668 va
.va_uid
= KAUTH_UID_NONE
;
669 va
.va_gid
= KAUTH_GID_NONE
;
670 va
.va_mode
= S_IRUSR
| S_IWUSR
;
672 va
.va_vaflags
= VA_EXCLUSIVE
;
673 VATTR_SET(&va
, va_type
, VREG
);
674 /* We no longer change the access, but we still hide it. */
675 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
677 /* Obtain the vnode for the shadow files directory. */
678 if (get_shadow_dir(&dvp
, context
) != 0) {
683 /* See if someone else already has it open. */
684 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, context
) == 0) {
685 /* Double check existence by asking for size. */
687 VATTR_WANTED(&va
, va_data_size
);
688 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
689 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
690 goto out
; /* OK to use. */
694 /* Otherwise make sure the resource fork data exists. */
695 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
696 XATTR_NOSECURITY
, context
);
698 * To maintain binary compatibility with legacy Carbon
699 * emulated resource fork support, if the resource fork
700 * doesn't exist but the Finder Info does, then act as
701 * if an empty resource fork is present (see 4724359).
703 if ((error
== ENOATTR
) &&
704 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
705 XATTR_NOSECURITY
, context
) == 0)) {
713 /* If the resource fork exists, its size is expected to be non-zero. */
720 /* Create the shadow stream file. */
721 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, context
);
726 else if ((error
== EEXIST
) && !makestream
) {
727 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, context
);
729 else if ((error
== ENOENT
) && !makestream
) {
731 * We could have raced with a rmdir on the shadow directory
732 * post-lookup. Retry from the beginning, 1x only, to
733 * try and see if we need to re-create the shadow directory
748 /* Otherwise, just error out normally below */
756 /* On errors, clean up shadow stream file. */
764 *rsrcsize
= datasize
;
771 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
773 vnode_t svp
= NULLVP
;
775 caddr_t bufptr
= NULL
;
782 * Only the "com.apple.ResourceFork" stream is supported here.
784 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
790 * Obtain a shadow file for the resource fork I/O.
792 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
799 * The creator of the shadow file provides its file data,
800 * all other threads should wait until its ready. In order to
801 * prevent a deadlock during error codepaths, we need to check if the
802 * vnode is being created, or if it has failed out. Regardless of success or
803 * failure, we set the VISSHADOW bit on the vnode, so we check that
804 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
805 * then we can infer the creator isn't done yet. If it's there, but
806 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
811 if (svp
->v_flag
& VISNAMEDSTREAM
) {
812 /* data is ready, go use it */
816 /* It's not ready, wait for it (sleep using v_parent as channel) */
817 if ((svp
->v_flag
& VISSHADOW
)) {
819 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
820 * thread is done with this vnode. Just unlock the vnode and try again
825 /* Otherwise, sleep if the shadow file is not created yet */
826 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
827 "getnamedstream", NULL
);
836 * Copy the real resource fork data into shadow stream file.
838 if (op
== NS_OPEN
&& datasize
!= 0) {
842 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
843 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
848 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
851 error
= VNOP_OPEN(svp
, 0, context
);
855 while (offset
< datasize
) {
858 iosize
= MIN(datasize
- offset
, iosize
);
860 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
861 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
862 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
863 XATTR_NOSECURITY
, context
);
868 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
869 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
870 error
= VNOP_WRITE(svp
, auio
, 0, context
);
876 (void) VNOP_CLOSE(svp
, 0, context
);
879 /* Wake up anyone waiting for svp file content */
883 /* VISSHADOW would be set later on anyway, so we set it now */
884 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
885 wakeup((caddr_t
)&svp
->v_parent
);
888 /* On post create errors, get rid of the shadow file. This
889 * way if there is another process waiting for initialization
890 * of the shadowfile by the current process will wake up and
891 * retry by creating and initializing the shadow file again.
892 * Also add the VISSHADOW bit here to indicate we're done operating
895 (void)vnode_relenamedstream(vp
, svp
, context
);
897 svp
->v_flag
|= VISSHADOW
;
898 wakeup((caddr_t
)&svp
->v_parent
);
904 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
910 /* On errors, clean up shadow stream file. */
921 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
927 * Only the "com.apple.ResourceFork" stream is supported here.
929 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
933 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
936 * Wake up any waiters over in default_getnamedstream().
938 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
942 /* If we're the creator, mark it as a named stream */
943 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
944 /* Wakeup any waiters on the v_parent channel */
945 wakeup((caddr_t
)&svp
->v_parent
);
954 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
957 * Only the "com.apple.ResourceFork" stream is supported here.
959 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
963 * XXX - what about other opened instances?
965 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
969 get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
)
971 vnode_t dvp
= NULLVP
;
972 vnode_t sdvp
= NULLVP
;
973 struct componentname cn
;
974 struct vnode_attr va
;
980 bzero(tmpname
, sizeof(tmpname
));
981 MAKE_SHADOW_DIRNAME(rootvnode
, tmpname
);
983 * Look up the shadow directory to ensure that it still exists.
984 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
985 * in caching it when multiple threads may be trying to manipulate the pointers.
987 error
= vnode_lookup(tmpname
, 0, &sdvp
, context
);
990 * If we get here, then we have successfully looked up the shadow dir,
991 * and it has an iocount from the lookup. Return the vp in the output argument.
996 /* In the failure case, no iocount is acquired */
998 bzero (tmpname
, sizeof(tmpname
));
1001 * Obtain the vnode for "/var/run" directory.
1002 * This is defined in the SHADOW_DIR_CONTAINER macro
1004 if (vnode_lookup(SHADOW_DIR_CONTAINER
, 0, &dvp
, context
) != 0) {
1010 * Create the shadow stream directory.
1011 * 'dvp' below suggests the parent directory so
1012 * we only need to provide the leaf entry name
1014 MAKE_SHADOW_DIR_LEAF(rootvnode
, tmpname
);
1015 bzero(&cn
, sizeof(cn
));
1016 cn
.cn_nameiop
= LOOKUP
;
1017 cn
.cn_flags
= ISLASTCN
;
1018 cn
.cn_context
= context
;
1019 cn
.cn_pnbuf
= tmpname
;
1020 cn
.cn_pnlen
= sizeof(tmpname
);
1021 cn
.cn_nameptr
= cn
.cn_pnbuf
;
1022 cn
.cn_namelen
= strlen(tmpname
);
1025 * owned by root, only readable by root, hidden
1028 VATTR_SET(&va
, va_uid
, 0);
1029 VATTR_SET(&va
, va_gid
, 0);
1030 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
1031 VATTR_SET(&va
, va_type
, VDIR
);
1032 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
1033 va
.va_vaflags
= VA_EXCLUSIVE
;
1035 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, context
);
1038 * There can be only one winner for an exclusive create.
1040 if (error
== EEXIST
) {
1041 /* loser has to look up directory */
1042 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, context
);
1044 /* Make sure its in fact a directory */
1045 if (sdvp
->v_type
!= VDIR
) {
1048 /* Obtain the fsid for /tmp directory */
1050 VATTR_WANTED(&va
, va_fsid
);
1051 if (VNOP_GETATTR(dvp
, &va
, context
) != 0 ||
1052 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1055 tmp_fsid
= va
.va_fsid
;
1058 VATTR_WANTED(&va
, va_uid
);
1059 VATTR_WANTED(&va
, va_gid
);
1060 VATTR_WANTED(&va
, va_mode
);
1061 VATTR_WANTED(&va
, va_fsid
);
1062 VATTR_WANTED(&va
, va_dirlinkcount
);
1063 VATTR_WANTED(&va
, va_acl
);
1064 /* Provide defaults for attrs that may not be supported */
1065 va
.va_dirlinkcount
= 1;
1066 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1068 if (VNOP_GETATTR(sdvp
, &va
, context
) != 0 ||
1069 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1070 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1071 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1072 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1076 * Make sure its what we want:
1078 * - not writable by anyone
1079 * - on same file system as /tmp
1080 * - not a hard-linked directory
1081 * - no ACLs (they might grant write access)
1083 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1084 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1085 (va
.va_fsid
!= tmp_fsid
) ||
1086 (va
.va_dirlinkcount
!= 1) ||
1087 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1097 /* On errors, clean up shadow stream directory. */
1107 /* This is not the dir we're looking for, move along */
1108 ++shadow_sequence
; /* try something else next time */
1117 * Default Implementation (Non-native EA)
1122 Typical "._" AppleDouble Header File layout:
1123 ------------------------------------------------------------
1128 .-- AD ENTRY[0] Finder Info Entry (must be first)
1129 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1131 | ///////////// Fixed Size Data (32 bytes)
1135 | ATTR ENTRY[1] --+--.
1136 | ATTR ENTRY[2] --+--+--.
1138 | ATTR ENTRY[N] --+--+--+--.
1139 | ATTR DATA 0 <-' | | |
1140 | //////////// | | |
1141 | ATTR DATA 1 <----' | |
1143 | ATTR DATA 2 <-------' |
1146 | ATTR DATA N <----------'
1148 | Attribute Free Space
1150 '----> RESOURCE FORK
1151 ///////////// Variable Sized Data
1160 ------------------------------------------------------------
1162 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1163 stored as part of the Finder Info. The length in the Finder
1164 Info AppleDouble entry includes the length of the extended
1165 attribute header, attribute entries, and attribute data.
1170 * On Disk Data Structures
1172 * Note: Motorola 68K alignment and big-endian.
1174 * See RFC 1740 for additional information about the AppleDouble file format.
1178 #define ADH_MAGIC 0x00051607
1179 #define ADH_VERSION 0x00020000
1180 #define ADH_MACOSX "Mac OS X "
1183 * AppleDouble Entry ID's
1185 #define AD_DATA 1 /* Data fork */
1186 #define AD_RESOURCE 2 /* Resource fork */
1187 #define AD_REALNAME 3 /* FileÕs name on home file system */
1188 #define AD_COMMENT 4 /* Standard Mac comment */
1189 #define AD_ICONBW 5 /* Mac black & white icon */
1190 #define AD_ICONCOLOR 6 /* Mac color icon */
1191 #define AD_UNUSED 7 /* Not used */
1192 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1193 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1194 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1195 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1196 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1197 #define AD_AFPNAME 13 /* Short name on AFP server */
1198 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1199 #define AD_AFPDIRID 15 /* AFP directory ID */
1200 #define AD_ATTRIBUTES AD_FINDERINFO
1203 #define ATTR_FILE_PREFIX "._"
1204 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1206 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1208 /* Implementation Limits */
1209 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1210 #define ATTR_MAX_HDR_SIZE 65536
1212 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1213 * size supported (including the attribute entries). All of
1214 * the attribute entries must reside within this limit. If
1215 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1216 * boundry, then all of the attribute data I/O is performed
1217 * separately from the attribute header I/O.
1219 * In particular, all of the attr_entry structures must lie
1220 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1221 * AppleDouble file. However, the attribute data (i.e. the
1222 * contents of the extended attributes) may extend beyond the
1223 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1224 * limit is to allow the implementation to optimize by reading
1225 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1229 #define FINDERINFOSIZE 32
1231 typedef struct apple_double_entry
{
1232 u_int32_t type
; /* entry type: see list, 0 invalid */
1233 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1234 u_int32_t length
; /* entry data length in bytes. */
1235 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1238 typedef struct apple_double_header
{
1239 u_int32_t magic
; /* == ADH_MAGIC */
1240 u_int32_t version
; /* format version: 2 = 0x00020000 */
1241 u_int32_t filler
[4];
1242 u_int16_t numEntries
; /* number of entries which follow */
1243 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1244 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1245 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1246 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1248 #define ADHDRSIZE (4+4+16+2)
1250 /* Entries are aligned on 4 byte boundaries */
1251 typedef struct attr_entry
{
1252 u_int32_t offset
; /* file offset to data */
1253 u_int32_t length
; /* size of attribute data */
1256 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1257 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1260 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1261 typedef struct attr_header
{
1262 apple_double_header_t appledouble
;
1263 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1264 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1265 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1266 u_int32_t data_start
; /* file offset to attribute data area */
1267 u_int32_t data_length
; /* length of attribute data area */
1268 u_int32_t reserved
[3];
1270 u_int16_t num_attrs
;
1271 } __attribute__((aligned(2), packed
)) attr_header_t
;
1274 /* Empty Resource Fork Header */
1275 typedef struct rsrcfork_header
{
1276 u_int32_t fh_DataOffset
;
1277 u_int32_t fh_MapOffset
;
1278 u_int32_t fh_DataLength
;
1279 u_int32_t fh_MapLength
;
1280 u_int8_t systemData
[112];
1281 u_int8_t appData
[128];
1282 u_int32_t mh_DataOffset
;
1283 u_int32_t mh_MapOffset
;
1284 u_int32_t mh_DataLength
;
1285 u_int32_t mh_MapLength
;
1287 u_int16_t mh_RefNum
;
1289 u_int8_t mh_InMemoryAttr
;
1292 u_int16_t typeCount
;
1293 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1295 #define RF_FIRST_RESOURCE 256
1296 #define RF_NULL_MAP_LENGTH 30
1297 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1299 /* Runtime information about the attribute file. */
1300 typedef struct attr_info
{
1301 vfs_context_t context
;
1306 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1307 apple_double_header_t
*filehdr
;
1308 apple_double_entry_t
*finderinfo
;
1309 apple_double_entry_t
*rsrcfork
;
1310 attr_header_t
*attrhdr
;
1311 attr_entry_t
*attr_entry
;
1313 u_int8_t emptyfinderinfo
;
1317 #define ATTR_SETTING 1
1319 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1321 #define ATTR_ENTRY_LENGTH(namelen) \
1322 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1324 #define ATTR_NEXT(ae) \
1325 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1327 #define ATTR_VALID(ae, ai) \
1328 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1330 #define SWAP16(x) OSSwapBigToHostInt16((x))
1331 #define SWAP32(x) OSSwapBigToHostInt32((x))
1332 #define SWAP64(x) OSSwapBigToHostInt64((x))
1335 static u_int32_t emptyfinfo
[8] = {0};
1339 * Local support routines
1341 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1343 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1345 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1347 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1349 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1351 static void rel_xattrinfo(attr_info_t
*ainfop
);
1353 static int write_xattrinfo(attr_info_t
*ainfop
);
1355 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1357 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1359 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1362 #if BYTE_ORDER == LITTLE_ENDIAN
1363 static void swap_adhdr(apple_double_header_t
*adh
);
1364 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1367 #define swap_adhdr(x)
1368 #define swap_attrhdr(x, y)
1371 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1372 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1373 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1377 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1378 * is in big endian (as it would exist on disk). Verifies the following:
1381 * - number of entries
1382 * - that each entry fits within the file size
1384 * If the header is invalid, ENOATTR is returned.
1386 * NOTE: Does not attempt to validate the extended attributes header that
1387 * may be embedded in the Finder Info entry.
1389 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1392 u_int32_t header_end
;
1393 u_int32_t entry_end
;
1395 apple_double_header_t
*header
;
1397 rawsize
= ainfop
->rawsize
;
1398 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1400 /* Is the file big enough to contain an AppleDouble header? */
1401 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1404 /* Swap the AppleDouble header fields to native order */
1405 header
->magic
= SWAP32(header
->magic
);
1406 header
->version
= SWAP32(header
->version
);
1407 header
->numEntries
= SWAP16(header
->numEntries
);
1409 /* Sanity check the AppleDouble header fields */
1410 if (header
->magic
!= ADH_MAGIC
||
1411 header
->version
!= ADH_VERSION
||
1412 header
->numEntries
< 1 ||
1413 header
->numEntries
> 15) {
1417 /* Calculate where the entries[] array ends */
1418 header_end
= offsetof(apple_double_header_t
, entries
) +
1419 header
->numEntries
* sizeof(apple_double_entry_t
);
1421 /* Is the file big enough to contain the AppleDouble entries? */
1422 if (rawsize
< header_end
) {
1426 /* Swap and sanity check each AppleDouble entry */
1427 for (i
=0; i
<header
->numEntries
; i
++) {
1428 /* Swap the per-entry fields to native order */
1429 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1430 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1431 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1433 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1436 * Does the entry's content start within the header itself,
1437 * did the addition overflow, or does the entry's content
1438 * extend past the end of the file?
1440 if (header
->entries
[i
].offset
< header_end
||
1441 entry_end
< header
->entries
[i
].offset
||
1442 entry_end
> ainfop
->filesize
) {
1447 * Does the current entry's content overlap with a previous
1450 * Yes, this is O(N**2), and there are more efficient algorithms
1451 * for testing pairwise overlap of N ranges when N is large.
1452 * But we have already ensured N < 16, and N is almost always 2.
1453 * So there's no point in using a more complex algorithm.
1456 for (j
=0; j
<i
; j
++) {
1457 if (entry_end
> header
->entries
[j
].offset
&&
1458 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1470 * Retrieve the data of an extended attribute.
1473 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1474 __unused
int options
, vfs_context_t context
)
1478 attr_header_t
*header
;
1479 attr_entry_t
*entry
;
1489 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1492 * Open the file locked (shared) since the Carbon
1493 * File Manager may have the Apple Double file open
1494 * and could be changing the resource fork.
1496 fileflags
|= O_SHLOCK
;
1501 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1504 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1505 close_xattrfile(xvp
, fileflags
, context
);
1509 /* Get the Finder Info. */
1510 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1512 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1514 } else if (uio
== NULL
) {
1515 *size
= FINDERINFOSIZE
;
1517 } else if (uio_offset(uio
) != 0) {
1519 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1522 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1523 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1528 /* Read the Resource Fork. */
1530 if (!vnode_isreg(vp
)) {
1532 } else if (ainfo
.rsrcfork
== NULL
) {
1534 } else if (uio
== NULL
) {
1535 *size
= (size_t)ainfo
.rsrcfork
->length
;
1537 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1538 error
= VNOP_READ(xvp
, uio
, 0, context
);
1540 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1545 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1549 if (uio_offset(uio
) != 0) {
1554 namelen
= strlen(name
) + 1;
1555 header
= ainfo
.attrhdr
;
1556 entry
= ainfo
.attr_entry
;
1558 * Search for attribute name in the header.
1560 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1561 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1562 datalen
= (size_t)entry
->length
;
1568 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1572 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1573 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1574 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1576 uio_setoffset(uio
, entry
->offset
);
1577 error
= VNOP_READ(xvp
, uio
, 0, context
);
1578 uio_setoffset(uio
, 0);
1582 entry
= ATTR_NEXT(entry
);
1585 rel_xattrinfo(&ainfo
);
1586 close_xattrfile(xvp
, fileflags
, context
);
1592 * Set the data of an extended attribute.
1595 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1599 attr_header_t
*header
;
1600 attr_entry_t
*entry
;
1601 attr_entry_t
*lastentry
;
1605 size_t datafreespace
;
1612 char finfo
[FINDERINFOSIZE
];
1614 datalen
= uio_resid(uio
);
1615 namelen
= strlen(name
) + 1;
1616 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1619 * By convention, Finder Info that is all zeroes is equivalent to not
1620 * having a Finder Info EA. So if we're trying to set the Finder Info
1621 * to all zeroes, then delete it instead. If a file didn't have an
1622 * AppleDouble file before, this prevents creating an AppleDouble file
1623 * with no useful content.
1625 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1626 * for all zeroes Finder Info before opening the AppleDouble file.
1627 * But if either of those options were specified, we need to open the
1628 * AppleDouble file to see whether there was already Finder Info (so we
1629 * can return an error if needed); this case is handled further below.
1631 * NOTE: this copies the Finder Info data into the "finfo" local.
1633 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1635 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1636 * That means we probably have to open_xattrfile and get_xattrinfo.
1638 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1641 error
= uiomove(finfo
, datalen
, uio
);
1644 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1645 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1646 error
= default_removexattr(vp
, name
, 0, context
);
1647 if (error
== ENOATTR
)
1655 * Open the file locked since setting an attribute
1656 * can change the layout of the Apple Double file.
1658 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1659 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1662 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1663 close_xattrfile(xvp
, fileflags
, context
);
1667 /* Set the Finder Info. */
1668 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1669 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1670 /* attr exists and "create" was specified? */
1671 if (options
& XATTR_CREATE
) {
1676 /* attr doesn't exists and "replace" was specified? */
1677 if (options
& XATTR_REPLACE
) {
1682 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1684 * Setting the Finder Info to all zeroes is equivalent to
1685 * removing it. Close the xattr file and let
1686 * default_removexattr do the work (including deleting
1687 * the xattr file if there are no other xattrs).
1689 * Note that we have to handle the case where the
1690 * Finder Info was already all zeroes, and we ignore
1693 * The common case where options == 0 was handled above.
1695 rel_xattrinfo(&ainfo
);
1696 close_xattrfile(xvp
, fileflags
, context
);
1697 error
= default_removexattr(vp
, name
, 0, context
);
1698 if (error
== ENOATTR
)
1702 if (ainfo
.finderinfo
) {
1703 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1704 bcopy(finfo
, attrdata
, datalen
);
1705 ainfo
.iosize
= sizeof(attr_header_t
);
1706 error
= write_xattrinfo(&ainfo
);
1713 /* Write the Resource Fork. */
1714 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1715 u_int32_t endoffset
;
1717 if (!vnode_isreg(vp
)) {
1721 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
1722 /* attr exists and "create" was specified? */
1723 if (options
& XATTR_CREATE
) {
1728 /* attr doesn't exists and "replace" was specified? */
1729 if (options
& XATTR_REPLACE
) {
1734 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1735 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1736 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1739 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1740 if (endoffset
> ainfo
.rsrcfork
->length
) {
1741 ainfo
.rsrcfork
->length
= endoffset
;
1742 ainfo
.iosize
= sizeof(attr_header_t
);
1743 error
= write_xattrinfo(&ainfo
);
1749 if (datalen
> ATTR_MAX_SIZE
) {
1750 return (E2BIG
); /* EINVAL instead ? */
1753 if (ainfo
.attrhdr
== NULL
) {
1757 header
= ainfo
.attrhdr
;
1758 entry
= ainfo
.attr_entry
;
1760 /* Check if data area crosses the maximum header size. */
1761 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1762 splitdata
= 1; /* do data I/O separately */
1767 * See if attribute already exists.
1769 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1770 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1774 entry
= ATTR_NEXT(entry
);
1778 if (options
& XATTR_CREATE
) {
1782 if (datalen
== entry
->length
) {
1784 uio_setoffset(uio
, entry
->offset
);
1785 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1786 uio_setoffset(uio
, 0);
1788 printf("setxattr: VNOP_WRITE error %d\n", error
);
1791 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1792 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1795 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1796 error
= write_xattrinfo(&ainfo
);
1798 printf("setxattr: write_xattrinfo error %d\n", error
);
1804 * Brute force approach - just remove old entry and set new entry.
1807 rel_xattrinfo(&ainfo
);
1808 close_xattrfile(xvp
, fileflags
, context
);
1809 error
= default_removexattr(vp
, name
, options
, context
);
1813 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1814 options
&= ~XATTR_REPLACE
;
1815 goto start
; /* start over */
1820 if (options
& XATTR_REPLACE
) {
1821 error
= ENOATTR
; /* nothing there to replace */
1824 /* Check if header size limit has been reached. */
1825 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1830 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1832 /* Check if we need more space. */
1833 if ((datalen
+ entrylen
) > datafreespace
) {
1836 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1838 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1839 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1840 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1843 ainfo
.filesize
+= growsize
;
1844 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1846 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1852 * Move the resource fork out of the way.
1854 if (ainfo
.rsrcfork
) {
1855 if (ainfo
.rsrcfork
->length
!= 0) {
1856 shift_data_down(xvp
,
1857 ainfo
.rsrcfork
->offset
,
1858 ainfo
.rsrcfork
->length
,
1861 ainfo
.rsrcfork
->offset
+= growsize
;
1863 ainfo
.finderinfo
->length
+= growsize
;
1864 header
->total_size
+= growsize
;
1867 /* Make space for a new entry. */
1869 shift_data_down(xvp
,
1871 header
->data_length
,
1874 bcopy((u_int8_t
*)header
+ header
->data_start
,
1875 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
1876 header
->data_length
);
1878 header
->data_start
+= entrylen
;
1880 /* Fix up entry data offsets. */
1882 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
1883 entry
->offset
+= entrylen
;
1887 * If the attribute data area is entirely within
1888 * the header buffer, then just update the buffer,
1889 * otherwise we'll write it separately to the file.
1894 /* Write new attribute data after the end of existing data. */
1895 offset
= header
->data_start
+ header
->data_length
;
1896 uio_setoffset(uio
, offset
);
1897 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1898 uio_setoffset(uio
, 0);
1900 printf("setxattr: VNOP_WRITE error %d\n", error
);
1904 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
1906 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1908 printf("setxattr: uiomove error %d\n", error
);
1913 /* Create the attribute entry. */
1914 lastentry
->length
= datalen
;
1915 lastentry
->offset
= header
->data_start
+ header
->data_length
;
1916 lastentry
->namelen
= namelen
;
1917 lastentry
->flags
= 0;
1918 bcopy(name
, &lastentry
->name
[0], namelen
);
1920 /* Update the attributes header. */
1921 header
->num_attrs
++;
1922 header
->data_length
+= datalen
;
1925 /* Only write the entries, since the data was written separately. */
1926 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
1928 /* The entry and data are both in the header; write them together. */
1929 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1931 error
= write_xattrinfo(&ainfo
);
1933 printf("setxattr: write_xattrinfo error %d\n", error
);
1937 rel_xattrinfo(&ainfo
);
1938 close_xattrfile(xvp
, fileflags
, context
);
1940 /* Touch the change time if we changed an attribute. */
1942 struct vnode_attr va
;
1944 /* Re-write the mtime to cause a ctime change. */
1946 VATTR_WANTED(&va
, va_modify_time
);
1947 if (vnode_getattr(vp
, &va
, context
) == 0) {
1949 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1950 (void) vnode_setattr(vp
, &va
, context
);
1954 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
1961 * Remove an extended attribute.
1964 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
1968 attr_header_t
*header
;
1969 attr_entry_t
*entry
;
1970 attr_entry_t
*oldslot
;
1976 int found
= 0, lastone
= 0;
1984 fileflags
= FREAD
| FWRITE
;
1985 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1988 * Open the file locked (exclusive) since the Carbon
1989 * File Manager may have the Apple Double file open
1990 * and could be changing the resource fork.
1992 fileflags
|= O_EXLOCK
;
1997 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
2000 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2001 close_xattrfile(xvp
, fileflags
, context
);
2005 attrcount
+= ainfo
.attrhdr
->num_attrs
;
2008 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
2011 /* Clear the Finder Info. */
2012 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
2013 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
2017 /* On removal of last attribute the ._ file is removed. */
2018 if (--attrcount
== 0)
2020 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
2021 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
2022 ainfo
.iosize
= sizeof(attr_header_t
);
2023 error
= write_xattrinfo(&ainfo
);
2027 /* Clear the Resource Fork. */
2029 if (!vnode_isreg(vp
)) {
2033 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
2037 /* On removal of last attribute the ._ file is removed. */
2038 if (--attrcount
== 0)
2042 * If the resource fork isn't the last AppleDouble
2043 * entry then the space needs to be reclaimed by
2044 * shifting the entries after the resource fork.
2046 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
2047 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
2048 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2051 ainfo
.rsrcfork
->length
= 0;
2052 ainfo
.iosize
= sizeof(attr_header_t
);
2053 error
= write_xattrinfo(&ainfo
);
2058 if (ainfo
.attrhdr
== NULL
) {
2062 namelen
= strlen(name
) + 1;
2063 header
= ainfo
.attrhdr
;
2064 entry
= ainfo
.attr_entry
;
2067 * See if this attribute exists.
2069 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2070 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2072 if ((i
+1) == header
->num_attrs
)
2076 entry
= ATTR_NEXT(entry
);
2082 /* On removal of last attribute the ._ file is removed. */
2083 if (--attrcount
== 0)
2086 datalen
= entry
->length
;
2087 dataoff
= entry
->offset
;
2088 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2089 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
2094 /* Remove the attribute entry. */
2096 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2097 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2100 /* Adjust the attribute data. */
2104 dataoff
- header
->data_start
,
2110 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2114 /* XXX write zeros to freed space ? */
2115 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2119 bcopy((u_int8_t
*)header
+ header
->data_start
,
2120 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2121 dataoff
- header
->data_start
);
2123 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2124 (u_int8_t
*)header
+ dataoff
- entrylen
,
2125 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2127 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2128 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2131 /* Adjust the header values and entry offsets. */
2132 header
->num_attrs
--;
2133 header
->data_start
-= entrylen
;
2134 header
->data_length
-= datalen
;
2137 entry
= ainfo
.attr_entry
;
2138 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2139 entry
->offset
-= entrylen
;
2140 if (entry
>= oldslot
)
2141 entry
->offset
-= datalen
;
2142 entry
= ATTR_NEXT(entry
);
2144 error
= write_xattrinfo(&ainfo
);
2146 printf("removexattr: write_xattrinfo error %d\n", error
);
2149 rel_xattrinfo(&ainfo
);
2151 /* When there are no more attributes remove the ._ file. */
2152 if (attrcount
== 0) {
2153 if (fileflags
& O_EXLOCK
)
2154 (void) unlock_xattrfile(xvp
, context
);
2155 VNOP_CLOSE(xvp
, fileflags
, context
);
2157 error
= remove_xattrfile(xvp
, context
);
2160 close_xattrfile(xvp
, fileflags
, context
);
2162 /* Touch the change time if we changed an attribute. */
2164 struct vnode_attr va
;
2166 /* Re-write the mtime to cause a ctime change. */
2168 VATTR_WANTED(&va
, va_modify_time
);
2169 if (vnode_getattr(vp
, &va
, context
) == 0) {
2171 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2172 (void) vnode_setattr(vp
, &va
, context
);
2176 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2184 * Retrieve the list of extended attribute names.
2187 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2191 attr_entry_t
*entry
;
2196 * We do not zero "*size" here as we don't want to stomp a size set when
2197 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2198 * system call layer, up in listxattr or flistxattr.
2201 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2202 if (error
== ENOATTR
)
2206 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2207 if (error
== ENOATTR
)
2209 close_xattrfile(xvp
, FREAD
, context
);
2213 /* Check for Finder Info. */
2214 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2216 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2217 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2221 error
= uiomove(XATTR_FINDERINFO_NAME
,
2222 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2230 /* Check for Resource Fork. */
2231 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2233 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2234 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2238 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2239 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2247 /* Check for attributes. */
2248 if (ainfo
.attrhdr
) {
2249 count
= ainfo
.attrhdr
->num_attrs
;
2250 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2251 if (xattr_protected((const char *)entry
->name
) ||
2252 xattr_validatename((const char *)entry
->name
) != 0) {
2253 entry
= ATTR_NEXT(entry
);
2257 *size
+= entry
->namelen
;
2258 entry
= ATTR_NEXT(entry
);
2261 if (uio_resid(uio
) < entry
->namelen
) {
2265 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2267 if (error
!= EFAULT
)
2271 entry
= ATTR_NEXT(entry
);
2275 rel_xattrinfo(&ainfo
);
2276 close_xattrfile(xvp
, FREAD
, context
);
2282 * Check the header of a ._ file to verify that it is in fact an Apple Double
2283 * file. Returns 0 if the header is valid, non-zero if invalid.
2285 int check_appledouble_header(vnode_t vp
, vfs_context_t ctx
)
2289 struct vnode_attr va
;
2291 void *buffer
= NULL
;
2295 ainfo
.context
= ctx
;
2297 VATTR_WANTED(&va
, va_data_size
);
2298 if ((error
= vnode_getattr(vp
, &va
, ctx
))) {
2301 ainfo
.filesize
= va
.va_data_size
;
2303 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfo
.filesize
);
2308 ainfo
.iosize
= iosize
;
2310 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2311 if (buffer
== NULL
) {
2316 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2317 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2319 /* Read the header */
2320 error
= VNOP_READ(vp
, auio
, 0, ctx
);
2324 ainfo
.rawsize
= iosize
- uio_resid(auio
);
2325 ainfo
.rawdata
= (u_int8_t
*)buffer
;
2327 error
= check_and_swap_apple_double_header(&ainfo
);
2332 /* If we made it here, then the header is ok */
2339 FREE(buffer
, M_TEMP
);
2346 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2348 vnode_t xvp
= NULLVP
;
2349 vnode_t dvp
= NULLVP
;
2350 struct vnode_attr va
;
2351 struct nameidata nd
;
2353 char *filename
= NULL
;
2354 const char *basename
= NULL
;
2360 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2362 * For the root directory use "._." to hold the attributes.
2364 filename
= &smallname
[0];
2365 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2366 dvp
= vp
; /* the "._." file resides in the root dir */
2369 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2373 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2378 /* "._" Attribute files cannot have attributes */
2379 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2380 basename
[0] == '.' && basename
[1] == '_') {
2384 filename
= &smallname
[0];
2385 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2386 if (len
>= sizeof(smallname
)) {
2387 len
++; /* snprintf result doesn't include '\0' */
2388 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2389 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2392 * Note that the lookup here does not authorize. Since we are looking
2393 * up in the same directory that we already have the file vnode in,
2394 * we must have been given the file vnode legitimately. Read/write
2395 * access has already been authorized in layers above for calls from
2396 * userspace, and the authorization code using this path to read
2397 * file security from the EA must always get access
2400 NDINIT(&nd
, LOOKUP
, OP_OPEN
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
,
2401 UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), context
);
2404 if (fileflags
& O_CREAT
) {
2405 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2410 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2412 if ( (error
= namei(&nd
))) {
2417 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2423 * Pick up uid/gid/mode from target file.
2426 VATTR_WANTED(&va
, va_uid
);
2427 VATTR_WANTED(&va
, va_gid
);
2428 VATTR_WANTED(&va
, va_mode
);
2429 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2430 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2431 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2432 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2435 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2436 } else /* fallback values */ {
2437 uid
= KAUTH_UID_NONE
;
2438 gid
= KAUTH_GID_NONE
;
2439 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2443 VATTR_SET(&va
, va_type
, VREG
);
2444 VATTR_SET(&va
, va_mode
, umode
);
2445 if (uid
!= KAUTH_UID_NONE
)
2446 VATTR_SET(&va
, va_uid
, uid
);
2447 if (gid
!= KAUTH_GID_NONE
)
2448 VATTR_SET(&va
, va_gid
, gid
);
2450 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
, &va
,
2451 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2461 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2466 if ((error
= namei(&nd
))) {
2476 if (xvp
->v_type
!= VREG
) {
2481 * Owners must match.
2484 VATTR_WANTED(&va
, va_uid
);
2485 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2486 uid_t owner
= va
.va_uid
;
2489 VATTR_WANTED(&va
, va_uid
);
2490 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2491 error
= ENOATTR
; /* don't use this "._" file */
2496 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2502 if ((error
= vnode_ref(xvp
))) {
2507 /* If create was requested, make sure file header exists. */
2508 if (fileflags
& O_CREAT
) {
2510 VATTR_WANTED(&va
, va_data_size
);
2511 VATTR_WANTED(&va
, va_fileid
);
2512 VATTR_WANTED(&va
, va_nlink
);
2513 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2518 /* If the file is empty then add a default header. */
2519 if (va
.va_data_size
== 0) {
2520 /* Don't adopt hard-linked "._" files. */
2521 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2525 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2529 /* Apply file locking if requested. */
2530 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2533 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2534 error
= lock_xattrfile(xvp
, locktype
, context
);
2539 if (dvp
&& (dvp
!= vp
)) {
2543 vnode_putname(basename
);
2545 if (filename
&& filename
!= &smallname
[0]) {
2546 FREE(filename
, M_TEMP
);
2549 if (xvp
!= NULLVP
) {
2551 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2554 (void) vnode_rele(xvp
);
2556 (void) vnode_put(xvp
);
2559 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2563 *xvpp
= xvp
; /* return a referenced vnode */
2568 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2570 // if (fileflags & FWRITE)
2571 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2573 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2574 (void) unlock_xattrfile(xvp
, context
);
2576 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2577 (void) vnode_rele(xvp
);
2578 (void) vnode_put(xvp
);
2582 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2585 struct nameidata nd
;
2590 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2594 pathlen
= MAXPATHLEN
;
2595 error
= vn_getpath(xvp
, path
, &pathlen
);
2597 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2601 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2602 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2604 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2611 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2620 * Read in and parse the AppleDouble header and entries, and the extended
2621 * attribute header and entries if any. Populates the fields of ainfop
2622 * based on the headers and entries found.
2624 * The basic idea is to:
2625 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2626 * AppleDouble entries, the extended attribute header, and extended
2627 * attribute entries must lie within this part of the file; the rest of
2628 * the AppleDouble handling code assumes this. Plus it allows us to
2629 * somewhat optimize by doing a smaller number of larger I/Os.
2630 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2632 * - Find the Finder Info and Resource Fork entries, if any.
2633 * - If we're going to be writing, try to make sure the Finder Info entry has
2634 * room to store the extended attribute header, plus some space for extended
2636 * - Swap and sanity check the extended attribute header and entries (if any).
2639 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2642 void * buffer
= NULL
;
2643 apple_double_header_t
*filehdr
;
2644 struct vnode_attr va
;
2649 bzero(ainfop
, sizeof(attr_info_t
));
2650 ainfop
->filevp
= xvp
;
2651 ainfop
->context
= context
;
2653 VATTR_WANTED(&va
, va_data_size
);
2654 VATTR_WANTED(&va
, va_fileid
);
2655 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2658 ainfop
->filesize
= va
.va_data_size
;
2660 /* When setting attributes, allow room for the header to grow. */
2662 iosize
= ATTR_MAX_HDR_SIZE
;
2664 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2670 ainfop
->iosize
= iosize
;
2671 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2672 if (buffer
== NULL
){
2677 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2678 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2680 /* Read the file header. */
2681 error
= VNOP_READ(xvp
, auio
, 0, context
);
2685 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2686 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2688 filehdr
= (apple_double_header_t
*)buffer
;
2690 error
= check_and_swap_apple_double_header(ainfop
);
2694 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2696 /* rel_xattrinfo is responsible for freeing the header buffer */
2699 /* Find the Finder Info and Resource Fork entries, if any */
2700 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2701 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2702 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2703 /* We found the Finder Info entry. */
2704 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2707 * Is the Finder Info "empty" (all zeroes)? If so,
2708 * we'll pretend like the Finder Info extended attribute
2711 * Note: we have to make sure the Finder Info is
2712 * contained within the buffer we have already read,
2713 * to avoid accidentally accessing a bogus address.
2714 * If it is outside the buffer, we just assume the
2715 * Finder Info is non-empty.
2717 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2718 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2719 ainfop
->emptyfinderinfo
= 1;
2722 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2724 * Ignore zero-length resource forks when getting. If setting,
2725 * we need to remember the resource fork entry so it can be
2726 * updated once the new content has been written.
2728 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2732 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2734 * The "empty" resource headers we created have a system data tag of:
2735 * "This resource fork intentionally left blank "
2737 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2739 u_int8_t systemData
[64];
2743 /* Read the system data which starts at byte 16 */
2744 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2745 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2746 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2747 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2751 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2752 continue; /* skip this resource fork */
2755 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2756 if (i
!= (filehdr
->numEntries
- 1)) {
2757 printf("get_xattrinfo: resource fork not last entry\n");
2758 ainfop
->readonly
= 1;
2765 * See if this file looks like it is laid out correctly to contain
2766 * extended attributes. If so, then do the following:
2768 * - If we're going to be writing, try to make sure the Finder Info
2769 * entry has room to store the extended attribute header, plus some
2770 * space for extended attributes.
2772 * - Swap and sanity check the extended attribute header and entries
2775 if (filehdr
->numEntries
== 2 &&
2776 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2777 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2778 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2779 attr_header_t
*attrhdr
;
2780 attrhdr
= (attr_header_t
*)filehdr
;
2782 * If we're going to be writing, try to make sure the Finder
2783 * Info entry has room to store the extended attribute header,
2784 * plus some space for extended attributes.
2786 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2790 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2791 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2792 /* Make some room before existing resource fork. */
2793 shift_data_down(xvp
,
2794 filehdr
->entries
[1].offset
,
2795 filehdr
->entries
[1].length
,
2797 writesize
= sizeof(attr_header_t
);
2799 /* Create a new, empty resource fork. */
2800 rsrcfork_header_t
*rsrcforkhdr
;
2802 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2804 /* Steal some space for an empty RF header. */
2805 delta
-= sizeof(rsrcfork_header_t
);
2807 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2808 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2810 /* Fill in Empty Resource Fork Header. */
2811 init_empty_resource_fork(rsrcforkhdr
);
2813 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2814 writesize
= ATTR_BUF_SIZE
;
2816 filehdr
->entries
[0].length
+= delta
;
2817 filehdr
->entries
[1].offset
+= delta
;
2819 /* Fill in Attribute Header. */
2820 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2821 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2822 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2823 attrhdr
->data_start
= sizeof(attr_header_t
);
2824 attrhdr
->data_length
= 0;
2825 attrhdr
->reserved
[0] = 0;
2826 attrhdr
->reserved
[1] = 0;
2827 attrhdr
->reserved
[2] = 0;
2829 attrhdr
->num_attrs
= 0;
2831 /* Push out new header */
2832 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2833 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2835 swap_adhdr(filehdr
); /* to big endian */
2836 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2837 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2838 swap_adhdr(filehdr
); /* back to native */
2839 /* The attribute header gets swapped below. */
2843 * Swap and sanity check the extended attribute header and
2844 * entries (if any). The Finder Info content must be big enough
2845 * to include the extended attribute header; if not, we just
2848 * Note that we're passing the offset + length (i.e. the end)
2849 * of the Finder Info instead of rawsize to validate_attrhdr.
2850 * This ensures that all extended attributes lie within the
2851 * Finder Info content according to the AppleDouble entry.
2853 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2856 if (ainfop
->finderinfo
&&
2857 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2858 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2859 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2861 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2862 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2863 /* First attr_entry starts immediately following attribute header */
2864 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2873 FREE(buffer
, M_TEMP
);
2879 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2882 rsrcfork_header_t
*rsrcforkhdr
;
2888 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2889 bzero(buffer
, ATTR_BUF_SIZE
);
2891 xah
= (attr_header_t
*)buffer
;
2892 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2893 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2894 rsrcforksize
= sizeof(rsrcfork_header_t
);
2895 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2897 /* Fill in Apple Double Header. */
2898 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2899 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2900 xah
->appledouble
.numEntries
= SWAP16 (2);
2901 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2902 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2903 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2904 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2905 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2906 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2907 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2909 /* Fill in Attribute Header. */
2910 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2911 xah
->debug_tag
= SWAP32 (fileid
);
2912 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2913 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2915 /* Fill in Empty Resource Fork Header. */
2916 init_empty_resource_fork(rsrcforkhdr
);
2919 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2922 FREE(buffer
, M_TEMP
);
2928 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
2930 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
2931 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2932 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2933 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2934 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2935 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2936 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2937 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
2938 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
2939 rsrcforkhdr
->typeCount
= SWAP16 (-1);
2940 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
2944 rel_xattrinfo(attr_info_t
*ainfop
)
2946 FREE(ainfop
->filehdr
, M_TEMP
);
2947 bzero(ainfop
, sizeof(attr_info_t
));
2951 write_xattrinfo(attr_info_t
*ainfop
)
2956 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2957 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
2959 swap_adhdr(ainfop
->filehdr
);
2960 if (ainfop
->attrhdr
!= NULL
) {
2961 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2964 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
2966 swap_adhdr(ainfop
->filehdr
);
2967 if (ainfop
->attrhdr
!= NULL
) {
2968 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2975 #if BYTE_ORDER == LITTLE_ENDIAN
2977 * Endian swap apple double header
2980 swap_adhdr(apple_double_header_t
*adh
)
2985 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
2987 adh
->magic
= SWAP32 (adh
->magic
);
2988 adh
->version
= SWAP32 (adh
->version
);
2989 adh
->numEntries
= SWAP16 (adh
->numEntries
);
2991 for (i
= 0; i
< count
; i
++) {
2992 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
2993 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
2994 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
2999 * Endian swap extended attributes header
3002 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
3008 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
3010 ah
->magic
= SWAP32 (ah
->magic
);
3011 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3012 ah
->total_size
= SWAP32 (ah
->total_size
);
3013 ah
->data_start
= SWAP32 (ah
->data_start
);
3014 ah
->data_length
= SWAP32 (ah
->data_length
);
3015 ah
->flags
= SWAP16 (ah
->flags
);
3016 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3018 ae
= (attr_entry_t
*)(&ah
[1]);
3019 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
3020 ae
->offset
= SWAP32 (ae
->offset
);
3021 ae
->length
= SWAP32 (ae
->length
);
3022 ae
->flags
= SWAP16 (ae
->flags
);
3028 * Validate and swap the attributes header contents, and each attribute's
3031 * Note: Assumes the caller has verified that the Finder Info content is large
3032 * enough to contain the attr_header structure itself. Therefore, we can
3033 * swap the header fields before sanity checking them.
3036 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
3047 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
3050 /* Swap the basic header fields */
3051 ah
->magic
= SWAP32(ah
->magic
);
3052 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
3053 ah
->total_size
= SWAP32 (ah
->total_size
);
3054 ah
->data_start
= SWAP32 (ah
->data_start
);
3055 ah
->data_length
= SWAP32 (ah
->data_length
);
3056 ah
->flags
= SWAP16 (ah
->flags
);
3057 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3060 * Make sure the total_size fits within the Finder Info area, and the
3061 * extended attribute data area fits within total_size.
3063 end
= ah
->data_start
+ ah
->data_length
;
3064 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3065 end
< ah
->data_start
||
3066 end
> ah
->total_size
) {
3071 * Make sure each of the attr_entry_t's fits within total_size.
3073 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3074 count
= ah
->num_attrs
;
3075 ae
= (attr_entry_t
*)(&ah
[1]);
3077 for (i
=0; i
<count
; i
++) {
3078 /* Make sure the fixed-size part of this attr_entry_t fits. */
3079 if ((u_int8_t
*) &ae
[1] > buf_end
)
3082 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3083 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3084 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
3087 /* Swap the attribute entry fields */
3088 ae
->offset
= SWAP32(ae
->offset
);
3089 ae
->length
= SWAP32(ae
->length
);
3090 ae
->flags
= SWAP16(ae
->flags
);
3092 /* Make sure the attribute content fits. */
3093 end
= ae
->offset
+ ae
->length
;
3094 if (end
< ae
->offset
|| end
> ah
->total_size
)
3101 * TODO: Make sure the contents of attributes don't overlap the header
3102 * and don't overlap each other. The hard part is that we don't know
3103 * what the actual header size is until we have looped over all of the
3104 * variable-sized attribute entries.
3106 * XXX Is there any guarantee that attribute entries are stored in
3107 * XXX order sorted by the contents' file offset? If so, that would
3108 * XXX make the pairwise overlap check much easier.
3115 // "start" & "end" are byte offsets in the file.
3116 // "to" is the byte offset we want to move the
3117 // data to. "to" should be > "start".
3119 // we do the copy backwards to avoid problems if
3120 // there's an overlap.
3123 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3126 size_t chunk
, orig_chunk
;
3129 kauth_cred_t ucred
= vfs_context_ucred(context
);
3130 proc_t p
= vfs_context_proc(context
);
3132 if (delta
== 0 || len
== 0) {
3142 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3146 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
3147 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3149 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3150 pos
, ret
, chunk
, ret
);
3154 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3156 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3157 pos
+delta
, ret
, chunk
, ret
);
3161 if ((pos
- (off_t
)chunk
) < start
) {
3162 chunk
= pos
- start
;
3164 if (chunk
== 0) { // we're all done
3169 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3176 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3179 size_t chunk
, orig_chunk
;
3183 kauth_cred_t ucred
= vfs_context_ucred(context
);
3184 proc_t p
= vfs_context_proc(context
);
3186 if (delta
== 0 || len
== 0) {
3197 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3201 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3202 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3204 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3205 pos
, ret
, chunk
, ret
);
3209 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3211 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3212 pos
+delta
, ret
, chunk
, ret
);
3216 if ((pos
+ (off_t
)chunk
) > end
) {
3219 if (chunk
== 0) { // we're all done
3224 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3230 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3235 lf
.l_whence
= SEEK_SET
;
3238 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3239 /* Note: id is just a kernel address that's not a proc */
3240 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
);
3241 return (error
== ENOTSUP
? 0 : error
);
3245 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3250 lf
.l_whence
= SEEK_SET
;
3253 lf
.l_type
= F_UNLCK
;
3254 /* Note: id is just a kernel address that's not a proc */
3255 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);
3256 return (error
== ENOTSUP
? 0 : error
);