2 * Copyright (c) 2004-2008 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>
63 * We use %p to prevent loss of precision for pointers on varying architectures.
65 #define MAKE_SHADOW_NAME(VP, NAME) \
66 snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%p%08x%p", (void*)(VP), (VP)->v_id, (VP)->v_data);
68 static vnode_t shadow_dvp
; /* tmp directory to hold stream shadow files */
69 static int shadow_vid
;
70 static int shadow_sequence
;
73 static int default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
);
75 static int default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
);
77 static int default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
);
79 static int getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
, int *creator
, vfs_context_t context
);
81 static int get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
);
87 * Default xattr support routines.
90 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
91 vfs_context_t context
);
96 * Retrieve the data of an extended attribute.
99 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
100 int options
, vfs_context_t context
)
104 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
108 /* getxattr calls are not allowed for streams. */
109 if (vp
->v_flag
& VISNAMEDSTREAM
) {
115 * Non-kernel request need extra checks performed.
117 * The XATTR_NOSECURITY flag implies a kernel request.
119 if (!(options
& XATTR_NOSECURITY
)) {
121 error
= mac_vnode_check_getextattr(context
, vp
, name
, uio
);
125 if ((error
= xattr_validatename(name
))) {
128 if ((error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
))) {
131 /* The offset can only be non-zero for resource forks. */
132 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
133 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
139 /* The offset can only be non-zero for resource forks. */
140 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
141 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
146 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
147 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
149 * A filesystem may keep some EAs natively and return ENOTSUP for others.
150 * SMB returns ENOTSUP for finderinfo and resource forks.
152 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
159 * Set the data of an extended attribute.
162 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
166 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
170 /* setxattr calls are not allowed for streams. */
171 if (vp
->v_flag
& VISNAMEDSTREAM
) {
176 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
179 if ((error
= xattr_validatename(name
))) {
182 if (!(options
& XATTR_NOSECURITY
)) {
184 error
= mac_vnode_check_setextattr(context
, vp
, name
, uio
);
188 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
192 /* The offset can only be non-zero for resource forks. */
193 if (uio_offset(uio
) != 0 &&
194 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0 ) {
199 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
202 * An EJUSTRETURN is from a filesystem which keeps this xattr
203 * natively as well as in a dot-underscore file. In this case the
204 * EJUSTRETURN means the filesytem has done nothing, but identifies the
205 * EA as one which may be represented natively and/or in a DU, and
206 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
207 * in vn_setxattr can we do the getxattrs needed to ascertain whether
208 * the XATTR_{CREATE,REPLACE} should yield an error.
210 if (error
== EJUSTRETURN
) {
211 int native
= 0, dufile
= 0;
212 size_t sz
; /* not used */
214 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
215 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
216 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
220 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
225 * Having determined no CREATE/REPLACE error should result, we
226 * zero those bits, so both backing stores get written to.
228 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
229 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
230 /* the mainline path here is to have error==ENOTSUP ... */
232 #endif /* DUAL_EAS */
233 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
235 * A filesystem may keep some EAs natively and return ENOTSUP for others.
236 * SMB returns ENOTSUP for finderinfo and resource forks.
238 error
= default_setxattr(vp
, name
, uio
, options
, context
);
241 if ((error
== 0) && !(options
& XATTR_NOSECURITY
) &&
242 (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
))
243 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
250 * Remove an extended attribute.
253 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
257 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
261 /* removexattr calls are not allowed for streams. */
262 if (vp
->v_flag
& VISNAMEDSTREAM
) {
267 if ((error
= xattr_validatename(name
))) {
270 if (!(options
& XATTR_NOSECURITY
)) {
272 error
= mac_vnode_check_deleteextattr(context
, vp
, name
);
276 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
);
280 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
281 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
283 * A filesystem may keep some EAs natively and return ENOTSUP for others.
284 * SMB returns ENOTSUP for finderinfo and resource forks.
286 error
= default_removexattr(vp
, name
, options
, context
);
288 } else if (error
== EJUSTRETURN
) {
290 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
291 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
292 * a native xattr, so failure to find it in a DU file during
293 * default_removexattr should not be considered an error.
295 error
= default_removexattr(vp
, name
, options
, context
);
296 if (error
== ENOATTR
)
298 #endif /* DUAL_EAS */
301 if ((error
== 0) && !(options
& XATTR_NOSECURITY
) &&
302 (vfs_flags(vnode_mount(vp
)) & MNT_MULTILABEL
))
303 mac_vnode_label_update_extattr(vnode_mount(vp
), vp
, name
);
310 * Retrieve the list of extended attribute names.
313 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
317 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
321 /* listxattr calls are not allowed for streams. */
322 if (vp
->v_flag
& VISNAMEDSTREAM
) {
327 if (!(options
& XATTR_NOSECURITY
)) {
329 error
= mac_vnode_check_listextattr(context
, vp
);
334 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
);
339 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
340 if (error
== ENOTSUP
&& !(options
& XATTR_NODEFAULT
)) {
342 * A filesystem may keep some but not all EAs natively, in which case
343 * the native EA names will have been uiomove-d out (or *size updated)
344 * and the default_listxattr here will finish the job. Note SMB takes
345 * advantage of this for its finder-info and resource forks.
347 error
= default_listxattr(vp
, uio
, size
, options
, context
);
354 xattr_validatename(const char *name
)
358 if (name
== NULL
|| name
[0] == '\0') {
361 namelen
= strnlen(name
, XATTR_MAXNAMELEN
);
362 if (name
[namelen
] != '\0')
363 return (ENAMETOOLONG
);
365 if (utf8_validatestr((const unsigned char *)name
, namelen
) != 0)
373 * Determine whether an EA is a protected system attribute.
376 xattr_protected(const char *attrname
)
378 return(!strncmp(attrname
, "com.apple.system.", 17));
384 * Obtain a named stream from vnode vp.
387 vnode_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, int flags
, vfs_context_t context
)
391 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
392 error
= VNOP_GETNAMEDSTREAM(vp
, svpp
, name
, op
, flags
, context
);
394 error
= default_getnamedstream(vp
, svpp
, name
, op
, context
);
397 uint32_t streamflags
= VISNAMEDSTREAM
;
400 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
401 streamflags
|= VISSHADOW
;
405 vnode_lock_spin(svp
);
406 svp
->v_flag
|= streamflags
;
409 /* Tag the parent so we know to flush credentials for streams on setattr */
411 vp
->v_lflag
|= VL_HASSTREAMS
;
414 /* Make the file it's parent.
415 * Note: This parent link helps us distinguish vnodes for
416 * shadow stream files from vnodes for resource fork on file
417 * systems that support namedstream natively (both have
418 * VISNAMEDSTREAM set) by allowing access to mount structure
419 * for checking MNTK_NAMED_STREAMS bit at many places in the
422 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
429 * Make a named stream for vnode vp.
432 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
436 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
437 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
439 error
= default_makenamedstream(vp
, svpp
, name
, context
);
442 uint32_t streamflags
= VISNAMEDSTREAM
;
446 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
447 streamflags
|= VISSHADOW
;
451 vnode_lock_spin(svp
);
452 svp
->v_flag
|= streamflags
;
455 /* Tag the parent so we know to flush credentials for streams on setattr */
457 vp
->v_lflag
|= VL_HASSTREAMS
;
460 /* Make the file it's parent.
461 * Note: This parent link helps us distinguish vnodes for
462 * shadow stream files from vnodes for resource fork on file
463 * systems that support namedstream natively (both have
464 * VISNAMEDSTREAM set) by allowing access to mount structure
465 * for checking MNTK_NAMED_STREAMS bit at many places in the
468 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
474 * Remove a named stream from vnode vp.
477 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
481 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
482 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
484 error
= default_removenamedstream(vp
, name
, context
);
489 #define NS_IOBUFSIZE (128 * 1024)
492 * Release a named stream shadow file.
494 * Note: This function is called from two places where we do not need
495 * to check if the vnode has any references held before deleting the
496 * shadow file. Once from vclean() when the vnode is being reclaimed
497 * and we do not hold any references on the vnode. Second time from
498 * default_getnamedstream() when we get an error during shadow stream
499 * file initialization so that other processes who are waiting for the
500 * shadow stream file initialization by the creator will get opportunity
501 * to create and initialize the file again.
504 vnode_relenamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
507 struct componentname cn
;
514 MAKE_SHADOW_NAME(vp
, tmpname
);
517 cn
.cn_nameiop
= DELETE
;
518 cn
.cn_flags
= ISLASTCN
;
519 cn
.cn_context
= context
;
520 cn
.cn_pnbuf
= tmpname
;
521 cn
.cn_pnlen
= sizeof(tmpname
);
522 cn
.cn_nameptr
= cn
.cn_pnbuf
;
523 cn
.cn_namelen
= strlen(tmpname
);
525 /* Obtain the vnode for the shadow files directory. */
526 err
= get_shadow_dir(&dvp
, context
);
531 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, context
);
538 * Flush a named stream shadow file.
541 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
543 struct vnode_attr va
;
545 caddr_t bufptr
= NULL
;
553 VATTR_WANTED(&va
, va_data_size
);
554 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
555 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
558 datasize
= va
.va_data_size
;
559 if ((datasize
== 0)) {
560 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
564 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
565 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
568 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
572 * Copy the shadow stream file data into the resource fork.
574 error
= VNOP_OPEN(svp
, 0, context
);
576 printf("vnode_flushnamedstream: err %d opening file\n", error
);
579 while (offset
< datasize
) {
580 iosize
= MIN(datasize
- offset
, iosize
);
582 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
583 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
584 error
= VNOP_READ(svp
, auio
, 0, context
);
588 /* Since there's no truncate xattr we must remove the resource fork. */
590 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
591 if ((error
!= 0) && (error
!= ENOATTR
)) {
595 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
596 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
597 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
603 (void) VNOP_CLOSE(svp
, 0, context
);
606 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
616 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
617 int *creator
, vfs_context_t context
)
619 vnode_t dvp
= NULLVP
;
620 vnode_t svp
= NULLVP
;
621 struct componentname cn
;
622 struct vnode_attr va
;
629 /* Establish a unique file name. */
630 MAKE_SHADOW_NAME(vp
, tmpname
);
631 bzero(&cn
, sizeof(cn
));
632 cn
.cn_nameiop
= LOOKUP
;
633 cn
.cn_flags
= ISLASTCN
;
634 cn
.cn_context
= context
;
635 cn
.cn_pnbuf
= tmpname
;
636 cn
.cn_pnlen
= sizeof(tmpname
);
637 cn
.cn_nameptr
= cn
.cn_pnbuf
;
638 cn
.cn_namelen
= strlen(tmpname
);
640 /* Pick up uid, gid, mode and date from original file. */
642 VATTR_WANTED(&va
, va_uid
);
643 VATTR_WANTED(&va
, va_gid
);
644 VATTR_WANTED(&va
, va_mode
);
645 VATTR_WANTED(&va
, va_create_time
);
646 VATTR_WANTED(&va
, va_modify_time
);
647 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
648 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
649 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
650 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
651 va
.va_uid
= KAUTH_UID_NONE
;
652 va
.va_gid
= KAUTH_GID_NONE
;
653 va
.va_mode
= S_IRUSR
| S_IWUSR
;
655 va
.va_vaflags
= VA_EXCLUSIVE
;
656 VATTR_SET(&va
, va_type
, VREG
);
657 /* We no longer change the access, but we still hide it. */
658 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
660 /* Obtain the vnode for the shadow files directory. */
661 if (get_shadow_dir(&dvp
, context
) != 0) {
666 /* See if someone else already has it open. */
667 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, context
) == 0) {
668 /* Double check existence by asking for size. */
670 VATTR_WANTED(&va
, va_data_size
);
671 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
672 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
673 goto out
; /* OK to use. */
677 /* Otherwise make sure the resource fork data exists. */
678 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
679 XATTR_NOSECURITY
, context
);
681 * To maintain binary compatibility with legacy Carbon
682 * emulated resource fork support, if the resource fork
683 * doesn't exist but the Finder Info does, then act as
684 * if an empty resource fork is present (see 4724359).
686 if ((error
== ENOATTR
) &&
687 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
688 XATTR_NOSECURITY
, context
) == 0)) {
696 /* If the resource fork exists, its size is expected to be non-zero. */
703 /* Create the shadow stream file. */
704 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, context
);
707 } else if ((error
== EEXIST
) && !makestream
) {
708 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, context
);
715 /* On errors, clean up shadow stream file. */
723 *rsrcsize
= datasize
;
730 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
732 vnode_t svp
= NULLVP
;
734 caddr_t bufptr
= NULL
;
741 * Only the "com.apple.ResourceFork" stream is supported here.
743 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
749 * Obtain a shadow file for the resource fork I/O.
751 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
758 * The creator of the shadow file provides its file data,
759 * all other threads should wait until its ready. In order to
760 * prevent a deadlock during error codepaths, we need to check if the
761 * vnode is being created, or if it has failed out. Regardless of success or
762 * failure, we set the VISSHADOW bit on the vnode, so we check that
763 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
764 * then we can infer the creator isn't done yet. If it's there, but
765 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
770 if (svp
->v_flag
& VISNAMEDSTREAM
) {
771 /* data is ready, go use it */
775 /* It's not ready, wait for it (sleep using v_parent as channel) */
776 if ((svp
->v_flag
& VISSHADOW
)) {
778 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
779 * thread is done with this vnode. Just unlock the vnode and try again
784 /* Otherwise, sleep if the shadow file is not created yet */
785 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
786 "getnamedstream", NULL
);
795 * Copy the real resource fork data into shadow stream file.
797 if (op
== NS_OPEN
&& datasize
!= 0) {
801 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
802 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
807 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
810 error
= VNOP_OPEN(svp
, 0, context
);
814 while (offset
< datasize
) {
817 iosize
= MIN(datasize
- offset
, iosize
);
819 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_READ
);
820 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
821 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
822 XATTR_NOSECURITY
, context
);
827 uio_reset(auio
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
828 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
829 error
= VNOP_WRITE(svp
, auio
, 0, context
);
835 (void) VNOP_CLOSE(svp
, 0, context
);
838 /* Wake up anyone waiting for svp file content */
842 /* VISSHADOW would be set later on anyway, so we set it now */
843 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
844 wakeup((caddr_t
)&svp
->v_parent
);
847 /* On post create errors, get rid of the shadow file. This
848 * way if there is another process waiting for initialization
849 * of the shadowfile by the current process will wake up and
850 * retry by creating and initializing the shadow file again.
851 * Also add the VISSHADOW bit here to indicate we're done operating
854 (void)vnode_relenamedstream(vp
, svp
, context
);
856 svp
->v_flag
|= VISSHADOW
;
857 wakeup((caddr_t
)&svp
->v_parent
);
863 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
869 /* On errors, clean up shadow stream file. */
880 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
886 * Only the "com.apple.ResourceFork" stream is supported here.
888 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
892 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
895 * Wake up any waiters over in default_getnamedstream().
897 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
901 /* If we're the creator, mark it as a named stream */
902 svp
->v_flag
|= (VISNAMEDSTREAM
| VISSHADOW
);
903 /* Wakeup any waiters on the v_parent channel */
904 wakeup((caddr_t
)&svp
->v_parent
);
913 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
916 * Only the "com.apple.ResourceFork" stream is supported here.
918 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
922 * XXX - what about other opened instances?
924 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
928 get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
)
930 vnode_t dvp
= NULLVP
;
931 vnode_t sdvp
= NULLVP
;
932 struct componentname cn
;
933 struct vnode_attr va
;
938 /* Check if we've already created it. */
939 if (shadow_dvp
!= NULLVP
) {
940 if ((error
= vnode_getwithvid(shadow_dvp
, shadow_vid
))) {
948 /* Obtain the vnode for "/tmp" directory. */
949 if (vnode_lookup("/tmp", 0, &dvp
, context
) != 0) {
954 /* Create the shadow stream directory. */
955 snprintf(tmpname
, sizeof(tmpname
), ".vfs_rsrc_streams_%p%x",
956 (void*)rootvnode
, shadow_sequence
);
957 bzero(&cn
, sizeof(cn
));
958 cn
.cn_nameiop
= LOOKUP
;
959 cn
.cn_flags
= ISLASTCN
;
960 cn
.cn_context
= context
;
961 cn
.cn_pnbuf
= tmpname
;
962 cn
.cn_pnlen
= sizeof(tmpname
);
963 cn
.cn_nameptr
= cn
.cn_pnbuf
;
964 cn
.cn_namelen
= strlen(tmpname
);
967 * owned by root, only readable by root, hidden
970 VATTR_SET(&va
, va_uid
, 0);
971 VATTR_SET(&va
, va_gid
, 0);
972 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
973 VATTR_SET(&va
, va_type
, VDIR
);
974 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
975 va
.va_vaflags
= VA_EXCLUSIVE
;
977 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, context
);
980 * There can be only one winner for an exclusive create.
983 /* Take a long term ref to keep this dir around. */
984 error
= vnode_ref(sdvp
);
987 shadow_vid
= sdvp
->v_id
;
989 } else if (error
== EEXIST
) {
990 /* loser has to look up directory */
991 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, context
);
993 /* Make sure its in fact a directory */
994 if (sdvp
->v_type
!= VDIR
) {
997 /* Obtain the fsid for /tmp directory */
999 VATTR_WANTED(&va
, va_fsid
);
1000 if (VNOP_GETATTR(dvp
, &va
, context
) != 0 ||
1001 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1004 tmp_fsid
= va
.va_fsid
;
1007 VATTR_WANTED(&va
, va_uid
);
1008 VATTR_WANTED(&va
, va_gid
);
1009 VATTR_WANTED(&va
, va_mode
);
1010 VATTR_WANTED(&va
, va_fsid
);
1011 VATTR_WANTED(&va
, va_dirlinkcount
);
1012 VATTR_WANTED(&va
, va_acl
);
1013 /* Provide defaults for attrs that may not be supported */
1014 va
.va_dirlinkcount
= 1;
1015 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1017 if (VNOP_GETATTR(sdvp
, &va
, context
) != 0 ||
1018 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
1019 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
1020 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
1021 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
1025 * Make sure its what we want:
1027 * - not writable by anyone
1028 * - on same file system as /tmp
1029 * - not a hard-linked directory
1030 * - no ACLs (they might grant write access)
1032 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
1033 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
1034 (va
.va_fsid
!= tmp_fsid
) ||
1035 (va
.va_dirlinkcount
!= 1) ||
1036 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1046 /* On errors, clean up shadow stream directory. */
1056 /* This is not the dir we're looking for, move along */
1057 ++shadow_sequence
; /* try something else next time */
1066 * Default Implementation (Non-native EA)
1071 Typical "._" AppleDouble Header File layout:
1072 ------------------------------------------------------------
1077 .-- AD ENTRY[0] Finder Info Entry (must be first)
1078 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1080 | ///////////// Fixed Size Data (32 bytes)
1084 | ATTR ENTRY[1] --+--.
1085 | ATTR ENTRY[2] --+--+--.
1087 | ATTR ENTRY[N] --+--+--+--.
1088 | ATTR DATA 0 <-' | | |
1089 | //////////// | | |
1090 | ATTR DATA 1 <----' | |
1092 | ATTR DATA 2 <-------' |
1095 | ATTR DATA N <----------'
1097 | Attribute Free Space
1099 '----> RESOURCE FORK
1100 ///////////// Variable Sized Data
1109 ------------------------------------------------------------
1111 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1112 stored as part of the Finder Info. The length in the Finder
1113 Info AppleDouble entry includes the length of the extended
1114 attribute header, attribute entries, and attribute data.
1119 * On Disk Data Structures
1121 * Note: Motorola 68K alignment and big-endian.
1123 * See RFC 1740 for additional information about the AppleDouble file format.
1127 #define ADH_MAGIC 0x00051607
1128 #define ADH_VERSION 0x00020000
1129 #define ADH_MACOSX "Mac OS X "
1132 * AppleDouble Entry ID's
1134 #define AD_DATA 1 /* Data fork */
1135 #define AD_RESOURCE 2 /* Resource fork */
1136 #define AD_REALNAME 3 /* FileÕs name on home file system */
1137 #define AD_COMMENT 4 /* Standard Mac comment */
1138 #define AD_ICONBW 5 /* Mac black & white icon */
1139 #define AD_ICONCOLOR 6 /* Mac color icon */
1140 #define AD_UNUSED 7 /* Not used */
1141 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1142 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1143 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1144 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1145 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1146 #define AD_AFPNAME 13 /* Short name on AFP server */
1147 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1148 #define AD_AFPDIRID 15 /* AFP directory ID */
1149 #define AD_ATTRIBUTES AD_FINDERINFO
1152 #define ATTR_FILE_PREFIX "._"
1153 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1155 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1157 /* Implementation Limits */
1158 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
1159 #define ATTR_MAX_HDR_SIZE 65536
1161 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1162 * size supported (including the attribute entries). All of
1163 * the attribute entries must reside within this limit. If
1164 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1165 * boundry, then all of the attribute data I/O is performed
1166 * separately from the attribute header I/O.
1168 * In particular, all of the attr_entry structures must lie
1169 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1170 * AppleDouble file. However, the attribute data (i.e. the
1171 * contents of the extended attributes) may extend beyond the
1172 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1173 * limit is to allow the implementation to optimize by reading
1174 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1178 #define FINDERINFOSIZE 32
1180 typedef struct apple_double_entry
{
1181 u_int32_t type
; /* entry type: see list, 0 invalid */
1182 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1183 u_int32_t length
; /* entry data length in bytes. */
1184 } __attribute__((aligned(2), packed
)) apple_double_entry_t
;
1187 typedef struct apple_double_header
{
1188 u_int32_t magic
; /* == ADH_MAGIC */
1189 u_int32_t version
; /* format version: 2 = 0x00020000 */
1190 u_int32_t filler
[4];
1191 u_int16_t numEntries
; /* number of entries which follow */
1192 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1193 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1194 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1195 } __attribute__((aligned(2), packed
)) apple_double_header_t
;
1197 #define ADHDRSIZE (4+4+16+2)
1199 /* Entries are aligned on 4 byte boundaries */
1200 typedef struct attr_entry
{
1201 u_int32_t offset
; /* file offset to data */
1202 u_int32_t length
; /* size of attribute data */
1205 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1206 } __attribute__((aligned(2), packed
)) attr_entry_t
;
1209 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1210 typedef struct attr_header
{
1211 apple_double_header_t appledouble
;
1212 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1213 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1214 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1215 u_int32_t data_start
; /* file offset to attribute data area */
1216 u_int32_t data_length
; /* length of attribute data area */
1217 u_int32_t reserved
[3];
1219 u_int16_t num_attrs
;
1220 } __attribute__((aligned(2), packed
)) attr_header_t
;
1223 /* Empty Resource Fork Header */
1224 typedef struct rsrcfork_header
{
1225 u_int32_t fh_DataOffset
;
1226 u_int32_t fh_MapOffset
;
1227 u_int32_t fh_DataLength
;
1228 u_int32_t fh_MapLength
;
1229 u_int8_t systemData
[112];
1230 u_int8_t appData
[128];
1231 u_int32_t mh_DataOffset
;
1232 u_int32_t mh_MapOffset
;
1233 u_int32_t mh_DataLength
;
1234 u_int32_t mh_MapLength
;
1236 u_int16_t mh_RefNum
;
1238 u_int8_t mh_InMemoryAttr
;
1241 u_int16_t typeCount
;
1242 } __attribute__((aligned(2), packed
)) rsrcfork_header_t
;
1244 #define RF_FIRST_RESOURCE 256
1245 #define RF_NULL_MAP_LENGTH 30
1246 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1248 /* Runtime information about the attribute file. */
1249 typedef struct attr_info
{
1250 vfs_context_t context
;
1255 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1256 apple_double_header_t
*filehdr
;
1257 apple_double_entry_t
*finderinfo
;
1258 apple_double_entry_t
*rsrcfork
;
1259 attr_header_t
*attrhdr
;
1260 attr_entry_t
*attr_entry
;
1262 u_int8_t emptyfinderinfo
;
1266 #define ATTR_SETTING 1
1268 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1270 #define ATTR_ENTRY_LENGTH(namelen) \
1271 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1273 #define ATTR_NEXT(ae) \
1274 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1276 #define ATTR_VALID(ae, ai) \
1277 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1279 #define SWAP16(x) OSSwapBigToHostInt16((x))
1280 #define SWAP32(x) OSSwapBigToHostInt32((x))
1281 #define SWAP64(x) OSSwapBigToHostInt64((x))
1284 static u_int32_t emptyfinfo
[8] = {0};
1288 * Local support routines
1290 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1292 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1294 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1296 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1298 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1300 static void rel_xattrinfo(attr_info_t
*ainfop
);
1302 static int write_xattrinfo(attr_info_t
*ainfop
);
1304 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1306 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1308 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1311 #if BYTE_ORDER == LITTLE_ENDIAN
1312 static void swap_adhdr(apple_double_header_t
*adh
);
1313 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1316 #define swap_adhdr(x)
1317 #define swap_attrhdr(x, y)
1320 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1321 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1322 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1326 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1327 * is in big endian (as it would exist on disk). Verifies the following:
1330 * - number of entries
1331 * - that each entry fits within the file size
1333 * If the header is invalid, ENOATTR is returned.
1335 * NOTE: Does not attempt to validate the extended attributes header that
1336 * may be embedded in the Finder Info entry.
1338 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1341 u_int32_t header_end
;
1342 u_int32_t entry_end
;
1344 apple_double_header_t
*header
;
1346 rawsize
= ainfop
->rawsize
;
1347 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1349 /* Is the file big enough to contain an AppleDouble header? */
1350 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1353 /* Swap the AppleDouble header fields to native order */
1354 header
->magic
= SWAP32(header
->magic
);
1355 header
->version
= SWAP32(header
->version
);
1356 header
->numEntries
= SWAP16(header
->numEntries
);
1358 /* Sanity check the AppleDouble header fields */
1359 if (header
->magic
!= ADH_MAGIC
||
1360 header
->version
!= ADH_VERSION
||
1361 header
->numEntries
< 1 ||
1362 header
->numEntries
> 15) {
1366 /* Calculate where the entries[] array ends */
1367 header_end
= offsetof(apple_double_header_t
, entries
) +
1368 header
->numEntries
* sizeof(apple_double_entry_t
);
1370 /* Is the file big enough to contain the AppleDouble entries? */
1371 if (rawsize
< header_end
) {
1375 /* Swap and sanity check each AppleDouble entry */
1376 for (i
=0; i
<header
->numEntries
; i
++) {
1377 /* Swap the per-entry fields to native order */
1378 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1379 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1380 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1382 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1385 * Does the entry's content start within the header itself,
1386 * did the addition overflow, or does the entry's content
1387 * extend past the end of the file?
1389 if (header
->entries
[i
].offset
< header_end
||
1390 entry_end
< header
->entries
[i
].offset
||
1391 entry_end
> ainfop
->filesize
) {
1396 * Does the current entry's content overlap with a previous
1399 * Yes, this is O(N**2), and there are more efficient algorithms
1400 * for testing pairwise overlap of N ranges when N is large.
1401 * But we have already ensured N < 16, and N is almost always 2.
1402 * So there's no point in using a more complex algorithm.
1405 for (j
=0; j
<i
; j
++) {
1406 if (entry_end
> header
->entries
[j
].offset
&&
1407 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1419 * Retrieve the data of an extended attribute.
1422 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1423 __unused
int options
, vfs_context_t context
)
1427 attr_header_t
*header
;
1428 attr_entry_t
*entry
;
1438 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1441 * Open the file locked (shared) since the Carbon
1442 * File Manager may have the Apple Double file open
1443 * and could be changing the resource fork.
1445 fileflags
|= O_SHLOCK
;
1450 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1453 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1454 close_xattrfile(xvp
, fileflags
, context
);
1458 /* Get the Finder Info. */
1459 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1461 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1463 } else if (uio
== NULL
) {
1464 *size
= FINDERINFOSIZE
;
1466 } else if (uio_offset(uio
) != 0) {
1468 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1471 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1472 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1477 /* Read the Resource Fork. */
1479 if (!vnode_isreg(vp
)) {
1481 } else if (ainfo
.rsrcfork
== NULL
) {
1483 } else if (uio
== NULL
) {
1484 *size
= (size_t)ainfo
.rsrcfork
->length
;
1486 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1487 error
= VNOP_READ(xvp
, uio
, 0, context
);
1489 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1494 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1498 if (uio_offset(uio
) != 0) {
1503 namelen
= strlen(name
) + 1;
1504 header
= ainfo
.attrhdr
;
1505 entry
= ainfo
.attr_entry
;
1507 * Search for attribute name in the header.
1509 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1510 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1511 datalen
= (size_t)entry
->length
;
1517 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1521 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1522 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1523 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1525 uio_setoffset(uio
, entry
->offset
);
1526 error
= VNOP_READ(xvp
, uio
, 0, context
);
1527 uio_setoffset(uio
, 0);
1531 entry
= ATTR_NEXT(entry
);
1534 rel_xattrinfo(&ainfo
);
1535 close_xattrfile(xvp
, fileflags
, context
);
1541 * Set the data of an extended attribute.
1544 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1548 attr_header_t
*header
;
1549 attr_entry_t
*entry
;
1550 attr_entry_t
*lastentry
;
1554 size_t datafreespace
;
1561 char finfo
[FINDERINFOSIZE
];
1563 datalen
= uio_resid(uio
);
1564 namelen
= strlen(name
) + 1;
1565 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1568 * By convention, Finder Info that is all zeroes is equivalent to not
1569 * having a Finder Info EA. So if we're trying to set the Finder Info
1570 * to all zeroes, then delete it instead. If a file didn't have an
1571 * AppleDouble file before, this prevents creating an AppleDouble file
1572 * with no useful content.
1574 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1575 * for all zeroes Finder Info before opening the AppleDouble file.
1576 * But if either of those options were specified, we need to open the
1577 * AppleDouble file to see whether there was already Finder Info (so we
1578 * can return an error if needed); this case is handled further below.
1580 * NOTE: this copies the Finder Info data into the "finfo" local.
1582 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1584 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1585 * That means we probably have to open_xattrfile and get_xattrinfo.
1587 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1590 error
= uiomove(finfo
, datalen
, uio
);
1593 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1594 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1595 error
= default_removexattr(vp
, name
, 0, context
);
1596 if (error
== ENOATTR
)
1604 * Open the file locked since setting an attribute
1605 * can change the layout of the Apple Double file.
1607 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1608 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1611 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1612 close_xattrfile(xvp
, fileflags
, context
);
1616 /* Set the Finder Info. */
1617 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1618 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1619 /* attr exists and "create" was specified? */
1620 if (options
& XATTR_CREATE
) {
1625 /* attr doesn't exists and "replace" was specified? */
1626 if (options
& XATTR_REPLACE
) {
1631 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1633 * Setting the Finder Info to all zeroes is equivalent to
1634 * removing it. Close the xattr file and let
1635 * default_removexattr do the work (including deleting
1636 * the xattr file if there are no other xattrs).
1638 * Note that we have to handle the case where the
1639 * Finder Info was already all zeroes, and we ignore
1642 * The common case where options == 0 was handled above.
1644 rel_xattrinfo(&ainfo
);
1645 close_xattrfile(xvp
, fileflags
, context
);
1646 error
= default_removexattr(vp
, name
, 0, context
);
1647 if (error
== ENOATTR
)
1651 if (ainfo
.finderinfo
) {
1652 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1653 bcopy(finfo
, attrdata
, datalen
);
1654 ainfo
.iosize
= sizeof(attr_header_t
);
1655 error
= write_xattrinfo(&ainfo
);
1662 /* Write the Resource Fork. */
1663 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1664 u_int32_t endoffset
;
1666 if (!vnode_isreg(vp
)) {
1670 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
1671 /* attr exists and "create" was specified? */
1672 if (options
& XATTR_CREATE
) {
1677 /* attr doesn't exists and "replace" was specified? */
1678 if (options
& XATTR_REPLACE
) {
1683 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1684 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1685 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1688 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1689 if (endoffset
> ainfo
.rsrcfork
->length
) {
1690 ainfo
.rsrcfork
->length
= endoffset
;
1691 ainfo
.iosize
= sizeof(attr_header_t
);
1692 error
= write_xattrinfo(&ainfo
);
1698 if (datalen
> ATTR_MAX_SIZE
) {
1699 return (E2BIG
); /* EINVAL instead ? */
1702 if (ainfo
.attrhdr
== NULL
) {
1706 header
= ainfo
.attrhdr
;
1707 entry
= ainfo
.attr_entry
;
1709 /* Check if data area crosses the maximum header size. */
1710 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1711 splitdata
= 1; /* do data I/O separately */
1716 * See if attribute already exists.
1718 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1719 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1723 entry
= ATTR_NEXT(entry
);
1727 if (options
& XATTR_CREATE
) {
1731 if (datalen
== entry
->length
) {
1733 uio_setoffset(uio
, entry
->offset
);
1734 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1735 uio_setoffset(uio
, 0);
1737 printf("setxattr: VNOP_WRITE error %d\n", error
);
1740 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1741 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1744 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1745 error
= write_xattrinfo(&ainfo
);
1747 printf("setxattr: write_xattrinfo error %d\n", error
);
1753 * Brute force approach - just remove old entry and set new entry.
1756 rel_xattrinfo(&ainfo
);
1757 close_xattrfile(xvp
, fileflags
, context
);
1758 error
= default_removexattr(vp
, name
, options
, context
);
1762 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1763 options
&= ~XATTR_REPLACE
;
1764 goto start
; /* start over */
1769 if (options
& XATTR_REPLACE
) {
1770 error
= ENOATTR
; /* nothing there to replace */
1773 /* Check if header size limit has been reached. */
1774 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1779 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1781 /* Check if we need more space. */
1782 if ((datalen
+ entrylen
) > datafreespace
) {
1785 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1787 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1788 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1789 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1792 ainfo
.filesize
+= growsize
;
1793 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1795 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1801 * Move the resource fork out of the way.
1803 if (ainfo
.rsrcfork
) {
1804 if (ainfo
.rsrcfork
->length
!= 0) {
1805 shift_data_down(xvp
,
1806 ainfo
.rsrcfork
->offset
,
1807 ainfo
.rsrcfork
->length
,
1810 ainfo
.rsrcfork
->offset
+= growsize
;
1812 ainfo
.finderinfo
->length
+= growsize
;
1813 header
->total_size
+= growsize
;
1816 /* Make space for a new entry. */
1818 shift_data_down(xvp
,
1820 header
->data_length
,
1823 bcopy((u_int8_t
*)header
+ header
->data_start
,
1824 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
1825 header
->data_length
);
1827 header
->data_start
+= entrylen
;
1829 /* Fix up entry data offsets. */
1831 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
1832 entry
->offset
+= entrylen
;
1836 * If the attribute data area is entirely within
1837 * the header buffer, then just update the buffer,
1838 * otherwise we'll write it separately to the file.
1843 /* Write new attribute data after the end of existing data. */
1844 offset
= header
->data_start
+ header
->data_length
;
1845 uio_setoffset(uio
, offset
);
1846 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1847 uio_setoffset(uio
, 0);
1849 printf("setxattr: VNOP_WRITE error %d\n", error
);
1853 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
1855 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1857 printf("setxattr: uiomove error %d\n", error
);
1862 /* Create the attribute entry. */
1863 lastentry
->length
= datalen
;
1864 lastentry
->offset
= header
->data_start
+ header
->data_length
;
1865 lastentry
->namelen
= namelen
;
1866 lastentry
->flags
= 0;
1867 bcopy(name
, &lastentry
->name
[0], namelen
);
1869 /* Update the attributes header. */
1870 header
->num_attrs
++;
1871 header
->data_length
+= datalen
;
1874 /* Only write the entries, since the data was written separately. */
1875 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
1877 /* The entry and data are both in the header; write them together. */
1878 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1880 error
= write_xattrinfo(&ainfo
);
1882 printf("setxattr: write_xattrinfo error %d\n", error
);
1886 rel_xattrinfo(&ainfo
);
1887 close_xattrfile(xvp
, fileflags
, context
);
1889 /* Touch the change time if we changed an attribute. */
1891 struct vnode_attr va
;
1893 /* Re-write the mtime to cause a ctime change. */
1895 VATTR_WANTED(&va
, va_modify_time
);
1896 if (vnode_getattr(vp
, &va
, context
) == 0) {
1898 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1899 (void) vnode_setattr(vp
, &va
, context
);
1903 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
1910 * Remove an extended attribute.
1913 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
1917 attr_header_t
*header
;
1918 attr_entry_t
*entry
;
1919 attr_entry_t
*oldslot
;
1925 int found
= 0, lastone
= 0;
1933 fileflags
= FREAD
| FWRITE
;
1934 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1937 * Open the file locked (exclusive) since the Carbon
1938 * File Manager may have the Apple Double file open
1939 * and could be changing the resource fork.
1941 fileflags
|= O_EXLOCK
;
1946 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1949 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1950 close_xattrfile(xvp
, fileflags
, context
);
1954 attrcount
+= ainfo
.attrhdr
->num_attrs
;
1957 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
1960 /* Clear the Finder Info. */
1961 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1962 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1966 /* On removal of last attribute the ._ file is removed. */
1967 if (--attrcount
== 0)
1969 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1970 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
1971 ainfo
.iosize
= sizeof(attr_header_t
);
1972 error
= write_xattrinfo(&ainfo
);
1976 /* Clear the Resource Fork. */
1978 if (!vnode_isreg(vp
)) {
1982 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
1986 /* On removal of last attribute the ._ file is removed. */
1987 if (--attrcount
== 0)
1991 * If the resource fork isn't the last AppleDouble
1992 * entry then the space needs to be reclaimed by
1993 * shifting the entries after the resource fork.
1995 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
1996 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
1997 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
2000 ainfo
.rsrcfork
->length
= 0;
2001 ainfo
.iosize
= sizeof(attr_header_t
);
2002 error
= write_xattrinfo(&ainfo
);
2007 if (ainfo
.attrhdr
== NULL
) {
2011 namelen
= strlen(name
) + 1;
2012 header
= ainfo
.attrhdr
;
2013 entry
= ainfo
.attr_entry
;
2016 * See if this attribute exists.
2018 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2019 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
2021 if ((i
+1) == header
->num_attrs
)
2025 entry
= ATTR_NEXT(entry
);
2031 /* On removal of last attribute the ._ file is removed. */
2032 if (--attrcount
== 0)
2035 datalen
= entry
->length
;
2036 dataoff
= entry
->offset
;
2037 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2038 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
2043 /* Remove the attribute entry. */
2045 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2046 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2049 /* Adjust the attribute data. */
2053 dataoff
- header
->data_start
,
2059 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2063 /* XXX write zeros to freed space ? */
2064 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2068 bcopy((u_int8_t
*)header
+ header
->data_start
,
2069 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2070 dataoff
- header
->data_start
);
2072 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2073 (u_int8_t
*)header
+ dataoff
- entrylen
,
2074 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2076 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2077 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2080 /* Adjust the header values and entry offsets. */
2081 header
->num_attrs
--;
2082 header
->data_start
-= entrylen
;
2083 header
->data_length
-= datalen
;
2086 entry
= ainfo
.attr_entry
;
2087 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2088 entry
->offset
-= entrylen
;
2089 if (entry
>= oldslot
)
2090 entry
->offset
-= datalen
;
2091 entry
= ATTR_NEXT(entry
);
2093 error
= write_xattrinfo(&ainfo
);
2095 printf("removexattr: write_xattrinfo error %d\n", error
);
2098 rel_xattrinfo(&ainfo
);
2100 /* When there are no more attributes remove the ._ file. */
2101 if (attrcount
== 0) {
2102 if (fileflags
& O_EXLOCK
)
2103 (void) unlock_xattrfile(xvp
, context
);
2104 VNOP_CLOSE(xvp
, fileflags
, context
);
2106 error
= remove_xattrfile(xvp
, context
);
2109 close_xattrfile(xvp
, fileflags
, context
);
2111 /* Touch the change time if we changed an attribute. */
2113 struct vnode_attr va
;
2115 /* Re-write the mtime to cause a ctime change. */
2117 VATTR_WANTED(&va
, va_modify_time
);
2118 if (vnode_getattr(vp
, &va
, context
) == 0) {
2120 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2121 (void) vnode_setattr(vp
, &va
, context
);
2125 post_event_if_success(vp
, error
, NOTE_ATTRIB
);
2133 * Retrieve the list of extended attribute names.
2136 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2140 attr_entry_t
*entry
;
2145 * We do not zero "*size" here as we don't want to stomp a size set when
2146 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2147 * system call layer, up in listxattr or flistxattr.
2150 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2151 if (error
== ENOATTR
)
2155 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2156 if (error
== ENOATTR
)
2158 close_xattrfile(xvp
, FREAD
, context
);
2162 /* Check for Finder Info. */
2163 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2165 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2166 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2170 error
= uiomove(XATTR_FINDERINFO_NAME
,
2171 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2179 /* Check for Resource Fork. */
2180 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2182 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2183 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2187 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2188 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2196 /* Check for attributes. */
2197 if (ainfo
.attrhdr
) {
2198 count
= ainfo
.attrhdr
->num_attrs
;
2199 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2200 if (xattr_protected((const char *)entry
->name
) ||
2201 xattr_validatename((const char *)entry
->name
) != 0) {
2202 entry
= ATTR_NEXT(entry
);
2206 *size
+= entry
->namelen
;
2207 entry
= ATTR_NEXT(entry
);
2210 if (uio_resid(uio
) < entry
->namelen
) {
2214 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2216 if (error
!= EFAULT
)
2220 entry
= ATTR_NEXT(entry
);
2224 rel_xattrinfo(&ainfo
);
2225 close_xattrfile(xvp
, FREAD
, context
);
2231 * Check the header of a ._ file to verify that it is in fact an Apple Double
2232 * file. Returns 0 if the header is valid, non-zero if invalid.
2234 int check_appledouble_header(vnode_t vp
, vfs_context_t ctx
)
2238 struct vnode_attr va
;
2240 void *buffer
= NULL
;
2244 ainfo
.context
= ctx
;
2246 VATTR_WANTED(&va
, va_data_size
);
2247 if ((error
= vnode_getattr(vp
, &va
, ctx
))) {
2250 ainfo
.filesize
= va
.va_data_size
;
2252 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfo
.filesize
);
2257 ainfo
.iosize
= iosize
;
2259 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2260 if (buffer
== NULL
) {
2265 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2266 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2268 /* Read the header */
2269 error
= VNOP_READ(vp
, auio
, 0, ctx
);
2273 ainfo
.rawsize
= iosize
- uio_resid(auio
);
2274 ainfo
.rawdata
= (u_int8_t
*)buffer
;
2276 error
= check_and_swap_apple_double_header(&ainfo
);
2281 /* If we made it here, then the header is ok */
2288 FREE(buffer
, M_TEMP
);
2295 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2297 vnode_t xvp
= NULLVP
;
2298 vnode_t dvp
= NULLVP
;
2299 struct vnode_attr va
;
2300 struct nameidata nd
;
2302 char *filename
= NULL
;
2303 const char *basename
= NULL
;
2309 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2311 * For the root directory use "._." to hold the attributes.
2313 filename
= &smallname
[0];
2314 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2315 dvp
= vp
; /* the "._." file resides in the root dir */
2318 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2322 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2327 /* "._" Attribute files cannot have attributes */
2328 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2329 basename
[0] == '.' && basename
[1] == '_') {
2333 filename
= &smallname
[0];
2334 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2335 if (len
>= sizeof(smallname
)) {
2336 len
++; /* snprintf result doesn't include '\0' */
2337 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2338 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2341 * Note that the lookup here does not authorize. Since we are looking
2342 * up in the same directory that we already have the file vnode in,
2343 * we must have been given the file vnode legitimately. Read/write
2344 * access has already been authorized in layers above for calls from
2345 * userspace, and the authorization code using this path to read
2346 * file security from the EA must always get access
2349 NDINIT(&nd
, LOOKUP
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
, UIO_SYSSPACE
,
2350 CAST_USER_ADDR_T(filename
), context
);
2353 if (fileflags
& O_CREAT
) {
2354 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2356 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2358 if ( (error
= namei(&nd
))) {
2363 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2369 * Pick up uid/gid/mode from target file.
2372 VATTR_WANTED(&va
, va_uid
);
2373 VATTR_WANTED(&va
, va_gid
);
2374 VATTR_WANTED(&va
, va_mode
);
2375 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2376 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2377 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2378 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2381 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2382 } else /* fallback values */ {
2383 uid
= KAUTH_UID_NONE
;
2384 gid
= KAUTH_GID_NONE
;
2385 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2389 VATTR_SET(&va
, va_type
, VREG
);
2390 VATTR_SET(&va
, va_mode
, umode
);
2391 if (uid
!= KAUTH_UID_NONE
)
2392 VATTR_SET(&va
, va_uid
, uid
);
2393 if (gid
!= KAUTH_GID_NONE
)
2394 VATTR_SET(&va
, va_gid
, gid
);
2396 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &va
,
2397 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2406 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2411 if ((error
= namei(&nd
))) {
2421 if (xvp
->v_type
!= VREG
) {
2426 * Owners must match.
2429 VATTR_WANTED(&va
, va_uid
);
2430 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2431 uid_t owner
= va
.va_uid
;
2434 VATTR_WANTED(&va
, va_uid
);
2435 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2436 error
= ENOATTR
; /* don't use this "._" file */
2441 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2447 if ((error
= vnode_ref(xvp
))) {
2452 /* If create was requested, make sure file header exists. */
2453 if (fileflags
& O_CREAT
) {
2455 VATTR_WANTED(&va
, va_data_size
);
2456 VATTR_WANTED(&va
, va_fileid
);
2457 VATTR_WANTED(&va
, va_nlink
);
2458 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2463 /* If the file is empty then add a default header. */
2464 if (va
.va_data_size
== 0) {
2465 /* Don't adopt hard-linked "._" files. */
2466 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2470 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2474 /* Apply file locking if requested. */
2475 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2478 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2479 error
= lock_xattrfile(xvp
, locktype
, context
);
2484 if (dvp
&& (dvp
!= vp
)) {
2488 vnode_putname(basename
);
2490 if (filename
&& filename
!= &smallname
[0]) {
2491 FREE(filename
, M_TEMP
);
2494 if (xvp
!= NULLVP
) {
2496 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2499 (void) vnode_rele(xvp
);
2501 (void) vnode_put(xvp
);
2504 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2508 *xvpp
= xvp
; /* return a referenced vnode */
2513 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2515 // if (fileflags & FWRITE)
2516 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2518 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2519 (void) unlock_xattrfile(xvp
, context
);
2521 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2522 (void) vnode_rele(xvp
);
2523 (void) vnode_put(xvp
);
2527 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2530 struct nameidata nd
;
2535 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2539 pathlen
= MAXPATHLEN
;
2540 error
= vn_getpath(xvp
, path
, &pathlen
);
2542 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2546 NDINIT(&nd
, DELETE
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2547 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2549 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2556 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2565 * Read in and parse the AppleDouble header and entries, and the extended
2566 * attribute header and entries if any. Populates the fields of ainfop
2567 * based on the headers and entries found.
2569 * The basic idea is to:
2570 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2571 * AppleDouble entries, the extended attribute header, and extended
2572 * attribute entries must lie within this part of the file; the rest of
2573 * the AppleDouble handling code assumes this. Plus it allows us to
2574 * somewhat optimize by doing a smaller number of larger I/Os.
2575 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2577 * - Find the Finder Info and Resource Fork entries, if any.
2578 * - If we're going to be writing, try to make sure the Finder Info entry has
2579 * room to store the extended attribute header, plus some space for extended
2581 * - Swap and sanity check the extended attribute header and entries (if any).
2584 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2587 void * buffer
= NULL
;
2588 apple_double_header_t
*filehdr
;
2589 struct vnode_attr va
;
2594 bzero(ainfop
, sizeof(attr_info_t
));
2595 ainfop
->filevp
= xvp
;
2596 ainfop
->context
= context
;
2598 VATTR_WANTED(&va
, va_data_size
);
2599 VATTR_WANTED(&va
, va_fileid
);
2600 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2603 ainfop
->filesize
= va
.va_data_size
;
2605 /* When setting attributes, allow room for the header to grow. */
2607 iosize
= ATTR_MAX_HDR_SIZE
;
2609 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2615 ainfop
->iosize
= iosize
;
2616 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2617 if (buffer
== NULL
){
2622 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2623 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2625 /* Read the file header. */
2626 error
= VNOP_READ(xvp
, auio
, 0, context
);
2630 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2631 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2633 filehdr
= (apple_double_header_t
*)buffer
;
2635 error
= check_and_swap_apple_double_header(ainfop
);
2639 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2641 /* rel_xattrinfo is responsible for freeing the header buffer */
2644 /* Find the Finder Info and Resource Fork entries, if any */
2645 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2646 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2647 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2648 /* We found the Finder Info entry. */
2649 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2652 * Is the Finder Info "empty" (all zeroes)? If so,
2653 * we'll pretend like the Finder Info extended attribute
2656 * Note: we have to make sure the Finder Info is
2657 * contained within the buffer we have already read,
2658 * to avoid accidentally accessing a bogus address.
2659 * If it is outside the buffer, we just assume the
2660 * Finder Info is non-empty.
2662 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2663 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2664 ainfop
->emptyfinderinfo
= 1;
2667 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2669 * Ignore zero-length resource forks when getting. If setting,
2670 * we need to remember the resource fork entry so it can be
2671 * updated once the new content has been written.
2673 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2677 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2679 * The "empty" resource headers we created have a system data tag of:
2680 * "This resource fork intentionally left blank "
2682 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2684 u_int8_t systemData
[64];
2688 /* Read the system data which starts at byte 16 */
2689 rf_uio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
2690 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2691 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2692 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2696 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2697 continue; /* skip this resource fork */
2700 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2701 if (i
!= (filehdr
->numEntries
- 1)) {
2702 printf("get_xattrinfo: resource fork not last entry\n");
2703 ainfop
->readonly
= 1;
2710 * See if this file looks like it is laid out correctly to contain
2711 * extended attributes. If so, then do the following:
2713 * - If we're going to be writing, try to make sure the Finder Info
2714 * entry has room to store the extended attribute header, plus some
2715 * space for extended attributes.
2717 * - Swap and sanity check the extended attribute header and entries
2720 if (filehdr
->numEntries
== 2 &&
2721 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2722 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2723 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2724 attr_header_t
*attrhdr
;
2725 attrhdr
= (attr_header_t
*)filehdr
;
2727 * If we're going to be writing, try to make sure the Finder
2728 * Info entry has room to store the extended attribute header,
2729 * plus some space for extended attributes.
2731 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2735 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2736 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2737 /* Make some room before existing resource fork. */
2738 shift_data_down(xvp
,
2739 filehdr
->entries
[1].offset
,
2740 filehdr
->entries
[1].length
,
2742 writesize
= sizeof(attr_header_t
);
2744 /* Create a new, empty resource fork. */
2745 rsrcfork_header_t
*rsrcforkhdr
;
2747 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2749 /* Steal some space for an empty RF header. */
2750 delta
-= sizeof(rsrcfork_header_t
);
2752 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2753 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2755 /* Fill in Empty Resource Fork Header. */
2756 init_empty_resource_fork(rsrcforkhdr
);
2758 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2759 writesize
= ATTR_BUF_SIZE
;
2761 filehdr
->entries
[0].length
+= delta
;
2762 filehdr
->entries
[1].offset
+= delta
;
2764 /* Fill in Attribute Header. */
2765 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2766 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2767 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2768 attrhdr
->data_start
= sizeof(attr_header_t
);
2769 attrhdr
->data_length
= 0;
2770 attrhdr
->reserved
[0] = 0;
2771 attrhdr
->reserved
[1] = 0;
2772 attrhdr
->reserved
[2] = 0;
2774 attrhdr
->num_attrs
= 0;
2776 /* Push out new header */
2777 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
2778 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2780 swap_adhdr(filehdr
); /* to big endian */
2781 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2782 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2783 swap_adhdr(filehdr
); /* back to native */
2784 /* The attribute header gets swapped below. */
2788 * Swap and sanity check the extended attribute header and
2789 * entries (if any). The Finder Info content must be big enough
2790 * to include the extended attribute header; if not, we just
2793 * Note that we're passing the offset + length (i.e. the end)
2794 * of the Finder Info instead of rawsize to validate_attrhdr.
2795 * This ensures that all extended attributes lie within the
2796 * Finder Info content according to the AppleDouble entry.
2798 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2801 if (ainfop
->finderinfo
&&
2802 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2803 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2804 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2806 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2807 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2808 /* First attr_entry starts immediately following attribute header */
2809 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2818 FREE(buffer
, M_TEMP
);
2824 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2827 rsrcfork_header_t
*rsrcforkhdr
;
2833 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2834 bzero(buffer
, ATTR_BUF_SIZE
);
2836 xah
= (attr_header_t
*)buffer
;
2837 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2838 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2839 rsrcforksize
= sizeof(rsrcfork_header_t
);
2840 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2842 /* Fill in Apple Double Header. */
2843 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2844 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2845 xah
->appledouble
.numEntries
= SWAP16 (2);
2846 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2847 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2848 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2849 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2850 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2851 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2852 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2854 /* Fill in Attribute Header. */
2855 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2856 xah
->debug_tag
= SWAP32 (fileid
);
2857 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2858 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2860 /* Fill in Empty Resource Fork Header. */
2861 init_empty_resource_fork(rsrcforkhdr
);
2864 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2867 FREE(buffer
, M_TEMP
);
2873 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
2875 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
2876 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2877 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2878 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2879 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2880 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2881 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2882 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
2883 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
2884 rsrcforkhdr
->typeCount
= SWAP16 (-1);
2885 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
2889 rel_xattrinfo(attr_info_t
*ainfop
)
2891 FREE(ainfop
->filehdr
, M_TEMP
);
2892 bzero(ainfop
, sizeof(attr_info_t
));
2896 write_xattrinfo(attr_info_t
*ainfop
)
2901 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
2902 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
2904 swap_adhdr(ainfop
->filehdr
);
2905 if (ainfop
->attrhdr
!= NULL
) {
2906 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2909 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
2911 swap_adhdr(ainfop
->filehdr
);
2912 if (ainfop
->attrhdr
!= NULL
) {
2913 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2920 #if BYTE_ORDER == LITTLE_ENDIAN
2922 * Endian swap apple double header
2925 swap_adhdr(apple_double_header_t
*adh
)
2930 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
2932 adh
->magic
= SWAP32 (adh
->magic
);
2933 adh
->version
= SWAP32 (adh
->version
);
2934 adh
->numEntries
= SWAP16 (adh
->numEntries
);
2936 for (i
= 0; i
< count
; i
++) {
2937 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
2938 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
2939 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
2944 * Endian swap extended attributes header
2947 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
2953 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
2955 ah
->magic
= SWAP32 (ah
->magic
);
2956 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
2957 ah
->total_size
= SWAP32 (ah
->total_size
);
2958 ah
->data_start
= SWAP32 (ah
->data_start
);
2959 ah
->data_length
= SWAP32 (ah
->data_length
);
2960 ah
->flags
= SWAP16 (ah
->flags
);
2961 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
2963 ae
= (attr_entry_t
*)(&ah
[1]);
2964 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
2965 ae
->offset
= SWAP32 (ae
->offset
);
2966 ae
->length
= SWAP32 (ae
->length
);
2967 ae
->flags
= SWAP16 (ae
->flags
);
2973 * Validate and swap the attributes header contents, and each attribute's
2976 * Note: Assumes the caller has verified that the Finder Info content is large
2977 * enough to contain the attr_header structure itself. Therefore, we can
2978 * swap the header fields before sanity checking them.
2981 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
2992 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
2995 /* Swap the basic header fields */
2996 ah
->magic
= SWAP32(ah
->magic
);
2997 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
2998 ah
->total_size
= SWAP32 (ah
->total_size
);
2999 ah
->data_start
= SWAP32 (ah
->data_start
);
3000 ah
->data_length
= SWAP32 (ah
->data_length
);
3001 ah
->flags
= SWAP16 (ah
->flags
);
3002 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
3005 * Make sure the total_size fits within the Finder Info area, and the
3006 * extended attribute data area fits within total_size.
3008 end
= ah
->data_start
+ ah
->data_length
;
3009 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
3010 end
< ah
->data_start
||
3011 end
> ah
->total_size
) {
3016 * Make sure each of the attr_entry_t's fits within total_size.
3018 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
3019 count
= ah
->num_attrs
;
3020 ae
= (attr_entry_t
*)(&ah
[1]);
3022 for (i
=0; i
<count
; i
++) {
3023 /* Make sure the fixed-size part of this attr_entry_t fits. */
3024 if ((u_int8_t
*) &ae
[1] > buf_end
)
3027 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3028 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3029 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
3032 /* Swap the attribute entry fields */
3033 ae
->offset
= SWAP32(ae
->offset
);
3034 ae
->length
= SWAP32(ae
->length
);
3035 ae
->flags
= SWAP16(ae
->flags
);
3037 /* Make sure the attribute content fits. */
3038 end
= ae
->offset
+ ae
->length
;
3039 if (end
< ae
->offset
|| end
> ah
->total_size
)
3046 * TODO: Make sure the contents of attributes don't overlap the header
3047 * and don't overlap each other. The hard part is that we don't know
3048 * what the actual header size is until we have looped over all of the
3049 * variable-sized attribute entries.
3051 * XXX Is there any guarantee that attribute entries are stored in
3052 * XXX order sorted by the contents' file offset? If so, that would
3053 * XXX make the pairwise overlap check much easier.
3060 // "start" & "end" are byte offsets in the file.
3061 // "to" is the byte offset we want to move the
3062 // data to. "to" should be > "start".
3064 // we do the copy backwards to avoid problems if
3065 // there's an overlap.
3068 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3071 size_t chunk
, orig_chunk
;
3074 kauth_cred_t ucred
= vfs_context_ucred(context
);
3075 proc_t p
= vfs_context_proc(context
);
3077 if (delta
== 0 || len
== 0) {
3087 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3091 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
3092 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3094 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3095 pos
, ret
, chunk
, ret
);
3099 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3101 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3102 pos
+delta
, ret
, chunk
, ret
);
3106 if ((pos
- (off_t
)chunk
) < start
) {
3107 chunk
= pos
- start
;
3109 if (chunk
== 0) { // we're all done
3114 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3121 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3124 size_t chunk
, orig_chunk
;
3128 kauth_cred_t ucred
= vfs_context_ucred(context
);
3129 proc_t p
= vfs_context_proc(context
);
3131 if (delta
== 0 || len
== 0) {
3142 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3146 for(pos
= start
; pos
< end
; 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
) > end
) {
3164 if (chunk
== 0) { // we're all done
3169 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3175 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3180 lf
.l_whence
= SEEK_SET
;
3183 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3184 /* Note: id is just a kernel address that's not a proc */
3185 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
);
3186 return (error
== ENOTSUP
? 0 : error
);
3190 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3195 lf
.l_whence
= SEEK_SET
;
3198 lf
.l_type
= F_UNLCK
;
3199 /* Note: id is just a kernel address that's not a proc */
3200 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);
3201 return (error
== ENOTSUP
? 0 : error
);