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 * Cast to 'unsigned int' loses precision - hope that's OK...
65 #define MAKE_SHADOW_NAME(VP, NAME) \
66 snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%x%08x%x", (unsigned int)(VP), (VP)->v_id, (unsigned int)(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
;
408 /* Make the file its parent.
409 * Note: This parent link helps us distinguish vnodes for
410 * shadow stream files from vnodes for resource fork on file
411 * systems that support named streams natively (both have
412 * VISNAMEDSTREAM set) by allowing access to mount structure
413 * for checking MNTK_NAMED_STREAMS bit at many places in the code
415 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
422 * Make a named stream for vnode vp.
425 vnode_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, int flags
, vfs_context_t context
)
429 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
430 error
= VNOP_MAKENAMEDSTREAM(vp
, svpp
, name
, flags
, context
);
432 error
= default_makenamedstream(vp
, svpp
, name
, context
);
435 uint32_t streamflags
= VISNAMEDSTREAM
;
439 if ((vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
) == 0) {
440 streamflags
|= VISSHADOW
;
444 vnode_lock_spin(svp
);
445 svp
->v_flag
|= streamflags
;
448 /* Make the file its parent.
449 * Note: This parent link helps us distinguish vnodes for
450 * shadow stream files from vnodes for resource fork on file
451 * systems that support named streams natively (both have
452 * VISNAMEDSTREAM set) by allowing access to mount structure
453 * for checking MNTK_NAMED_STREAMS bit at many places in the code
455 vnode_update_identity(svp
, vp
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
461 * Remove a named stream from vnode vp.
464 vnode_removenamedstream(vnode_t vp
, vnode_t svp
, const char *name
, int flags
, vfs_context_t context
)
468 if (vp
->v_mount
->mnt_kern_flag
& MNTK_NAMED_STREAMS
)
469 error
= VNOP_REMOVENAMEDSTREAM(vp
, svp
, name
, flags
, context
);
471 error
= default_removenamedstream(vp
, name
, context
);
476 #define NS_IOBUFSIZE (128 * 1024)
479 * Release a named stream shadow file.
481 * Note: This function is called from two places where we do not need
482 * to check if the vnode has any references held before deleting the
483 * shadow file. Once from vclean() when the vnode is being reclaimed
484 * and we do not hold any references on the vnode. Second time from
485 * default_getnamedstream() when we get an error during shadow stream
486 * file initialization so that other processes who are waiting for the
487 * shadow stream file initialization by the creator will get opportunity
488 * to create and initialize the file again.
491 vnode_relenamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
494 struct componentname cn
;
501 MAKE_SHADOW_NAME(vp
, tmpname
);
504 cn
.cn_nameiop
= DELETE
;
505 cn
.cn_flags
= ISLASTCN
;
506 cn
.cn_context
= context
;
507 cn
.cn_pnbuf
= tmpname
;
508 cn
.cn_pnlen
= sizeof(tmpname
);
509 cn
.cn_nameptr
= cn
.cn_pnbuf
;
510 cn
.cn_namelen
= strlen(tmpname
);
512 /* Obtain the vnode for the shadow files directory. */
513 err
= get_shadow_dir(&dvp
, context
);
518 (void) VNOP_REMOVE(dvp
, svp
, &cn
, 0, context
);
525 * Flush a named stream shadow file.
528 vnode_flushnamedstream(vnode_t vp
, vnode_t svp
, vfs_context_t context
)
530 struct vnode_attr va
;
532 caddr_t bufptr
= NULL
;
540 VATTR_WANTED(&va
, va_data_size
);
541 if (VNOP_GETATTR(svp
, &va
, context
) != 0 ||
542 !VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
545 datasize
= va
.va_data_size
;
546 if ((datasize
== 0)) {
547 (void) default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
551 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
552 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
555 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
559 * Copy the shadow stream file data into the resource fork.
561 error
= VNOP_OPEN(svp
, 0, context
);
563 printf("vnode_flushnamedstream: err %d opening file\n", error
);
566 while (offset
< datasize
) {
567 iosize
= MIN(datasize
- offset
, iosize
);
569 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
570 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
571 error
= VNOP_READ(svp
, auio
, 0, context
);
575 /* Since there's no truncate xattr we must remove the resource fork. */
577 error
= default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
578 if ((error
!= 0) && (error
!= ENOATTR
)) {
582 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_WRITE
);
583 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
584 error
= vn_setxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, XATTR_NOSECURITY
, context
);
590 (void) VNOP_CLOSE(svp
, 0, context
);
593 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
603 getshadowfile(vnode_t vp
, vnode_t
*svpp
, int makestream
, size_t *rsrcsize
,
604 int *creator
, vfs_context_t context
)
606 vnode_t dvp
= NULLVP
;
607 vnode_t svp
= NULLVP
;
608 struct componentname cn
;
609 struct vnode_attr va
;
616 /* Establish a unique file name. */
617 MAKE_SHADOW_NAME(vp
, tmpname
);
618 bzero(&cn
, sizeof(cn
));
619 cn
.cn_nameiop
= LOOKUP
;
620 cn
.cn_flags
= ISLASTCN
;
621 cn
.cn_context
= context
;
622 cn
.cn_pnbuf
= tmpname
;
623 cn
.cn_pnlen
= sizeof(tmpname
);
624 cn
.cn_nameptr
= cn
.cn_pnbuf
;
625 cn
.cn_namelen
= strlen(tmpname
);
627 /* Pick up uid, gid, mode and date from original file. */
629 VATTR_WANTED(&va
, va_uid
);
630 VATTR_WANTED(&va
, va_gid
);
631 VATTR_WANTED(&va
, va_mode
);
632 VATTR_WANTED(&va
, va_create_time
);
633 VATTR_WANTED(&va
, va_modify_time
);
634 if (VNOP_GETATTR(vp
, &va
, context
) != 0 ||
635 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
636 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
637 !VATTR_IS_SUPPORTED(&va
, va_mode
)) {
638 va
.va_uid
= KAUTH_UID_NONE
;
639 va
.va_gid
= KAUTH_GID_NONE
;
640 va
.va_mode
= S_IRUSR
| S_IWUSR
;
642 va
.va_vaflags
= VA_EXCLUSIVE
;
643 VATTR_SET(&va
, va_type
, VREG
);
644 /* We no longer change the access, but we still hide it. */
645 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
647 /* Obtain the vnode for the shadow files directory. */
648 if (get_shadow_dir(&dvp
, context
) != 0) {
653 /* See if someone else already has it open. */
654 if (VNOP_LOOKUP(dvp
, &svp
, &cn
, context
) == 0) {
655 /* Double check existence by asking for size. */
657 VATTR_WANTED(&va
, va_data_size
);
658 if (VNOP_GETATTR(svp
, &va
, context
) == 0 &&
659 VATTR_IS_SUPPORTED(&va
, va_data_size
)) {
660 goto out
; /* OK to use. */
664 /* Otherwise make sure the resource fork data exists. */
665 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, NULL
, &datasize
,
666 XATTR_NOSECURITY
, context
);
668 * To maintain binary compatibility with legacy Carbon
669 * emulated resource fork support, if the resource fork
670 * doesn't exist but the Finder Info does, then act as
671 * if an empty resource fork is present (see 4724359).
673 if ((error
== ENOATTR
) &&
674 (vn_getxattr(vp
, XATTR_FINDERINFO_NAME
, NULL
, &datasize
,
675 XATTR_NOSECURITY
, context
) == 0)) {
683 /* If the resource fork exists, its size is expected to be non-zero. */
690 /* Create the shadow stream file. */
691 error
= VNOP_CREATE(dvp
, &svp
, &cn
, &va
, context
);
694 } else if ((error
== EEXIST
) && !makestream
) {
695 error
= VNOP_LOOKUP(dvp
, &svp
, &cn
, context
);
702 /* On errors, clean up shadow stream file. */
710 *rsrcsize
= datasize
;
717 default_getnamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, enum nsoperation op
, vfs_context_t context
)
719 vnode_t svp
= NULLVP
;
721 caddr_t bufptr
= NULL
;
728 * Only the "com.apple.ResourceFork" stream is supported here.
730 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
736 * Obtain a shadow file for the resource fork I/O.
738 error
= getshadowfile(vp
, &svp
, 0, &datasize
, &creator
, context
);
745 * The creator of the shadow file provides its file data,
746 * all other threads should wait until its ready.
750 if (svp
->v_flag
& VISNAMEDSTREAM
) {
751 /* data is ready, go use it */
755 /* its not ready, wait for it (sleep using v_parent as channel) */
756 msleep((caddr_t
)&svp
->v_parent
, &svp
->v_lock
, PINOD
| PDROP
,
757 "getnamedstream", NULL
);
765 * Copy the real resource fork data into shadow stream file.
767 if (op
== NS_OPEN
&& datasize
!= 0) {
771 iosize
= bufsize
= MIN(datasize
, NS_IOBUFSIZE
);
772 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&bufptr
, bufsize
)) {
777 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
780 error
= VNOP_OPEN(svp
, 0, context
);
784 while (offset
< datasize
) {
787 iosize
= MIN(datasize
- offset
, iosize
);
789 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_READ
);
790 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
791 error
= vn_getxattr(vp
, XATTR_RESOURCEFORK_NAME
, auio
, &tmpsize
,
792 XATTR_NOSECURITY
, context
);
797 uio_reset(auio
, offset
, UIO_SYSSPACE32
, UIO_WRITE
);
798 uio_addiov(auio
, (uintptr_t)bufptr
, iosize
);
799 error
= VNOP_WRITE(svp
, auio
, 0, context
);
805 (void) VNOP_CLOSE(svp
, 0, context
);
808 /* Wake up anyone waiting for svp file content */
812 svp
->v_flag
|= VISNAMEDSTREAM
;
813 wakeup((caddr_t
)&svp
->v_parent
);
816 /* On post create errors, get rid of the shadow file. This
817 * way, if there is another process waiting for initialization
818 * of the shadow file by the current process, it will wake up
819 * and retry by creating and initializing the shadow file again.
821 (void) vnode_relenamedstream(vp
, svp
, context
);
822 wakeup((caddr_t
)&svp
->v_parent
);
827 kmem_free(kernel_map
, (vm_offset_t
)bufptr
, bufsize
);
833 /* On errors, clean up shadow stream file. */
844 default_makenamedstream(vnode_t vp
, vnode_t
*svpp
, const char *name
, vfs_context_t context
)
850 * Only the "com.apple.ResourceFork" stream is supported here.
852 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
856 error
= getshadowfile(vp
, svpp
, 1, NULL
, &creator
, context
);
859 * Wake up any waiters over in default_getnamedstream().
861 if ((error
== 0) && (*svpp
!= NULL
) && creator
) {
865 svp
->v_flag
|= VISNAMEDSTREAM
;
866 /* Wakeup any waiters on the v_parent channel */
867 wakeup((caddr_t
)&svp
->v_parent
);
874 default_removenamedstream(vnode_t vp
, const char *name
, vfs_context_t context
)
877 * Only the "com.apple.ResourceFork" stream is supported here.
879 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
883 * XXX - what about other opened instances?
885 return default_removexattr(vp
, XATTR_RESOURCEFORK_NAME
, 0, context
);
889 get_shadow_dir(vnode_t
*sdvpp
, vfs_context_t context
)
891 vnode_t dvp
= NULLVP
;
892 vnode_t sdvp
= NULLVP
;
893 struct componentname cn
;
894 struct vnode_attr va
;
899 /* Check if we've already created it. */
900 if (shadow_dvp
!= NULLVP
) {
901 if ((error
= vnode_getwithvid(shadow_dvp
, shadow_vid
))) {
909 /* Obtain the vnode for "/tmp" directory. */
910 if (vnode_lookup("/tmp", 0, &dvp
, context
) != 0) {
915 /* Create the shadow stream directory. */
916 snprintf(tmpname
, sizeof(tmpname
), ".vfs_rsrc_streams_%x%x",
917 (unsigned int)rootvnode
, shadow_sequence
);
918 bzero(&cn
, sizeof(cn
));
919 cn
.cn_nameiop
= LOOKUP
;
920 cn
.cn_flags
= ISLASTCN
;
921 cn
.cn_context
= context
;
922 cn
.cn_pnbuf
= tmpname
;
923 cn
.cn_pnlen
= sizeof(tmpname
);
924 cn
.cn_nameptr
= cn
.cn_pnbuf
;
925 cn
.cn_namelen
= strlen(tmpname
);
928 * owned by root, only readable by root, hidden
931 VATTR_SET(&va
, va_uid
, 0);
932 VATTR_SET(&va
, va_gid
, 0);
933 VATTR_SET(&va
, va_mode
, S_IRUSR
| S_IXUSR
);
934 VATTR_SET(&va
, va_type
, VDIR
);
935 VATTR_SET(&va
, va_flags
, UF_HIDDEN
);
936 va
.va_vaflags
= VA_EXCLUSIVE
;
938 error
= VNOP_MKDIR(dvp
, &sdvp
, &cn
, &va
, context
);
941 * There can be only one winner for an exclusive create.
944 /* Take a long term ref to keep this dir around. */
945 error
= vnode_ref(sdvp
);
948 shadow_vid
= sdvp
->v_id
;
950 } else if (error
== EEXIST
) {
951 /* loser has to look up directory */
952 error
= VNOP_LOOKUP(dvp
, &sdvp
, &cn
, context
);
954 /* Make sure its in fact a directory */
955 if (sdvp
->v_type
!= VDIR
) {
958 /* Obtain the fsid for /tmp directory */
960 VATTR_WANTED(&va
, va_fsid
);
961 if (VNOP_GETATTR(dvp
, &va
, context
) != 0 ||
962 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
965 tmp_fsid
= va
.va_fsid
;
968 VATTR_WANTED(&va
, va_uid
);
969 VATTR_WANTED(&va
, va_gid
);
970 VATTR_WANTED(&va
, va_mode
);
971 VATTR_WANTED(&va
, va_fsid
);
972 VATTR_WANTED(&va
, va_dirlinkcount
);
973 VATTR_WANTED(&va
, va_acl
);
974 /* Provide defaults for attrs that may not be supported */
975 va
.va_dirlinkcount
= 1;
976 va
.va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
978 if (VNOP_GETATTR(sdvp
, &va
, context
) != 0 ||
979 !VATTR_IS_SUPPORTED(&va
, va_uid
) ||
980 !VATTR_IS_SUPPORTED(&va
, va_gid
) ||
981 !VATTR_IS_SUPPORTED(&va
, va_mode
) ||
982 !VATTR_IS_SUPPORTED(&va
, va_fsid
)) {
986 * Make sure its what we want:
988 * - not writable by anyone
989 * - on same file system as /tmp
990 * - not a hard-linked directory
991 * - no ACLs (they might grant write access)
993 if ((va
.va_uid
!= 0) || (va
.va_gid
!= 0) ||
994 (va
.va_mode
& (S_IWUSR
| S_IRWXG
| S_IRWXO
)) ||
995 (va
.va_fsid
!= tmp_fsid
) ||
996 (va
.va_dirlinkcount
!= 1) ||
997 (va
.va_acl
!= (kauth_acl_t
) KAUTH_FILESEC_NONE
)) {
1007 /* On errors, clean up shadow stream directory. */
1017 /* This is not the dir we're looking for, move along */
1018 ++shadow_sequence
; /* try something else next time */
1027 * Default Implementation (Non-native EA)
1032 Typical "._" AppleDouble Header File layout:
1033 ------------------------------------------------------------
1038 .-- AD ENTRY[0] Finder Info Entry (must be first)
1039 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1041 | ///////////// Fixed Size Data (32 bytes)
1045 | ATTR ENTRY[1] --+--.
1046 | ATTR ENTRY[2] --+--+--.
1048 | ATTR ENTRY[N] --+--+--+--.
1049 | ATTR DATA 0 <-' | | |
1050 | //////////// | | |
1051 | ATTR DATA 1 <----' | |
1053 | ATTR DATA 2 <-------' |
1056 | ATTR DATA N <----------'
1058 | Attribute Free Space
1060 '----> RESOURCE FORK
1061 ///////////// Variable Sized Data
1070 ------------------------------------------------------------
1072 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1073 stored as part of the Finder Info. The length in the Finder
1074 Info AppleDouble entry includes the length of the extended
1075 attribute header, attribute entries, and attribute data.
1080 * On Disk Data Structures
1082 * Note: Motorola 68K alignment and big-endian.
1084 * See RFC 1740 for additional information about the AppleDouble file format.
1088 #define ADH_MAGIC 0x00051607
1089 #define ADH_VERSION 0x00020000
1090 #define ADH_MACOSX "Mac OS X "
1093 * AppleDouble Entry ID's
1095 #define AD_DATA 1 /* Data fork */
1096 #define AD_RESOURCE 2 /* Resource fork */
1097 #define AD_REALNAME 3 /* FileÕs name on home file system */
1098 #define AD_COMMENT 4 /* Standard Mac comment */
1099 #define AD_ICONBW 5 /* Mac black & white icon */
1100 #define AD_ICONCOLOR 6 /* Mac color icon */
1101 #define AD_UNUSED 7 /* Not used */
1102 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1103 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1104 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1105 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1106 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1107 #define AD_AFPNAME 13 /* Short name on AFP server */
1108 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1109 #define AD_AFPDIRID 15 /* AFP directory ID */
1110 #define AD_ATTRIBUTES AD_FINDERINFO
1113 #define ATTR_FILE_PREFIX "._"
1114 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1116 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1118 /* Implementation Limits */
1119 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
1120 #define ATTR_MAX_HDR_SIZE 65536
1122 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1123 * size supported (including the attribute entries). All of
1124 * the attribute entries must reside within this limit. If
1125 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1126 * boundry, then all of the attribute data I/O is performed
1127 * separately from the attribute header I/O.
1129 * In particular, all of the attr_entry structures must lie
1130 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1131 * AppleDouble file. However, the attribute data (i.e. the
1132 * contents of the extended attributes) may extend beyond the
1133 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1134 * limit is to allow the implementation to optimize by reading
1135 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1139 #pragma options align=mac68k
1141 #define FINDERINFOSIZE 32
1143 typedef struct apple_double_entry
{
1144 u_int32_t type
; /* entry type: see list, 0 invalid */
1145 u_int32_t offset
; /* entry data offset from the beginning of the file. */
1146 u_int32_t length
; /* entry data length in bytes. */
1147 } apple_double_entry_t
;
1150 typedef struct apple_double_header
{
1151 u_int32_t magic
; /* == ADH_MAGIC */
1152 u_int32_t version
; /* format version: 2 = 0x00020000 */
1153 u_int32_t filler
[4];
1154 u_int16_t numEntries
; /* number of entries which follow */
1155 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
1156 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
1157 u_int8_t pad
[2]; /* get better alignment inside attr_header */
1158 } apple_double_header_t
;
1160 #define ADHDRSIZE (4+4+16+2)
1162 /* Entries are aligned on 4 byte boundaries */
1163 typedef struct attr_entry
{
1164 u_int32_t offset
; /* file offset to data */
1165 u_int32_t length
; /* size of attribute data */
1168 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1172 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1173 typedef struct attr_header
{
1174 apple_double_header_t appledouble
;
1175 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
1176 u_int32_t debug_tag
; /* for debugging == file id of owning file */
1177 u_int32_t total_size
; /* file offset of end of attribute header + entries + data */
1178 u_int32_t data_start
; /* file offset to attribute data area */
1179 u_int32_t data_length
; /* length of attribute data area */
1180 u_int32_t reserved
[3];
1182 u_int16_t num_attrs
;
1186 /* Empty Resource Fork Header */
1187 typedef struct rsrcfork_header
{
1188 u_int32_t fh_DataOffset
;
1189 u_int32_t fh_MapOffset
;
1190 u_int32_t fh_DataLength
;
1191 u_int32_t fh_MapLength
;
1192 u_int8_t systemData
[112];
1193 u_int8_t appData
[128];
1194 u_int32_t mh_DataOffset
;
1195 u_int32_t mh_MapOffset
;
1196 u_int32_t mh_DataLength
;
1197 u_int32_t mh_MapLength
;
1199 u_int16_t mh_RefNum
;
1201 u_int8_t mh_InMemoryAttr
;
1204 u_int16_t typeCount
;
1205 } rsrcfork_header_t
;
1207 #define RF_FIRST_RESOURCE 256
1208 #define RF_NULL_MAP_LENGTH 30
1209 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1211 #pragma options align=reset
1213 /* Runtime information about the attribute file. */
1214 typedef struct attr_info
{
1215 vfs_context_t context
;
1220 size_t rawsize
; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1221 apple_double_header_t
*filehdr
;
1222 apple_double_entry_t
*finderinfo
;
1223 apple_double_entry_t
*rsrcfork
;
1224 attr_header_t
*attrhdr
;
1225 attr_entry_t
*attr_entry
;
1227 u_int8_t emptyfinderinfo
;
1231 #define ATTR_SETTING 1
1233 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1235 #define ATTR_ENTRY_LENGTH(namelen) \
1236 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1238 #define ATTR_NEXT(ae) \
1239 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1241 #define ATTR_VALID(ae, ai) \
1242 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1244 #define SWAP16(x) OSSwapBigToHostInt16((x))
1245 #define SWAP32(x) OSSwapBigToHostInt32((x))
1246 #define SWAP64(x) OSSwapBigToHostInt64((x))
1249 static u_int32_t emptyfinfo
[8] = {0};
1253 * Local support routines
1255 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
1257 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
1259 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
1261 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
1263 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
1265 static void rel_xattrinfo(attr_info_t
*ainfop
);
1267 static int write_xattrinfo(attr_info_t
*ainfop
);
1269 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
1271 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
1273 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
1276 #if BYTE_ORDER == LITTLE_ENDIAN
1277 static void swap_adhdr(apple_double_header_t
*adh
);
1278 static void swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
);
1281 #define swap_adhdr(x)
1282 #define swap_attrhdr(x, y)
1285 static int check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
* ainfop
);
1286 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1287 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
1291 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1292 * is in big endian (as it would exist on disk). Verifies the following:
1295 * - number of entries
1296 * - that each entry fits within the file size
1298 * If the header is invalid, ENOATTR is returned.
1300 * NOTE: Does not attempt to validate the extended attributes header that
1301 * may be embedded in the Finder Info entry.
1303 static int check_and_swap_apple_double_header(attr_info_t
*ainfop
)
1306 u_int32_t header_end
;
1307 u_int32_t entry_end
;
1309 apple_double_header_t
*header
;
1311 rawsize
= ainfop
->rawsize
;
1312 header
= (apple_double_header_t
*) ainfop
->rawdata
;
1314 /* Is the file big enough to contain an AppleDouble header? */
1315 if (rawsize
< offsetof(apple_double_header_t
, entries
))
1318 /* Swap the AppleDouble header fields to native order */
1319 header
->magic
= SWAP32(header
->magic
);
1320 header
->version
= SWAP32(header
->version
);
1321 header
->numEntries
= SWAP16(header
->numEntries
);
1323 /* Sanity check the AppleDouble header fields */
1324 if (header
->magic
!= ADH_MAGIC
||
1325 header
->version
!= ADH_VERSION
||
1326 header
->numEntries
< 1 ||
1327 header
->numEntries
> 15) {
1331 /* Calculate where the entries[] array ends */
1332 header_end
= offsetof(apple_double_header_t
, entries
) +
1333 header
->numEntries
* sizeof(apple_double_entry_t
);
1335 /* Is the file big enough to contain the AppleDouble entries? */
1336 if (rawsize
< header_end
) {
1340 /* Swap and sanity check each AppleDouble entry */
1341 for (i
=0; i
<header
->numEntries
; i
++) {
1342 /* Swap the per-entry fields to native order */
1343 header
->entries
[i
].type
= SWAP32(header
->entries
[i
].type
);
1344 header
->entries
[i
].offset
= SWAP32(header
->entries
[i
].offset
);
1345 header
->entries
[i
].length
= SWAP32(header
->entries
[i
].length
);
1347 entry_end
= header
->entries
[i
].offset
+ header
->entries
[i
].length
;
1350 * Does the entry's content start within the header itself,
1351 * did the addition overflow, or does the entry's content
1352 * extend past the end of the file?
1354 if (header
->entries
[i
].offset
< header_end
||
1355 entry_end
< header
->entries
[i
].offset
||
1356 entry_end
> ainfop
->filesize
) {
1361 * Does the current entry's content overlap with a previous
1364 * Yes, this is O(N**2), and there are more efficient algorithms
1365 * for testing pairwise overlap of N ranges when N is large.
1366 * But we have already ensured N < 16, and N is almost always 2.
1367 * So there's no point in using a more complex algorithm.
1370 for (j
=0; j
<i
; j
++) {
1371 if (entry_end
> header
->entries
[j
].offset
&&
1372 header
->entries
[j
].offset
+ header
->entries
[j
].length
> header
->entries
[i
].offset
) {
1384 * Retrieve the data of an extended attribute.
1387 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
1388 __unused
int options
, vfs_context_t context
)
1392 attr_header_t
*header
;
1393 attr_entry_t
*entry
;
1403 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1406 * Open the file locked (shared) since the Carbon
1407 * File Manager may have the Apple Double file open
1408 * and could be changing the resource fork.
1410 fileflags
|= O_SHLOCK
;
1415 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1418 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1419 close_xattrfile(xvp
, fileflags
, context
);
1423 /* Get the Finder Info. */
1424 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1426 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1428 } else if (uio
== NULL
) {
1429 *size
= FINDERINFOSIZE
;
1431 } else if (uio_offset(uio
) != 0) {
1433 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
1436 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1437 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
1442 /* Read the Resource Fork. */
1444 if (!vnode_isreg(vp
)) {
1446 } else if (ainfo
.rsrcfork
== NULL
) {
1448 } else if (uio
== NULL
) {
1449 *size
= (size_t)ainfo
.rsrcfork
->length
;
1451 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1452 error
= VNOP_READ(xvp
, uio
, 0, context
);
1454 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1459 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
1463 if (uio_offset(uio
) != 0) {
1468 namelen
= strlen(name
) + 1;
1469 header
= ainfo
.attrhdr
;
1470 entry
= ainfo
.attr_entry
;
1472 * Search for attribute name in the header.
1474 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1475 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1476 datalen
= (size_t)entry
->length
;
1482 if (uio_resid(uio
) < (user_ssize_t
)datalen
) {
1486 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
1487 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
1488 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1490 uio_setoffset(uio
, entry
->offset
);
1491 error
= VNOP_READ(xvp
, uio
, 0, context
);
1492 uio_setoffset(uio
, 0);
1496 entry
= ATTR_NEXT(entry
);
1499 rel_xattrinfo(&ainfo
);
1500 close_xattrfile(xvp
, fileflags
, context
);
1506 * Set the data of an extended attribute.
1509 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
1513 attr_header_t
*header
;
1514 attr_entry_t
*entry
;
1515 attr_entry_t
*lastentry
;
1519 size_t datafreespace
;
1526 char finfo
[FINDERINFOSIZE
];
1528 datalen
= uio_resid(uio
);
1529 namelen
= strlen(name
) + 1;
1530 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1533 * By convention, Finder Info that is all zeroes is equivalent to not
1534 * having a Finder Info EA. So if we're trying to set the Finder Info
1535 * to all zeroes, then delete it instead. If a file didn't have an
1536 * AppleDouble file before, this prevents creating an AppleDouble file
1537 * with no useful content.
1539 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1540 * for all zeroes Finder Info before opening the AppleDouble file.
1541 * But if either of those options were specified, we need to open the
1542 * AppleDouble file to see whether there was already Finder Info (so we
1543 * can return an error if needed); this case is handled further below.
1545 * NOTE: this copies the Finder Info data into the "finfo" local.
1547 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1549 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1550 * That means we probably have to open_xattrfile and get_xattrinfo.
1552 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
1555 error
= uiomove(finfo
, datalen
, uio
);
1558 if ((options
& (XATTR_CREATE
|XATTR_REPLACE
)) == 0 &&
1559 bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1560 error
= default_removexattr(vp
, name
, 0, context
);
1561 if (error
== ENOATTR
)
1569 * Open the file locked since setting an attribute
1570 * can change the layout of the Apple Double file.
1572 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
1573 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
1576 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
1577 close_xattrfile(xvp
, fileflags
, context
);
1581 /* Set the Finder Info. */
1582 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1583 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1584 /* attr exists and "create" was specified? */
1585 if (options
& XATTR_CREATE
) {
1590 /* attr doesn't exists and "replace" was specified? */
1591 if (options
& XATTR_REPLACE
) {
1596 if (options
!= 0 && bcmp(finfo
, emptyfinfo
, FINDERINFOSIZE
) == 0) {
1598 * Setting the Finder Info to all zeroes is equivalent to
1599 * removing it. Close the xattr file and let
1600 * default_removexattr do the work (including deleting
1601 * the xattr file if there are no other xattrs).
1603 * Note that we have to handle the case where the
1604 * Finder Info was already all zeroes, and we ignore
1607 * The common case where options == 0 was handled above.
1609 rel_xattrinfo(&ainfo
);
1610 close_xattrfile(xvp
, fileflags
, context
);
1611 error
= default_removexattr(vp
, name
, 0, context
);
1612 if (error
== ENOATTR
)
1616 if (ainfo
.finderinfo
) {
1617 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1618 bcopy(finfo
, attrdata
, datalen
);
1619 ainfo
.iosize
= sizeof(attr_header_t
);
1620 error
= write_xattrinfo(&ainfo
);
1627 /* Write the Resource Fork. */
1628 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1629 u_int32_t endoffset
;
1631 if (!vnode_isreg(vp
)) {
1635 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
1636 /* attr exists and "create" was specified? */
1637 if (options
& XATTR_CREATE
) {
1642 /* attr doesn't exists and "replace" was specified? */
1643 if (options
& XATTR_REPLACE
) {
1648 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
1649 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
1650 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1653 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
1654 if (endoffset
> ainfo
.rsrcfork
->length
) {
1655 ainfo
.rsrcfork
->length
= endoffset
;
1656 ainfo
.iosize
= sizeof(attr_header_t
);
1657 error
= write_xattrinfo(&ainfo
);
1663 if (datalen
> ATTR_MAX_SIZE
) {
1664 return (E2BIG
); /* EINVAL instead ? */
1667 if (ainfo
.attrhdr
== NULL
) {
1671 header
= ainfo
.attrhdr
;
1672 entry
= ainfo
.attr_entry
;
1674 /* Check if data area crosses the maximum header size. */
1675 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
1676 splitdata
= 1; /* do data I/O separately */
1681 * See if attribute already exists.
1683 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1684 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1688 entry
= ATTR_NEXT(entry
);
1692 if (options
& XATTR_CREATE
) {
1696 if (datalen
== entry
->length
) {
1698 uio_setoffset(uio
, entry
->offset
);
1699 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1700 uio_setoffset(uio
, 0);
1702 printf("setxattr: VNOP_WRITE error %d\n", error
);
1705 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
1706 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1709 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1710 error
= write_xattrinfo(&ainfo
);
1712 printf("setxattr: write_xattrinfo error %d\n", error
);
1718 * Brute force approach - just remove old entry and set new entry.
1721 rel_xattrinfo(&ainfo
);
1722 close_xattrfile(xvp
, fileflags
, context
);
1723 error
= default_removexattr(vp
, name
, options
, context
);
1727 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1728 options
&= ~XATTR_REPLACE
;
1729 goto start
; /* start over */
1734 if (options
& XATTR_REPLACE
) {
1735 error
= ENOATTR
; /* nothing there to replace */
1738 /* Check if header size limit has been reached. */
1739 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
1744 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
1746 /* Check if we need more space. */
1747 if ((datalen
+ entrylen
) > datafreespace
) {
1750 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
1752 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1753 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
1754 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
1757 ainfo
.filesize
+= growsize
;
1758 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1760 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
1766 * Move the resource fork out of the way.
1768 if (ainfo
.rsrcfork
) {
1769 if (ainfo
.rsrcfork
->length
!= 0) {
1770 shift_data_down(xvp
,
1771 ainfo
.rsrcfork
->offset
,
1772 ainfo
.rsrcfork
->length
,
1775 ainfo
.rsrcfork
->offset
+= growsize
;
1777 ainfo
.finderinfo
->length
+= growsize
;
1778 header
->total_size
+= growsize
;
1781 /* Make space for a new entry. */
1783 shift_data_down(xvp
,
1785 header
->data_length
,
1788 bcopy((u_int8_t
*)header
+ header
->data_start
,
1789 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
1790 header
->data_length
);
1792 header
->data_start
+= entrylen
;
1794 /* Fix up entry data offsets. */
1796 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
1797 entry
->offset
+= entrylen
;
1801 * If the attribute data area is entirely within
1802 * the header buffer, then just update the buffer,
1803 * otherwise we'll write it separately to the file.
1808 /* Write new attribute data after the end of existing data. */
1809 offset
= header
->data_start
+ header
->data_length
;
1810 uio_setoffset(uio
, offset
);
1811 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
1812 uio_setoffset(uio
, 0);
1814 printf("setxattr: VNOP_WRITE error %d\n", error
);
1818 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
1820 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
1822 printf("setxattr: uiomove error %d\n", error
);
1827 /* Create the attribute entry. */
1828 lastentry
->length
= datalen
;
1829 lastentry
->offset
= header
->data_start
+ header
->data_length
;
1830 lastentry
->namelen
= namelen
;
1831 lastentry
->flags
= 0;
1832 bcopy(name
, &lastentry
->name
[0], namelen
);
1834 /* Update the attributes header. */
1835 header
->num_attrs
++;
1836 header
->data_length
+= datalen
;
1839 /* Only write the entries, since the data was written separately. */
1840 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
1842 /* The entry and data are both in the header; write them together. */
1843 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1845 error
= write_xattrinfo(&ainfo
);
1847 printf("setxattr: write_xattrinfo error %d\n", error
);
1851 rel_xattrinfo(&ainfo
);
1852 close_xattrfile(xvp
, fileflags
, context
);
1854 /* Touch the change time if we changed an attribute. */
1856 struct vnode_attr va
;
1858 /* Re-write the mtime to cause a ctime change. */
1860 VATTR_WANTED(&va
, va_modify_time
);
1861 if (vnode_getattr(vp
, &va
, context
) == 0) {
1863 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1864 (void) vnode_setattr(vp
, &va
, context
);
1872 * Remove an extended attribute.
1875 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
1879 attr_header_t
*header
;
1880 attr_entry_t
*entry
;
1881 attr_entry_t
*oldslot
;
1887 int found
= 0, lastone
= 0;
1895 fileflags
= FREAD
| FWRITE
;
1896 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
1899 * Open the file locked (exclusive) since the Carbon
1900 * File Manager may have the Apple Double file open
1901 * and could be changing the resource fork.
1903 fileflags
|= O_EXLOCK
;
1908 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1911 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1912 close_xattrfile(xvp
, fileflags
, context
);
1916 attrcount
+= ainfo
.attrhdr
->num_attrs
;
1919 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
1922 /* Clear the Finder Info. */
1923 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1924 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1928 /* On removal of last attribute the ._ file is removed. */
1929 if (--attrcount
== 0)
1931 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1932 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
1933 ainfo
.iosize
= sizeof(attr_header_t
);
1934 error
= write_xattrinfo(&ainfo
);
1938 /* Clear the Resource Fork. */
1940 if (!vnode_isreg(vp
)) {
1944 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
1948 /* On removal of last attribute the ._ file is removed. */
1949 if (--attrcount
== 0)
1953 * If the resource fork isn't the last AppleDouble
1954 * entry then the space needs to be reclaimed by
1955 * shifting the entries after the resource fork.
1957 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
1958 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
1959 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1962 ainfo
.rsrcfork
->length
= 0;
1963 ainfo
.iosize
= sizeof(attr_header_t
);
1964 error
= write_xattrinfo(&ainfo
);
1969 if (ainfo
.attrhdr
== NULL
) {
1973 namelen
= strlen(name
) + 1;
1974 header
= ainfo
.attrhdr
;
1975 entry
= ainfo
.attr_entry
;
1978 * See if this attribute exists.
1980 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1981 if (strncmp((const char *)entry
->name
, name
, namelen
) == 0) {
1983 if ((i
+1) == header
->num_attrs
)
1987 entry
= ATTR_NEXT(entry
);
1993 /* On removal of last attribute the ._ file is removed. */
1994 if (--attrcount
== 0)
1997 datalen
= entry
->length
;
1998 dataoff
= entry
->offset
;
1999 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
2000 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
2005 /* Remove the attribute entry. */
2007 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
2008 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
2011 /* Adjust the attribute data. */
2015 dataoff
- header
->data_start
,
2021 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
2025 /* XXX write zeros to freed space ? */
2026 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
2030 bcopy((u_int8_t
*)header
+ header
->data_start
,
2031 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
2032 dataoff
- header
->data_start
);
2034 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
2035 (u_int8_t
*)header
+ dataoff
- entrylen
,
2036 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
2038 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
2039 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
2042 /* Adjust the header values and entry offsets. */
2043 header
->num_attrs
--;
2044 header
->data_start
-= entrylen
;
2045 header
->data_length
-= datalen
;
2048 entry
= ainfo
.attr_entry
;
2049 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
2050 entry
->offset
-= entrylen
;
2051 if (entry
>= oldslot
)
2052 entry
->offset
-= datalen
;
2053 entry
= ATTR_NEXT(entry
);
2055 error
= write_xattrinfo(&ainfo
);
2057 printf("removexattr: write_xattrinfo error %d\n", error
);
2060 rel_xattrinfo(&ainfo
);
2062 /* When there are no more attributes remove the ._ file. */
2063 if (attrcount
== 0) {
2064 if (fileflags
& O_EXLOCK
)
2065 (void) unlock_xattrfile(xvp
, context
);
2066 VNOP_CLOSE(xvp
, fileflags
, context
);
2068 error
= remove_xattrfile(xvp
, context
);
2071 close_xattrfile(xvp
, fileflags
, context
);
2073 /* Touch the change time if we changed an attribute. */
2075 struct vnode_attr va
;
2077 /* Re-write the mtime to cause a ctime change. */
2079 VATTR_WANTED(&va
, va_modify_time
);
2080 if (vnode_getattr(vp
, &va
, context
) == 0) {
2082 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
2083 (void) vnode_setattr(vp
, &va
, context
);
2092 * Retrieve the list of extended attribute names.
2095 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
2099 attr_entry_t
*entry
;
2104 * We do not zero "*size" here as we don't want to stomp a size set when
2105 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2106 * system call layer, up in listxattr or flistxattr.
2109 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
2110 if (error
== ENOATTR
)
2114 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
2115 close_xattrfile(xvp
, FREAD
, context
);
2119 /* Check for Finder Info. */
2120 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
2122 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
2123 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_FINDERINFO_NAME
)) {
2127 error
= uiomove(XATTR_FINDERINFO_NAME
,
2128 sizeof(XATTR_FINDERINFO_NAME
), uio
);
2136 /* Check for Resource Fork. */
2137 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
2139 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
2140 } else if (uio_resid(uio
) < (user_ssize_t
)sizeof(XATTR_RESOURCEFORK_NAME
)) {
2144 error
= uiomove(XATTR_RESOURCEFORK_NAME
,
2145 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
2153 /* Check for attributes. */
2154 if (ainfo
.attrhdr
) {
2155 count
= ainfo
.attrhdr
->num_attrs
;
2156 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
2157 if (xattr_protected((const char *)entry
->name
) ||
2158 xattr_validatename((const char *)entry
->name
) != 0) {
2159 entry
= ATTR_NEXT(entry
);
2163 *size
+= entry
->namelen
;
2164 entry
= ATTR_NEXT(entry
);
2167 if (uio_resid(uio
) < entry
->namelen
) {
2171 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
2173 if (error
!= EFAULT
)
2177 entry
= ATTR_NEXT(entry
);
2181 rel_xattrinfo(&ainfo
);
2182 close_xattrfile(xvp
, FREAD
, context
);
2188 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
2190 vnode_t xvp
= NULLVP
;
2191 vnode_t dvp
= NULLVP
;
2192 struct vnode_attr va
;
2193 struct nameidata nd
;
2195 char *filename
= NULL
;
2196 const char *basename
= NULL
;
2202 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
2204 * For the root directory use "._." to hold the attributes.
2206 filename
= &smallname
[0];
2207 snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, ".");
2208 dvp
= vp
; /* the "._." file resides in the root dir */
2211 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
2215 if ( (basename
= vnode_getname(vp
)) == NULL
) {
2220 /* "._" Attribute files cannot have attributes */
2221 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
2222 basename
[0] == '.' && basename
[1] == '_') {
2226 filename
= &smallname
[0];
2227 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
2228 if (len
>= sizeof(smallname
)) {
2229 len
++; /* snprintf result doesn't include '\0' */
2230 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
2231 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
2234 * Note that the lookup here does not authorize. Since we are looking
2235 * up in the same directory that we already have the file vnode in,
2236 * we must have been given the file vnode legitimately. Read/write
2237 * access has already been authorized in layers above for calls from
2238 * userspace, and the authorization code using this path to read
2239 * file security from the EA must always get access
2242 NDINIT(&nd
, LOOKUP
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
, UIO_SYSSPACE
,
2243 CAST_USER_ADDR_T(filename
), context
);
2246 if (fileflags
& O_CREAT
) {
2247 nd
.ni_cnd
.cn_nameiop
= CREATE
;
2249 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
2251 if ( (error
= namei(&nd
))) {
2256 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
2262 * Pick up uid/gid/mode from target file.
2265 VATTR_WANTED(&va
, va_uid
);
2266 VATTR_WANTED(&va
, va_gid
);
2267 VATTR_WANTED(&va
, va_mode
);
2268 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
2269 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
2270 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
2271 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
2274 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
2275 } else /* fallback values */ {
2276 uid
= KAUTH_UID_NONE
;
2277 gid
= KAUTH_GID_NONE
;
2278 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
2282 VATTR_SET(&va
, va_type
, VREG
);
2283 VATTR_SET(&va
, va_mode
, umode
);
2284 if (uid
!= KAUTH_UID_NONE
)
2285 VATTR_SET(&va
, va_uid
, uid
);
2286 if (gid
!= KAUTH_GID_NONE
)
2287 VATTR_SET(&va
, va_gid
, gid
);
2289 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &va
,
2290 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
| VN_CREATE_NOLABEL
,
2297 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
2302 if ((error
= namei(&nd
))) {
2312 if (xvp
->v_type
!= VREG
) {
2317 * Owners must match.
2320 VATTR_WANTED(&va
, va_uid
);
2321 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
2322 uid_t owner
= va
.va_uid
;
2325 VATTR_WANTED(&va
, va_uid
);
2326 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
2327 error
= ENOATTR
; /* don't use this "._" file */
2332 if ( (error
= VNOP_OPEN(xvp
, fileflags
& ~(O_EXLOCK
| O_SHLOCK
), context
))) {
2338 if ((error
= vnode_ref(xvp
))) {
2343 /* If create was requested, make sure file header exists. */
2344 if (fileflags
& O_CREAT
) {
2346 VATTR_WANTED(&va
, va_data_size
);
2347 VATTR_WANTED(&va
, va_fileid
);
2348 VATTR_WANTED(&va
, va_nlink
);
2349 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
2354 /* If the file is empty then add a default header. */
2355 if (va
.va_data_size
== 0) {
2356 /* Don't adopt hard-linked "._" files. */
2357 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
2361 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
2365 /* Apply file locking if requested. */
2366 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
2369 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
2370 error
= lock_xattrfile(xvp
, locktype
, context
);
2373 if (dvp
&& (dvp
!= vp
)) {
2377 vnode_putname(basename
);
2379 if (filename
&& filename
!= &smallname
[0]) {
2380 FREE(filename
, M_TEMP
);
2383 if (xvp
!= NULLVP
) {
2385 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2388 (void) vnode_rele(xvp
);
2390 (void) vnode_put(xvp
);
2393 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
2397 *xvpp
= xvp
; /* return a referenced vnode */
2402 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
2404 // if (fileflags & FWRITE)
2405 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2407 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
2408 (void) unlock_xattrfile(xvp
, context
);
2410 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
2411 (void) vnode_rele(xvp
);
2412 (void) vnode_put(xvp
);
2416 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
2419 struct nameidata nd
;
2424 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2428 pathlen
= MAXPATHLEN
;
2429 error
= vn_getpath(xvp
, path
, &pathlen
);
2431 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2435 NDINIT(&nd
, DELETE
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
2436 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
2438 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
2445 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
2454 * Read in and parse the AppleDouble header and entries, and the extended
2455 * attribute header and entries if any. Populates the fields of ainfop
2456 * based on the headers and entries found.
2458 * The basic idea is to:
2459 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2460 * AppleDouble entries, the extended attribute header, and extended
2461 * attribute entries must lie within this part of the file; the rest of
2462 * the AppleDouble handling code assumes this. Plus it allows us to
2463 * somewhat optimize by doing a smaller number of larger I/Os.
2464 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2466 * - Find the Finder Info and Resource Fork entries, if any.
2467 * - If we're going to be writing, try to make sure the Finder Info entry has
2468 * room to store the extended attribute header, plus some space for extended
2470 * - Swap and sanity check the extended attribute header and entries (if any).
2473 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
2476 void * buffer
= NULL
;
2477 apple_double_header_t
*filehdr
;
2478 struct vnode_attr va
;
2483 bzero(ainfop
, sizeof(attr_info_t
));
2484 ainfop
->filevp
= xvp
;
2485 ainfop
->context
= context
;
2487 VATTR_WANTED(&va
, va_data_size
);
2488 VATTR_WANTED(&va
, va_fileid
);
2489 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
2492 ainfop
->filesize
= va
.va_data_size
;
2494 /* When setting attributes, allow room for the header to grow. */
2496 iosize
= ATTR_MAX_HDR_SIZE
;
2498 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
2504 ainfop
->iosize
= iosize
;
2505 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
2506 if (buffer
== NULL
){
2511 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2512 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
2514 /* Read the file header. */
2515 error
= VNOP_READ(xvp
, auio
, 0, context
);
2519 ainfop
->rawsize
= iosize
- uio_resid(auio
);
2520 ainfop
->rawdata
= (u_int8_t
*)buffer
;
2522 filehdr
= (apple_double_header_t
*)buffer
;
2524 error
= check_and_swap_apple_double_header(ainfop
);
2528 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
2530 /* rel_xattrinfo is responsible for freeing the header buffer */
2533 /* Find the Finder Info and Resource Fork entries, if any */
2534 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
2535 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
2536 filehdr
->entries
[i
].length
>= FINDERINFOSIZE
) {
2537 /* We found the Finder Info entry. */
2538 ainfop
->finderinfo
= &filehdr
->entries
[i
];
2541 * Is the Finder Info "empty" (all zeroes)? If so,
2542 * we'll pretend like the Finder Info extended attribute
2545 * Note: we have to make sure the Finder Info is
2546 * contained within the buffer we have already read,
2547 * to avoid accidentally accessing a bogus address.
2548 * If it is outside the buffer, we just assume the
2549 * Finder Info is non-empty.
2551 if (ainfop
->finderinfo
->offset
+ FINDERINFOSIZE
<= ainfop
->rawsize
&&
2552 bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
, emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
2553 ainfop
->emptyfinderinfo
= 1;
2556 if (filehdr
->entries
[i
].type
== AD_RESOURCE
) {
2558 * Ignore zero-length resource forks when getting. If setting,
2559 * we need to remember the resource fork entry so it can be
2560 * updated once the new content has been written.
2562 if (filehdr
->entries
[i
].length
== 0 && !setting
)
2566 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2568 * The "empty" resource headers we created have a system data tag of:
2569 * "This resource fork intentionally left blank "
2571 if (filehdr
->entries
[i
].length
== sizeof(rsrcfork_header_t
) && !setting
) {
2573 u_int8_t systemData
[64];
2577 /* Read the system data which starts at byte 16 */
2578 rf_uio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
2579 uio_addiov(rf_uio
, (uintptr_t)systemData
, sizeof(systemData
));
2580 uio_setoffset(rf_uio
, filehdr
->entries
[i
].offset
+ 16);
2581 rf_err
= VNOP_READ(xvp
, rf_uio
, 0, context
);
2585 bcmp(systemData
, RF_EMPTY_TAG
, sizeof(RF_EMPTY_TAG
)) == 0) {
2586 continue; /* skip this resource fork */
2589 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
2590 if (i
!= (filehdr
->numEntries
- 1)) {
2591 printf("get_xattrinfo: resource fork not last entry\n");
2592 ainfop
->readonly
= 1;
2599 * See if this file looks like it is laid out correctly to contain
2600 * extended attributes. If so, then do the following:
2602 * - If we're going to be writing, try to make sure the Finder Info
2603 * entry has room to store the extended attribute header, plus some
2604 * space for extended attributes.
2606 * - Swap and sanity check the extended attribute header and entries
2609 if (filehdr
->numEntries
== 2 &&
2610 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2611 ainfop
->rsrcfork
== &filehdr
->entries
[1] &&
2612 ainfop
->finderinfo
->offset
== offsetof(apple_double_header_t
, finfo
)) {
2613 attr_header_t
*attrhdr
;
2614 attrhdr
= (attr_header_t
*)filehdr
;
2616 * If we're going to be writing, try to make sure the Finder
2617 * Info entry has room to store the extended attribute header,
2618 * plus some space for extended attributes.
2620 if (setting
&& ainfop
->finderinfo
->length
== FINDERINFOSIZE
) {
2624 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
2625 if (ainfop
->rsrcfork
&& filehdr
->entries
[1].length
) {
2626 /* Make some room before existing resource fork. */
2627 shift_data_down(xvp
,
2628 filehdr
->entries
[1].offset
,
2629 filehdr
->entries
[1].length
,
2631 writesize
= sizeof(attr_header_t
);
2633 /* Create a new, empty resource fork. */
2634 rsrcfork_header_t
*rsrcforkhdr
;
2636 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
2638 /* Steal some space for an empty RF header. */
2639 delta
-= sizeof(rsrcfork_header_t
);
2641 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
2642 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
2644 /* Fill in Empty Resource Fork Header. */
2645 init_empty_resource_fork(rsrcforkhdr
);
2647 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
2648 writesize
= ATTR_BUF_SIZE
;
2650 filehdr
->entries
[0].length
+= delta
;
2651 filehdr
->entries
[1].offset
+= delta
;
2653 /* Fill in Attribute Header. */
2654 attrhdr
->magic
= ATTR_HDR_MAGIC
;
2655 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
2656 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
2657 attrhdr
->data_start
= sizeof(attr_header_t
);
2658 attrhdr
->data_length
= 0;
2659 attrhdr
->reserved
[0] = 0;
2660 attrhdr
->reserved
[1] = 0;
2661 attrhdr
->reserved
[2] = 0;
2663 attrhdr
->num_attrs
= 0;
2665 /* Push out new header */
2666 uio_reset(auio
, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2667 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
2669 swap_adhdr(filehdr
); /* to big endian */
2670 swap_attrhdr(attrhdr
, ainfop
); /* to big endian */
2671 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2672 swap_adhdr(filehdr
); /* back to native */
2673 /* The attribute header gets swapped below. */
2677 * Swap and sanity check the extended attribute header and
2678 * entries (if any). The Finder Info content must be big enough
2679 * to include the extended attribute header; if not, we just
2682 * Note that we're passing the offset + length (i.e. the end)
2683 * of the Finder Info instead of rawsize to validate_attrhdr.
2684 * This ensures that all extended attributes lie within the
2685 * Finder Info content according to the AppleDouble entry.
2687 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2690 if (ainfop
->finderinfo
&&
2691 ainfop
->finderinfo
== &filehdr
->entries
[0] &&
2692 ainfop
->finderinfo
->length
>= (sizeof(attr_header_t
) - sizeof(apple_double_header_t
))) {
2693 attr_header_t
*attrhdr
= (attr_header_t
*)filehdr
;
2695 if ((error
= check_and_swap_attrhdr(attrhdr
, ainfop
)) == 0) {
2696 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
2697 /* First attr_entry starts immediately following attribute header */
2698 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
2707 FREE(buffer
, M_TEMP
);
2713 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
2716 rsrcfork_header_t
*rsrcforkhdr
;
2722 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
2723 bzero(buffer
, ATTR_BUF_SIZE
);
2725 xah
= (attr_header_t
*)buffer
;
2726 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2727 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
2728 rsrcforksize
= sizeof(rsrcfork_header_t
);
2729 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
2731 /* Fill in Apple Double Header. */
2732 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
2733 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
2734 xah
->appledouble
.numEntries
= SWAP16 (2);
2735 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
2736 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
2737 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
2738 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
2739 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2740 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
2741 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
2743 /* Fill in Attribute Header. */
2744 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
2745 xah
->debug_tag
= SWAP32 (fileid
);
2746 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
2747 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
2749 /* Fill in Empty Resource Fork Header. */
2750 init_empty_resource_fork(rsrcforkhdr
);
2753 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
2756 FREE(buffer
, M_TEMP
);
2762 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
2764 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
2765 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2766 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2767 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2768 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2769 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
2770 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
2771 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
2772 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
2773 rsrcforkhdr
->typeCount
= SWAP16 (-1);
2774 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
2778 rel_xattrinfo(attr_info_t
*ainfop
)
2780 FREE(ainfop
->filehdr
, M_TEMP
);
2781 bzero(ainfop
, sizeof(attr_info_t
));
2785 write_xattrinfo(attr_info_t
*ainfop
)
2790 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
2791 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
2793 swap_adhdr(ainfop
->filehdr
);
2794 if (ainfop
->attrhdr
!= NULL
) {
2795 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2798 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
2800 swap_adhdr(ainfop
->filehdr
);
2801 if (ainfop
->attrhdr
!= NULL
) {
2802 swap_attrhdr(ainfop
->attrhdr
, ainfop
);
2809 #if BYTE_ORDER == LITTLE_ENDIAN
2811 * Endian swap apple double header
2814 swap_adhdr(apple_double_header_t
*adh
)
2819 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
2821 adh
->magic
= SWAP32 (adh
->magic
);
2822 adh
->version
= SWAP32 (adh
->version
);
2823 adh
->numEntries
= SWAP16 (adh
->numEntries
);
2825 for (i
= 0; i
< count
; i
++) {
2826 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
2827 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
2828 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
2833 * Endian swap extended attributes header
2836 swap_attrhdr(attr_header_t
*ah
, attr_info_t
* info
)
2842 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
2844 ah
->magic
= SWAP32 (ah
->magic
);
2845 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
2846 ah
->total_size
= SWAP32 (ah
->total_size
);
2847 ah
->data_start
= SWAP32 (ah
->data_start
);
2848 ah
->data_length
= SWAP32 (ah
->data_length
);
2849 ah
->flags
= SWAP16 (ah
->flags
);
2850 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
2852 ae
= (attr_entry_t
*)(&ah
[1]);
2853 for (i
= 0; i
< count
&& ATTR_VALID(ae
, *info
); i
++, ae
= ATTR_NEXT(ae
)) {
2854 ae
->offset
= SWAP32 (ae
->offset
);
2855 ae
->length
= SWAP32 (ae
->length
);
2856 ae
->flags
= SWAP16 (ae
->flags
);
2862 * Validate and swap the attributes header contents, and each attribute's
2865 * Note: Assumes the caller has verified that the Finder Info content is large
2866 * enough to contain the attr_header structure itself. Therefore, we can
2867 * swap the header fields before sanity checking them.
2870 check_and_swap_attrhdr(attr_header_t
*ah
, attr_info_t
*ainfop
)
2881 if (SWAP32(ah
->magic
) != ATTR_HDR_MAGIC
)
2884 /* Swap the basic header fields */
2885 ah
->magic
= SWAP32(ah
->magic
);
2886 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
2887 ah
->total_size
= SWAP32 (ah
->total_size
);
2888 ah
->data_start
= SWAP32 (ah
->data_start
);
2889 ah
->data_length
= SWAP32 (ah
->data_length
);
2890 ah
->flags
= SWAP16 (ah
->flags
);
2891 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
2894 * Make sure the total_size fits within the Finder Info area, and the
2895 * extended attribute data area fits within total_size.
2897 end
= ah
->data_start
+ ah
->data_length
;
2898 if (ah
->total_size
> ainfop
->finderinfo
->offset
+ ainfop
->finderinfo
->length
||
2899 end
< ah
->data_start
||
2900 end
> ah
->total_size
) {
2905 * Make sure each of the attr_entry_t's fits within total_size.
2907 buf_end
= ainfop
->rawdata
+ ah
->total_size
;
2908 count
= ah
->num_attrs
;
2909 ae
= (attr_entry_t
*)(&ah
[1]);
2911 for (i
=0; i
<count
; i
++) {
2912 /* Make sure the fixed-size part of this attr_entry_t fits. */
2913 if ((u_int8_t
*) &ae
[1] > buf_end
)
2916 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
2917 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
2918 if (&ae
->name
[ae
->namelen
+1] > buf_end
)
2921 /* Swap the attribute entry fields */
2922 ae
->offset
= SWAP32(ae
->offset
);
2923 ae
->length
= SWAP32(ae
->length
);
2924 ae
->flags
= SWAP16(ae
->flags
);
2926 /* Make sure the attribute content fits. */
2927 end
= ae
->offset
+ ae
->length
;
2928 if (end
< ae
->offset
|| end
> ah
->total_size
)
2935 * TODO: Make sure the contents of attributes don't overlap the header
2936 * and don't overlap each other. The hard part is that we don't know
2937 * what the actual header size is until we have looped over all of the
2938 * variable-sized attribute entries.
2940 * XXX Is there any guarantee that attribute entries are stored in
2941 * XXX order sorted by the contents' file offset? If so, that would
2942 * XXX make the pairwise overlap check much easier.
2949 // "start" & "end" are byte offsets in the file.
2950 // "to" is the byte offset we want to move the
2951 // data to. "to" should be > "start".
2953 // we do the copy backwards to avoid problems if
2954 // there's an overlap.
2957 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
2960 size_t chunk
, orig_chunk
;
2963 ucred_t ucred
= vfs_context_ucred(context
);
2964 proc_t p
= vfs_context_proc(context
);
2966 if (delta
== 0 || len
== 0) {
2976 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
2980 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
2981 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
2983 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
2984 pos
, ret
, chunk
, ret
);
2988 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
2990 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
2991 pos
+delta
, ret
, chunk
, ret
);
2995 if ((pos
- chunk
) < start
) {
2996 chunk
= pos
- start
;
2998 if (chunk
== 0) { // we're all done
3003 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3010 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
3013 size_t chunk
, orig_chunk
;
3017 ucred_t ucred
= vfs_context_ucred(context
);
3018 proc_t p
= vfs_context_proc(context
);
3020 if (delta
== 0 || len
== 0) {
3031 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
3035 for(pos
= start
; pos
< end
; pos
+= chunk
) {
3036 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3038 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3039 pos
, ret
, chunk
, ret
);
3043 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
|IO_NOAUTH
, ucred
, &iolen
, p
);
3045 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3046 pos
+delta
, ret
, chunk
, ret
);
3050 if ((pos
+ chunk
) > end
) {
3053 if (chunk
== 0) { // we're all done
3058 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
3064 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
3069 lf
.l_whence
= SEEK_SET
;
3072 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
3073 /* Note: id is just a kernel address that's not a proc */
3074 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
|F_WAIT
, context
);
3075 return (error
== ENOTSUP
? 0 : error
);
3079 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
3084 lf
.l_whence
= SEEK_SET
;
3087 lf
.l_type
= F_UNLCK
;
3088 /* Note: id is just a kernel address that's not a proc */
3089 error
= VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);
3090 return (error
== ENOTSUP
? 0 : error
);