1/*
2 * Copyright (c) 2004-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
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,
32 * Version 2.0.
33 */
34
35#include <sys/param.h>
36
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>
45#include <sys/stat.h>
46#include <sys/uio.h>
47#include <sys/utfconv.h>
48#include <sys/vnode.h>
49#include <sys/vnode_internal.h>
50#include <sys/xattr.h>
51
52#include <libkern/OSByteOrder.h>
53#include <vm/vm_kern.h>
54
55#if CONFIG_MACF
56#include <security/mac_framework.h>
57#endif
58
59
60#if NAMEDSTREAMS
61
62static int shadow_sequence;
63
64/*
65 * We use %p to prevent loss of precision for pointers on varying architectures.
66 */
67
68#define SHADOW_NAME_FMT		".vfs_rsrc_stream_%p%08x%p"
69#define SHADOW_DIR_FMT		".vfs_rsrc_streams_%p%x"
70#define SHADOW_DIR_CONTAINER "/var/run"
71
72#define MAKE_SHADOW_NAME(VP, NAME)  \
73	snprintf((NAME), sizeof((NAME)), (SHADOW_NAME_FMT), \
74			((void*)(VM_KERNEL_ADDRPERM(VP))), \
75			(VP)->v_id, \
76			((void*)(VM_KERNEL_ADDRPERM((VP)->v_data))))
77
78/* The full path to the shadow directory */
79#define MAKE_SHADOW_DIRNAME(VP, NAME)	\
80	snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_CONTAINER "/" SHADOW_DIR_FMT), \
81			((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
82
83/* The shadow directory as a 'leaf' entry */
84#define MAKE_SHADOW_DIR_LEAF(VP, NAME)	\
85	snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_FMT), \
86			((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
87
88static int  default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context);
89
90static int  default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context);
91
92static int  default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context);
93
94static int  getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, int *creator, vfs_context_t context);
95
96static int  get_shadow_dir(vnode_t *sdvpp);
97
98#endif /* NAMEDSTREAMS */
99
100/*
101 * Default xattr support routines.
102 */
103
104static int default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, int options,
105    vfs_context_t context);
106static int default_setxattr(vnode_t vp, const char *name, uio_t uio, int options,
107    vfs_context_t context);
108static int default_listxattr(vnode_t vp, uio_t uio, size_t *size, int options,
109    vfs_context_t context);
110static int default_removexattr(vnode_t vp, const char *name, int options,
111    vfs_context_t context);
112
113/*
114 *  Retrieve the data of an extended attribute.
115 */
116int
117vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
118            int options, vfs_context_t context)
119{
120	int error;
121
122	if (!XATTR_VNODE_SUPPORTED(vp)) {
123		return (EPERM);
124	}
125#if NAMEDSTREAMS
126	/* getxattr calls are not allowed for streams. */
127	if (vp->v_flag & VISNAMEDSTREAM) {
128		error = EPERM;
129		goto out;
130	}
131#endif
132	/*
133	 * Non-kernel request need extra checks performed.
134	 *
135	 * The XATTR_NOSECURITY flag implies a kernel request.
136	 */
137	if (!(options & XATTR_NOSECURITY)) {
138#if CONFIG_MACF
139		error = mac_vnode_check_getextattr(context, vp, name, uio);
140		if (error)
141			goto out;
142#endif /* MAC */
143		if ((error = xattr_validatename(name))) {
144			goto out;
145		}
146		if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context))) {
147			goto out;
148		}
149		/* The offset can only be non-zero for resource forks. */
150		if (uio != NULL && uio_offset(uio) != 0 &&
151		    bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
152			error = EINVAL;
153			goto out;
154		}
155	}
156
157	/* The offset can only be non-zero for resource forks. */
158	if (uio != NULL && uio_offset(uio) != 0 &&
159	    bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
160		error = EINVAL;
161		goto out;
162	}
163
164	error = VNOP_GETXATTR(vp, name, uio, size, options, context);
165	if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
166		/*
167		 * A filesystem may keep some EAs natively and return ENOTSUP for others.
168		 */
169		error = default_getxattr(vp, name, uio, size, options, context);
170	}
171out:
172	return (error);
173}
174
175/*
176 * Set the data of an extended attribute.
177 */
178int
179vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
180{
181	int error;
182
183	if (!XATTR_VNODE_SUPPORTED(vp)) {
184		return (EPERM);
185	}
186#if NAMEDSTREAMS
187	/* setxattr calls are not allowed for streams. */
188	if (vp->v_flag & VISNAMEDSTREAM) {
189		error = EPERM;
190		goto out;
191	}
192#endif
193	if ((options & (XATTR_REPLACE|XATTR_CREATE)) == (XATTR_REPLACE|XATTR_CREATE)) {
194		return (EINVAL);
195	}
196	if ((error = xattr_validatename(name))) {
197		return (error);
198	}
199 	if (!(options & XATTR_NOSECURITY)) {
200#if CONFIG_MACF
201		error = mac_vnode_check_setextattr(context, vp, name, uio);
202		if (error)
203			goto out;
204#endif /* MAC */
205		error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context);
206		if (error)
207			goto out;
208	}
209	/* The offset can only be non-zero for resource forks. */
210	if (uio_offset(uio) != 0 &&
211	    bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0 ) {
212		error = EINVAL;
213		goto out;
214	}
215
216	error = VNOP_SETXATTR(vp, name, uio, options, context);
217#ifdef DUAL_EAS
218	/*
219	 * An EJUSTRETURN is from a filesystem which keeps this xattr
220	 * natively as well as in a dot-underscore file.  In this case the
221	 * EJUSTRETURN means the filesytem has done nothing, but identifies the
222	 * EA as one which may be represented natively and/or in a DU, and
223	 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
224	 * in vn_setxattr can we do the getxattrs needed to ascertain whether
225	 * the XATTR_{CREATE,REPLACE} should yield an error.
226	 */
227	if (error == EJUSTRETURN) {
228		int native = 0, dufile = 0;
229		size_t sz;	/* not used */
230
231		native = VNOP_GETXATTR(vp, name, NULL, &sz, 0, context) ? 0 : 1;
232		dufile = default_getxattr(vp, name, NULL, &sz, 0, context) ? 0 : 1;
233		if (options & XATTR_CREATE && (native || dufile)) {
234			error = EEXIST;
235			goto out;
236		}
237		if (options & XATTR_REPLACE && !(native || dufile)) {
238			error = ENOATTR;
239			goto out;
240		}
241		/*
242		 * Having determined no CREATE/REPLACE error should result, we
243		 * zero those bits, so both backing stores get written to.
244		 */
245		options &= ~(XATTR_CREATE | XATTR_REPLACE);
246		error = VNOP_SETXATTR(vp, name, uio, options, context);
247		/* the mainline path here is to have error==ENOTSUP ... */
248	}
249#endif /* DUAL_EAS */
250	if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
251		/*
252		 * A filesystem may keep some EAs natively and return ENOTSUP for others.
253		 */
254		error = default_setxattr(vp, name, uio, options, context);
255	}
256#if CONFIG_MACF
257	if ((error == 0) && !(options & XATTR_NOSECURITY) &&
258	    (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL))
259		mac_vnode_label_update_extattr(vnode_mount(vp), vp, name);
260#endif
261out:
262	return (error);
263}
264
265/*
266 * Remove an extended attribute.
267 */
268int
269vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context)
270{
271	int error;
272
273	if (!XATTR_VNODE_SUPPORTED(vp)) {
274		return (EPERM);
275	}
276#if NAMEDSTREAMS
277	/* removexattr calls are not allowed for streams. */
278	if (vp->v_flag & VISNAMEDSTREAM) {
279		error = EPERM;
280		goto out;
281	}
282#endif
283	if ((error = xattr_validatename(name))) {
284		return (error);
285	}
286	if (!(options & XATTR_NOSECURITY)) {
287#if CONFIG_MACF
288		error = mac_vnode_check_deleteextattr(context, vp, name);
289		if (error)
290			goto out;
291#endif /* MAC */
292		error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context);
293		if (error)
294			goto out;
295	}
296	error = VNOP_REMOVEXATTR(vp, name, options, context);
297	if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
298		/*
299		 * A filesystem may keep some EAs natively and return ENOTSUP for others.
300		 */
301		error = default_removexattr(vp, name, options, context);
302#ifdef DUAL_EAS
303	} else if (error == EJUSTRETURN) {
304		/*
305		 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
306		 * as in a dot-underscore file.  EJUSTRETURN means the filesytem did remove
307		 * a native xattr, so failure to find it in a DU file during
308		 * default_removexattr should not be considered an error.
309		 */
310		error = default_removexattr(vp, name, options, context);
311		if (error == ENOATTR)
312			error = 0;
313#endif /* DUAL_EAS */
314	}
315#if CONFIG_MACF
316	if ((error == 0) && !(options & XATTR_NOSECURITY) &&
317	    (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL))
318		mac_vnode_label_update_extattr(vnode_mount(vp), vp, name);
319#endif
320out:
321	return (error);
322}
323
324/*
325 * Retrieve the list of extended attribute names.
326 */
327int
328vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t context)
329{
330	int error;
331
332	if (!XATTR_VNODE_SUPPORTED(vp)) {
333		return (EPERM);
334	}
335#if NAMEDSTREAMS
336	/* listxattr calls are not allowed for streams. */
337	if (vp->v_flag & VISNAMEDSTREAM) {
338		return (EPERM);
339	}
340#endif
341
342	if (!(options & XATTR_NOSECURITY)) {
343#if CONFIG_MACF
344		error = mac_vnode_check_listextattr(context, vp);
345		if (error)
346			goto out;
347#endif /* MAC */
348
349		error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context);
350		if (error)
351			goto out;
352	}
353
354	error = VNOP_LISTXATTR(vp, uio, size, options, context);
355	if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
356		/*
357		 * A filesystem may keep some but not all EAs natively, in which case
358		 * the native EA names will have been uiomove-d out (or *size updated)
359		 * and the default_listxattr here will finish the job.
360		 */
361		error = default_listxattr(vp, uio, size, options, context);
362	}
363out:
364	return (error);
365}
366
367int
368xattr_validatename(const char *name)
369{
370	int namelen;
371
372	if (name == NULL || name[0] == '\0') {
373		return (EINVAL);
374	}
375	namelen = strnlen(name, XATTR_MAXNAMELEN);
376	if (name[namelen] != '\0')
377		return (ENAMETOOLONG);
378
379	if (utf8_validatestr((const unsigned char *)name, namelen) != 0)
380		return (EINVAL);
381
382	return (0);
383}
384
385
386/*
387 * Determine whether an EA is a protected system attribute.
388 */
389int
390xattr_protected(const char *attrname)
391{
392	return(!strncmp(attrname, "com.apple.system.", 17));
393}
394
395
396#if NAMEDSTREAMS
397
398/*
399 * Obtain a named stream from vnode vp.
400 */
401errno_t
402vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, int flags, vfs_context_t context)
403{
404	int error;
405
406	if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS)
407		error = VNOP_GETNAMEDSTREAM(vp, svpp, name, op, flags, context);
408	else
409		error = default_getnamedstream(vp, svpp, name, op, context);
410
411	if (error == 0) {
412		uint32_t streamflags = VISNAMEDSTREAM;
413		vnode_t svp = *svpp;
414
415		if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
416			streamflags |= VISSHADOW;
417		}
418
419		/* Tag the vnode. */
420		vnode_lock_spin(svp);
421		svp->v_flag |= streamflags;
422		vnode_unlock(svp);
423
424		/* Tag the parent so we know to flush credentials for streams on setattr */
425		vnode_lock_spin(vp);
426		vp->v_lflag |= VL_HASSTREAMS;
427		vnode_unlock(vp);
428
429		/* Make the file it's parent.
430		 * Note:  This parent link helps us distinguish vnodes for
431		 * shadow stream files from vnodes for resource fork on file
432		 * systems that support namedstream natively (both have
433		 * VISNAMEDSTREAM set) by allowing access to mount structure
434		 * for checking MNTK_NAMED_STREAMS bit at many places in the
435		 * code.
436		 */
437		vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
438	}
439
440	return (error);
441}
442
443/*
444 * Make a named stream for vnode vp.
445 */
446errno_t
447vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t context)
448{
449	int error;
450
451	if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS)
452		error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context);
453	else
454		error = default_makenamedstream(vp, svpp, name, context);
455
456	if (error == 0) {
457		uint32_t streamflags = VISNAMEDSTREAM;
458		vnode_t svp = *svpp;
459
460		/* Tag the vnode. */
461		if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
462			streamflags |= VISSHADOW;
463		}
464
465		/* Tag the vnode. */
466		vnode_lock_spin(svp);
467		svp->v_flag |= streamflags;
468		vnode_unlock(svp);
469
470		/* Tag the parent so we know to flush credentials for streams on setattr */
471		vnode_lock_spin(vp);
472		vp->v_lflag |= VL_HASSTREAMS;
473		vnode_unlock(vp);
474
475		/* Make the file it's parent.
476		 * Note:  This parent link helps us distinguish vnodes for
477		 * shadow stream files from vnodes for resource fork on file
478		 * systems that support namedstream natively (both have
479		 * VISNAMEDSTREAM set) by allowing access to mount structure
480		 * for checking MNTK_NAMED_STREAMS bit at many places in the
481		 * code.
482		 */
483		vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
484	}
485	return (error);
486}
487
488/*
489 * Remove a named stream from vnode vp.
490 */
491errno_t
492vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t context)
493{
494	int error;
495
496	if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS)
497		error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context);
498	else
499		error = default_removenamedstream(vp, name, context);
500
501	return (error);
502}
503
504#define NS_IOBUFSIZE  (128 * 1024)
505
506/*
507 * Release a named stream shadow file.
508 *
509 * Note: This function is called from two places where we do not need
510 * to check if the vnode has any references held before deleting the
511 * shadow file.  Once from vclean() when the vnode is being reclaimed
512 * and we do not hold any references on the vnode.  Second time from
513 * default_getnamedstream() when we get an error during shadow stream
514 * file initialization so that other processes who are waiting for the
515 * shadow stream file initialization by the creator will get opportunity
516 * to create and initialize the file again.
517 */
518errno_t
519vnode_relenamedstream(vnode_t vp, vnode_t svp) {
520	vnode_t dvp;
521	struct componentname cn;
522	char tmpname[80];
523	errno_t err;
524
525	/*
526	 * We need to use the kernel context here.  If we used the supplied
527	 * VFS context we have no clue whether or not it originated from userland
528	 * where it could be subject to a chroot jail.  We need to ensure that all
529	 * filesystem access to shadow files is done on the same FS regardless of
530	 * userland process restrictions.
531	 */
532	vfs_context_t kernelctx = vfs_context_kernel();
533
534	cache_purge(svp);
535
536	vnode_lock(svp);
537	MAKE_SHADOW_NAME(vp, tmpname);
538	vnode_unlock(svp);
539
540	cn.cn_nameiop = DELETE;
541	cn.cn_flags = ISLASTCN;
542	cn.cn_context = kernelctx;
543	cn.cn_pnbuf = tmpname;
544	cn.cn_pnlen = sizeof(tmpname);
545	cn.cn_nameptr = cn.cn_pnbuf;
546	cn.cn_namelen = strlen(tmpname);
547
548	/*
549	 * Obtain the vnode for the shadow files directory.  Make sure to
550	 * use the kernel ctx as described above.
551	 */
552	err = get_shadow_dir(&dvp);
553	if (err != 0) {
554		return err;
555	}
556
557	(void) VNOP_REMOVE(dvp, svp, &cn, 0, kernelctx);
558	vnode_put(dvp);
559
560	return (0);
561}
562
563/*
564 * Flush a named stream shadow file.
565 *
566 * 'vp' represents the AppleDouble file.
567 * 'svp' represents the shadow file.
568 */
569errno_t
570vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context)
571{
572	struct vnode_attr va;
573	uio_t auio = NULL;
574	caddr_t  bufptr = NULL;
575	size_t  bufsize = 0;
576	size_t  offset;
577	size_t  iosize;
578	size_t datasize;
579	int error;
580	/*
581	 * The kernel context must be used for all I/O to the shadow file
582	 * and its namespace operations
583	 */
584	vfs_context_t kernelctx = vfs_context_kernel();
585
586	/* The supplied context is used for access to the AD file itself */
587
588	VATTR_INIT(&va);
589	VATTR_WANTED(&va, va_data_size);
590	if (VNOP_GETATTR(svp, &va, context) != 0  ||
591		!VATTR_IS_SUPPORTED(&va, va_data_size)) {
592		return (0);
593	}
594	datasize = va.va_data_size;
595	if (datasize == 0) {
596		(void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
597		return (0);
598	}
599
600	iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
601	if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) {
602		return (ENOMEM);
603	}
604	auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
605	offset = 0;
606
607	/*
608	 * Copy the shadow stream file data into the resource fork.
609	 */
610	error = VNOP_OPEN(svp, 0, kernelctx);
611	if (error) {
612		printf("vnode_flushnamedstream: err %d opening file\n", error);
613		goto out;
614	}
615	while (offset < datasize) {
616		iosize = MIN(datasize - offset, iosize);
617
618		uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
619		uio_addiov(auio, (uintptr_t)bufptr, iosize);
620		error = VNOP_READ(svp, auio, 0, kernelctx);
621		if (error) {
622			break;
623		}
624		/* Since there's no truncate xattr we must remove the resource fork. */
625		if (offset == 0) {
626			error = default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
627			if ((error != 0) && (error != ENOATTR)) {
628				break;
629			}
630		}
631		uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
632		uio_addiov(auio, (uintptr_t)bufptr, iosize);
633		error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context);
634		if (error) {
635			break;
636		}
637		offset += iosize;
638	}
639
640	/* close shadowfile */
641	(void) VNOP_CLOSE(svp, 0, kernelctx);
642out:
643	if (bufptr) {
644		kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
645	}
646	if (auio) {
647		uio_free(auio);
648	}
649	return (error);
650}
651
652
653/*
654 * Verify that the vnode 'vp' is a vnode that lives in the shadow
655 * directory.  We can't just query the parent pointer directly since
656 * the shadowfile is hooked up to the actual file it's a stream for.
657 */
658errno_t vnode_verifynamedstream(vnode_t vp) {
659	int error;
660	struct vnode *shadow_dvp = NULL;
661	struct vnode *shadowfile = NULL;
662	struct componentname cn;
663
664	/*
665	 * We need to use the kernel context here.  If we used the supplied
666	 * VFS context we have no clue whether or not it originated from userland
667	 * where it could be subject to a chroot jail.  We need to ensure that all
668	 * filesystem access to shadow files is done on the same FS regardless of
669	 * userland process restrictions.
670	 */
671	vfs_context_t kernelctx = vfs_context_kernel();
672	char tmpname[80];
673
674
675	/* Get the shadow directory vnode */
676	error = get_shadow_dir(&shadow_dvp);
677	if (error) {
678		return error;
679	}
680
681	/* Re-generate the shadow name in the buffer */
682	MAKE_SHADOW_NAME (vp, tmpname);
683
684	/* Look up item in shadow dir */
685	bzero(&cn, sizeof(cn));
686	cn.cn_nameiop = LOOKUP;
687	cn.cn_flags = ISLASTCN | CN_ALLOWRSRCFORK;
688	cn.cn_context = kernelctx;
689	cn.cn_pnbuf = tmpname;
690	cn.cn_pnlen = sizeof(tmpname);
691	cn.cn_nameptr = cn.cn_pnbuf;
692	cn.cn_namelen = strlen(tmpname);
693
694	if (VNOP_LOOKUP (shadow_dvp, &shadowfile, &cn, kernelctx) == 0) {
695		/* is the pointer the same? */
696		if (shadowfile == vp) {
697			error = 0;
698		}
699		else {
700			error = EPERM;
701		}
702		/* drop the iocount acquired */
703		vnode_put (shadowfile);
704	}
705
706	/* Drop iocount on shadow dir */
707	vnode_put (shadow_dvp);
708	return error;
709}
710
711/*
712 * Access or create the shadow file as needed.
713 *
714 * 'makestream' with non-zero value means that we need to guarantee we were the
715 * creator of the shadow file.
716 *
717 * 'context' is the user supplied context for the original VFS operation that
718 * caused us to need a shadow file.
719 *
720 * int pointed to by 'creator' is nonzero if we created the shadowfile.
721 */
722static int
723getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize,
724              int *creator, vfs_context_t context)
725{
726	vnode_t  dvp = NULLVP;
727	vnode_t  svp = NULLVP;
728	struct componentname cn;
729	struct vnode_attr va;
730	char tmpname[80];
731	size_t datasize = 0;
732	int  error = 0;
733	int retries = 0;
734	vfs_context_t kernelctx = vfs_context_kernel();
735
736retry_create:
737	*creator = 0;
738	/* Establish a unique file name. */
739	MAKE_SHADOW_NAME(vp, tmpname);
740	bzero(&cn, sizeof(cn));
741	cn.cn_nameiop = LOOKUP;
742	cn.cn_flags = ISLASTCN;
743	cn.cn_context = context;
744	cn.cn_pnbuf = tmpname;
745	cn.cn_pnlen = sizeof(tmpname);
746	cn.cn_nameptr = cn.cn_pnbuf;
747	cn.cn_namelen = strlen(tmpname);
748
749	/* Pick up uid, gid, mode and date from original file. */
750	VATTR_INIT(&va);
751	VATTR_WANTED(&va, va_uid);
752	VATTR_WANTED(&va, va_gid);
753	VATTR_WANTED(&va, va_mode);
754	VATTR_WANTED(&va, va_create_time);
755	VATTR_WANTED(&va, va_modify_time);
756	if (VNOP_GETATTR(vp, &va, context) != 0  ||
757		!VATTR_IS_SUPPORTED(&va, va_uid)  ||
758		!VATTR_IS_SUPPORTED(&va, va_gid)  ||
759		!VATTR_IS_SUPPORTED(&va, va_mode)) {
760		va.va_uid = KAUTH_UID_NONE;
761		va.va_gid = KAUTH_GID_NONE;
762		va.va_mode = S_IRUSR | S_IWUSR;
763	}
764	va.va_vaflags = VA_EXCLUSIVE;
765	VATTR_SET(&va, va_type, VREG);
766	/* We no longer change the access, but we still hide it. */
767	VATTR_SET(&va, va_flags, UF_HIDDEN);
768
769	/* Obtain the vnode for the shadow files directory. */
770	if (get_shadow_dir(&dvp) != 0) {
771		error = ENOTDIR;
772		goto out;
773	}
774	if (!makestream) {
775		/* See if someone else already has it open. */
776		if (VNOP_LOOKUP(dvp, &svp, &cn, kernelctx) == 0) {
777			/* Double check existence by asking for size. */
778			VATTR_INIT(&va);
779			VATTR_WANTED(&va, va_data_size);
780			if (VNOP_GETATTR(svp, &va, context) == 0  &&
781			    VATTR_IS_SUPPORTED(&va, va_data_size)) {
782				goto out;  /* OK to use. */
783			}
784		}
785
786		/*
787		 * Otherwise make sure the resource fork data exists.
788		 * Use the supplied context for accessing the AD file.
789		 */
790		error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &datasize,
791		                    XATTR_NOSECURITY, context);
792		/*
793		 * To maintain binary compatibility with legacy Carbon
794		 * emulated resource fork support, if the resource fork
795		 * doesn't exist but the Finder Info does,  then act as
796		 * if an empty resource fork is present (see 4724359).
797		 */
798		if ((error == ENOATTR) &&
799		    (vn_getxattr(vp, XATTR_FINDERINFO_NAME, NULL, &datasize,
800		                 XATTR_NOSECURITY, context) == 0)) {
801			datasize = 0;
802			error = 0;
803		} else {
804			if (error) {
805				goto out;
806			}
807
808			/* If the resource fork exists, its size is expected to be non-zero. */
809			if (datasize == 0) {
810				error = ENOATTR;
811				goto out;
812			}
813		}
814	}
815	/* Create the shadow stream file. */
816	error = VNOP_CREATE(dvp, &svp, &cn, &va, kernelctx);
817	if (error == 0) {
818		vnode_recycle(svp);
819		*creator = 1;
820	}
821	else if ((error == EEXIST) && !makestream) {
822		error = VNOP_LOOKUP(dvp, &svp, &cn, kernelctx);
823	}
824	else if ((error == ENOENT) && !makestream) {
825		/*
826		 * We could have raced with a rmdir on the shadow directory
827		 * post-lookup.  Retry from the beginning, 1x only, to
828		 * try and see if we need to re-create the shadow directory
829		 * in get_shadow_dir.
830		 */
831		if (retries == 0) {
832			retries++;
833			if (dvp) {
834				vnode_put (dvp);
835				dvp = NULLVP;
836			}
837			if (svp) {
838				vnode_put (svp);
839				svp = NULLVP;
840			}
841			goto retry_create;
842		}
843		/* Otherwise, just error out normally below */
844	}
845
846out:
847	if (dvp) {
848		vnode_put(dvp);
849	}
850	if (error) {
851		/* On errors, clean up shadow stream file. */
852		if (svp) {
853			vnode_put(svp);
854			svp = NULLVP;
855		}
856	}
857	*svpp = svp;
858	if (rsrcsize) {
859		*rsrcsize = datasize;
860	}
861	return (error);
862}
863
864
865static int
866default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context)
867{
868	vnode_t  svp = NULLVP;
869	uio_t auio = NULL;
870	caddr_t  bufptr = NULL;
871	size_t  bufsize = 0;
872	size_t  datasize = 0;
873	int  creator;
874	int  error;
875
876	/* need the kernel context for accessing the shadowfile */
877	vfs_context_t kernelctx = vfs_context_kernel();
878
879	/*
880	 * Only the "com.apple.ResourceFork" stream is supported here.
881	 */
882	if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
883		*svpp = NULLVP;
884		return (ENOATTR);
885	}
886retry:
887	/*
888	 * Obtain a shadow file for the resource fork I/O.
889	 *
890	 * Need to pass along the supplied context so that getshadowfile
891	 * can access the AD file as needed, using it.
892	 */
893	error = getshadowfile(vp, &svp, 0, &datasize, &creator, context);
894	if (error) {
895		*svpp = NULLVP;
896		return (error);
897	}
898
899	/*
900	 * The creator of the shadow file provides its file data,
901	 * all other threads should wait until its ready.  In order to
902	 * prevent a deadlock during error codepaths, we need to check if the
903	 * vnode is being created, or if it has failed out. Regardless of success or
904	 * failure, we set the VISSHADOW bit on the vnode, so we check that
905	 * if the vnode's flags don't have VISNAMEDSTREAM set.  If it doesn't,
906	 * then we can infer the creator isn't done yet.  If it's there, but
907	 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
908	 * try again.
909	 */
910	if (!creator) {
911		vnode_lock(svp);
912		if (svp->v_flag & VISNAMEDSTREAM) {
913			/* data is ready, go use it */
914			vnode_unlock(svp);
915			goto out;
916		} else {
917			/* It's not ready, wait for it (sleep using v_parent as channel) */
918			if ((svp->v_flag & VISSHADOW)) {
919				/*
920				 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
921				 * thread is done with this vnode. Just unlock the vnode and try again
922				 */
923				vnode_unlock(svp);
924			}
925			else {
926				/* Otherwise, sleep if the shadow file is not created yet */
927				msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP,
928						"getnamedstream", NULL);
929			}
930			vnode_put(svp);
931			svp = NULLVP;
932			goto retry;
933		}
934	}
935
936	/*
937	 * Copy the real resource fork data into shadow stream file.
938	 */
939	if (op == NS_OPEN && datasize != 0) {
940		size_t  offset;
941        	size_t  iosize;
942
943		iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
944		if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) {
945			error = ENOMEM;
946			goto out;
947		}
948
949		auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
950		offset = 0;
951
952		/* open the shadow file */
953		error = VNOP_OPEN(svp, 0, kernelctx);
954		if (error) {
955			goto out;
956		}
957		while (offset < datasize) {
958			size_t	tmpsize;
959
960			iosize = MIN(datasize - offset, iosize);
961
962			uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
963			uio_addiov(auio, (uintptr_t)bufptr, iosize);
964			/* use supplied ctx for AD file */
965			error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize,
966			                    XATTR_NOSECURITY, context);
967			if (error) {
968				break;
969			}
970
971			uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
972			uio_addiov(auio, (uintptr_t)bufptr, iosize);
973			/* kernel context for writing shadowfile */
974			error = VNOP_WRITE(svp, auio, 0, kernelctx);
975			if (error) {
976				break;
977			}
978			offset += iosize;
979		}
980
981		/* close shadow file */
982		(void) VNOP_CLOSE(svp, 0, kernelctx);
983	}
984out:
985	/* Wake up anyone waiting for svp file content */
986	if (creator) {
987		if (error == 0) {
988			vnode_lock(svp);
989			/* VISSHADOW would be set later on anyway, so we set it now */
990			svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
991			wakeup((caddr_t)&svp->v_parent);
992			vnode_unlock(svp);
993		} else {
994			/* On post create errors, get rid of the shadow file.  This
995			 * way if there is another process waiting for initialization
996			 * of the shadowfile by the current process will wake up and
997			 * retry by creating and initializing the shadow file again.
998			 * Also add the VISSHADOW bit here to indicate we're done operating
999			 * on this vnode.
1000			 */
1001			(void)vnode_relenamedstream(vp, svp);
1002			vnode_lock (svp);
1003			svp->v_flag |= VISSHADOW;
1004			wakeup((caddr_t)&svp->v_parent);
1005			vnode_unlock(svp);
1006		}
1007	}
1008
1009	if (bufptr) {
1010		kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
1011	}
1012	if (auio) {
1013		uio_free(auio);
1014	}
1015	if (error) {
1016		/* On errors, clean up shadow stream file. */
1017		if (svp) {
1018			vnode_put(svp);
1019			svp = NULLVP;
1020		}
1021	}
1022	*svpp = svp;
1023	return (error);
1024}
1025
1026static int
1027default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context)
1028{
1029	int creator;
1030	int error;
1031
1032	/*
1033	 * Only the "com.apple.ResourceFork" stream is supported here.
1034	 */
1035	if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
1036		*svpp = NULLVP;
1037		return (ENOATTR);
1038	}
1039
1040	/* Supply the context to getshadowfile so it can manipulate the AD file */
1041	error = getshadowfile(vp, svpp, 1, NULL, &creator, context);
1042
1043	/*
1044	 * Wake up any waiters over in default_getnamedstream().
1045	 */
1046	if ((error == 0) && (*svpp != NULL) && creator) {
1047		vnode_t svp = *svpp;
1048
1049		vnode_lock(svp);
1050		/* If we're the creator, mark it as a named stream */
1051		svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
1052		/* Wakeup any waiters on the v_parent channel */
1053		wakeup((caddr_t)&svp->v_parent);
1054		vnode_unlock(svp);
1055
1056	}
1057
1058	return (error);
1059}
1060
1061static int
1062default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context)
1063{
1064	/*
1065	 * Only the "com.apple.ResourceFork" stream is supported here.
1066	 */
1067	if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
1068		return (ENOATTR);
1069	}
1070	/*
1071	 * XXX - what about other opened instances?
1072	 */
1073	return default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
1074}
1075
1076static int
1077get_shadow_dir(vnode_t *sdvpp) {
1078	vnode_t  dvp = NULLVP;
1079	vnode_t  sdvp = NULLVP;
1080	struct componentname  cn;
1081	struct vnode_attr  va;
1082	char tmpname[80];
1083	uint32_t  tmp_fsid;
1084	int  error;
1085	vfs_context_t kernelctx = vfs_context_kernel();
1086
1087	bzero(tmpname, sizeof(tmpname));
1088	MAKE_SHADOW_DIRNAME(rootvnode, tmpname);
1089	/*
1090	 * Look up the shadow directory to ensure that it still exists.
1091	 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1092	 * in caching it when multiple threads may be trying to manipulate the pointers.
1093	 *
1094	 * Make sure to use the kernel context.  We want a singular view of
1095	 * the shadow dir regardless of chrooted processes.
1096	 */
1097	error = vnode_lookup(tmpname, 0, &sdvp, kernelctx);
1098	if (error == 0) {
1099		/*
1100		 * If we get here, then we have successfully looked up the shadow dir,
1101		 * and it has an iocount from the lookup. Return the vp in the output argument.
1102		 */
1103		*sdvpp = sdvp;
1104		return (0);
1105	}
1106	/* In the failure case, no iocount is acquired */
1107	sdvp = NULLVP;
1108	bzero (tmpname, sizeof(tmpname));
1109
1110	/*
1111	 * Obtain the vnode for "/var/run" directory using the kernel
1112	 * context.
1113	 *
1114	 * This is defined in the SHADOW_DIR_CONTAINER macro
1115	 */
1116	if (vnode_lookup(SHADOW_DIR_CONTAINER, 0, &dvp, kernelctx) != 0) {
1117		error = ENOTSUP;
1118		goto out;
1119	}
1120
1121	/*
1122	 * Create the shadow stream directory.
1123	 * 'dvp' below suggests the parent directory so
1124	 * we only need to provide the leaf entry name
1125	 */
1126	MAKE_SHADOW_DIR_LEAF(rootvnode, tmpname);
1127	bzero(&cn, sizeof(cn));
1128	cn.cn_nameiop = LOOKUP;
1129	cn.cn_flags = ISLASTCN;
1130	cn.cn_context = kernelctx;
1131	cn.cn_pnbuf = tmpname;
1132	cn.cn_pnlen = sizeof(tmpname);
1133	cn.cn_nameptr = cn.cn_pnbuf;
1134	cn.cn_namelen = strlen(tmpname);
1135
1136	/*
1137	 * owned by root, only readable by root, hidden
1138	 */
1139	VATTR_INIT(&va);
1140	VATTR_SET(&va, va_uid, 0);
1141	VATTR_SET(&va, va_gid, 0);
1142	VATTR_SET(&va, va_mode, S_IRUSR | S_IXUSR);
1143	VATTR_SET(&va, va_type, VDIR);
1144	VATTR_SET(&va, va_flags, UF_HIDDEN);
1145	va.va_vaflags = VA_EXCLUSIVE;
1146
1147	error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, kernelctx);
1148
1149	/*
1150	 * There can be only one winner for an exclusive create.
1151	 */
1152	if (error == EEXIST) {
1153		/* loser has to look up directory */
1154		error = VNOP_LOOKUP(dvp, &sdvp, &cn, kernelctx);
1155		if (error == 0) {
1156			/* Make sure its in fact a directory */
1157			if (sdvp->v_type != VDIR) {
1158				goto baddir;
1159			}
1160			/* Obtain the fsid for /var/run directory */
1161			VATTR_INIT(&va);
1162			VATTR_WANTED(&va, va_fsid);
1163			if (VNOP_GETATTR(dvp, &va, kernelctx) != 0  ||
1164			    !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1165				goto baddir;
1166			}
1167			tmp_fsid = va.va_fsid;
1168
1169			VATTR_INIT(&va);
1170			VATTR_WANTED(&va, va_uid);
1171			VATTR_WANTED(&va, va_gid);
1172			VATTR_WANTED(&va, va_mode);
1173			VATTR_WANTED(&va, va_fsid);
1174			VATTR_WANTED(&va, va_dirlinkcount);
1175			VATTR_WANTED(&va, va_acl);
1176			/* Provide defaults for attrs that may not be supported */
1177			va.va_dirlinkcount = 1;
1178			va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
1179
1180			if (VNOP_GETATTR(sdvp, &va, kernelctx) != 0  ||
1181			    !VATTR_IS_SUPPORTED(&va, va_uid)  ||
1182			    !VATTR_IS_SUPPORTED(&va, va_gid)  ||
1183			    !VATTR_IS_SUPPORTED(&va, va_mode)  ||
1184			    !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1185				goto baddir;
1186			}
1187			/*
1188			 * Make sure its what we want:
1189			 * 	- owned by root
1190			 *	- not writable by anyone
1191			 *	- on same file system as /var/run
1192			 *	- not a hard-linked directory
1193			 *	- no ACLs (they might grant write access)
1194			 */
1195			if ((va.va_uid != 0) || (va.va_gid != 0) ||
1196			    (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) ||
1197			    (va.va_fsid != tmp_fsid) ||
1198			    (va.va_dirlinkcount != 1) ||
1199			     (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) {
1200				goto baddir;
1201			}
1202		}
1203	}
1204out:
1205	if (dvp) {
1206		vnode_put(dvp);
1207	}
1208	if (error) {
1209		/* On errors, clean up shadow stream directory. */
1210		if (sdvp) {
1211			vnode_put(sdvp);
1212			sdvp = NULLVP;
1213		}
1214	}
1215	*sdvpp = sdvp;
1216	return (error);
1217
1218baddir:
1219	/* This is not the dir we're looking for, move along */
1220	++shadow_sequence;  /* try something else next time */
1221	error = ENOTDIR;
1222	goto out;
1223}
1224#endif /* NAMEDSTREAMS */
1225
1226
1227#if CONFIG_APPLEDOUBLE
1228/*
1229 * Default Implementation (Non-native EA)
1230 */
1231
1232
1233/*
1234   Typical "._" AppleDouble Header File layout:
1235  ------------------------------------------------------------
1236         MAGIC          0x00051607
1237         VERSION        0x00020000
1238         FILLER         0
1239         COUNT          2
1240     .-- AD ENTRY[0]    Finder Info Entry (must be first)
1241  .--+-- AD ENTRY[1]    Resource Fork Entry (must be last)
1242  |  '-> FINDER INFO
1243  |      /////////////  Fixed Size Data (32 bytes)
1244  |      EXT ATTR HDR
1245  |      /////////////
1246  |      ATTR ENTRY[0] --.
1247  |      ATTR ENTRY[1] --+--.
1248  |      ATTR ENTRY[2] --+--+--.
1249  |         ...          |  |  |
1250  |      ATTR ENTRY[N] --+--+--+--.
1251  |      ATTR DATA 0   <-'  |  |  |
1252  |      ////////////       |  |  |
1253  |      ATTR DATA 1   <----'  |  |
1254  |      /////////////         |  |
1255  |      ATTR DATA 2   <-------'  |
1256  |      /////////////            |
1257  |         ...                   |
1258  |      ATTR DATA N   <----------'
1259  |      /////////////
1260  |                      Attribute Free Space
1261  |
1262  '----> RESOURCE FORK
1263         /////////////   Variable Sized Data
1264         /////////////
1265         /////////////
1266         /////////////
1267         /////////////
1268         /////////////
1269            ...
1270         /////////////
1271
1272  ------------------------------------------------------------
1273
1274   NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1275   stored as part of the Finder Info.  The length in the Finder
1276   Info AppleDouble entry includes the length of the extended
1277   attribute header, attribute entries, and attribute data.
1278*/
1279
1280
1281/*
1282 * On Disk Data Structures
1283 *
1284 * Note: Motorola 68K alignment and big-endian.
1285 *
1286 * See RFC 1740 for additional information about the AppleDouble file format.
1287 *
1288 */
1289
1290#define ADH_MAGIC     0x00051607
1291#define ADH_VERSION   0x00020000
1292#define ADH_MACOSX    "Mac OS X        "
1293
1294/*
1295 * AppleDouble Entry ID's
1296 */
1297#define AD_DATA          1   /* Data fork */
1298#define AD_RESOURCE      2   /* Resource fork */
1299#define AD_REALNAME      3   /* File�s name on home file system */
1300#define AD_COMMENT       4   /* Standard Mac comment */
1301#define AD_ICONBW        5   /* Mac black & white icon */
1302#define AD_ICONCOLOR     6   /* Mac color icon */
1303#define AD_UNUSED        7   /* Not used */
1304#define AD_FILEDATES     8   /* File dates; create, modify, etc */
1305#define AD_FINDERINFO    9   /* Mac Finder info & extended info */
1306#define AD_MACINFO      10   /* Mac file info, attributes, etc */
1307#define AD_PRODOSINFO   11   /* Pro-DOS file info, attrib., etc */
1308#define AD_MSDOSINFO    12   /* MS-DOS file info, attributes, etc */
1309#define AD_AFPNAME      13   /* Short name on AFP server */
1310#define AD_AFPINFO      14   /* AFP file info, attrib., etc */
1311#define AD_AFPDIRID     15   /* AFP directory ID */
1312#define AD_ATTRIBUTES   AD_FINDERINFO
1313
1314
1315#define ATTR_FILE_PREFIX   "._"
1316#define ATTR_HDR_MAGIC     0x41545452   /* 'ATTR' */
1317
1318#define ATTR_BUF_SIZE      4096        /* default size of the attr file and how much we'll grow by */
1319
1320/* Implementation Limits */
1321#define ATTR_MAX_SIZE      AD_XATTR_MAXSIZE
1322#define ATTR_MAX_HDR_SIZE  65536
1323/*
1324 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1325 * size supported (including the attribute entries). All of
1326 * the attribute entries must reside within this limit.  If
1327 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1328 * boundry, then all of the attribute data I/O is performed
1329 * separately from the attribute header I/O.
1330 *
1331 * In particular, all of the attr_entry structures must lie
1332 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1333 * AppleDouble file.  However, the attribute data (i.e. the
1334 * contents of the extended attributes) may extend beyond the
1335 * first ATTR_MAX_HDR_SIZE bytes of the file.  Note that this
1336 * limit is to allow the implementation to optimize by reading
1337 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1338 */
1339
1340
1341#define FINDERINFOSIZE	32
1342
1343typedef struct apple_double_entry {
1344	u_int32_t   type;     /* entry type: see list, 0 invalid */
1345	u_int32_t   offset;   /* entry data offset from the beginning of the file. */
1346 	u_int32_t   length;   /* entry data length in bytes. */
1347} __attribute__((aligned(2), packed)) apple_double_entry_t;
1348
1349
1350typedef struct apple_double_header {
1351	u_int32_t   magic;         /* == ADH_MAGIC */
1352	u_int32_t   version;       /* format version: 2 = 0x00020000 */
1353	u_int32_t   filler[4];
1354	u_int16_t   numEntries;	   /* number of entries which follow */
1355	apple_double_entry_t   entries[2];  /* 'finfo' & 'rsrc' always exist */
1356	u_int8_t    finfo[FINDERINFOSIZE];  /* Must start with Finder Info (32 bytes) */
1357	u_int8_t    pad[2];        /* get better alignment inside attr_header */
1358} __attribute__((aligned(2), packed)) apple_double_header_t;
1359
1360#define ADHDRSIZE  (4+4+16+2)
1361
1362/* Entries are aligned on 4 byte boundaries */
1363typedef struct attr_entry {
1364	u_int32_t   offset;     /* file offset to data */
1365	u_int32_t   length;     /* size of attribute data */
1366	u_int16_t   flags;
1367	u_int8_t    namelen;
1368	u_int8_t    name[1];    /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1369} __attribute__((aligned(2), packed)) attr_entry_t;
1370
1371
1372/* Header + entries must fit into 64K.  Data may extend beyond 64K. */
1373typedef struct attr_header {
1374	apple_double_header_t  appledouble;
1375	u_int32_t   magic;        /* == ATTR_HDR_MAGIC */
1376	u_int32_t   debug_tag;    /* for debugging == file id of owning file */
1377	u_int32_t   total_size;   /* file offset of end of attribute header + entries + data */
1378	u_int32_t   data_start;   /* file offset to attribute data area */
1379	u_int32_t   data_length;  /* length of attribute data area */
1380	u_int32_t   reserved[3];
1381	u_int16_t   flags;
1382	u_int16_t   num_attrs;
1383} __attribute__((aligned(2), packed)) attr_header_t;
1384
1385
1386/* Empty Resource Fork Header */
1387typedef struct rsrcfork_header {
1388	u_int32_t    fh_DataOffset;
1389	u_int32_t    fh_MapOffset;
1390	u_int32_t    fh_DataLength;
1391	u_int32_t    fh_MapLength;
1392	u_int8_t     systemData[112];
1393	u_int8_t     appData[128];
1394	u_int32_t    mh_DataOffset;
1395	u_int32_t    mh_MapOffset;
1396	u_int32_t    mh_DataLength;
1397	u_int32_t    mh_MapLength;
1398	u_int32_t    mh_Next;
1399	u_int16_t    mh_RefNum;
1400	u_int8_t     mh_Attr;
1401	u_int8_t     mh_InMemoryAttr;
1402	u_int16_t    mh_Types;
1403	u_int16_t    mh_Names;
1404	u_int16_t    typeCount;
1405} __attribute__((aligned(2), packed)) rsrcfork_header_t;
1406
1407#define RF_FIRST_RESOURCE    256
1408#define RF_NULL_MAP_LENGTH    30
1409#define RF_EMPTY_TAG  "This resource fork intentionally left blank   "
1410
1411/* Runtime information about the attribute file. */
1412typedef struct attr_info {
1413	vfs_context_t          context;
1414	vnode_t                filevp;
1415	size_t                 filesize;
1416	size_t                 iosize;
1417	u_int8_t               *rawdata;
1418	size_t                 rawsize;  /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1419	apple_double_header_t  *filehdr;
1420	apple_double_entry_t   *finderinfo;
1421	apple_double_entry_t   *rsrcfork;
1422	attr_header_t          *attrhdr;
1423	attr_entry_t           *attr_entry;
1424	u_int8_t               readonly;
1425	u_int8_t               emptyfinderinfo;
1426} attr_info_t;
1427
1428
1429#define ATTR_SETTING  1
1430
1431#define ATTR_ALIGN 3L  /* Use four-byte alignment */
1432
1433#define ATTR_ENTRY_LENGTH(namelen)  \
1434        ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1435
1436#define ATTR_NEXT(ae)  \
1437	 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1438
1439#define ATTR_VALID(ae, ai)  \
1440	((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1441
1442#define SWAP16(x)  OSSwapBigToHostInt16((x))
1443#define SWAP32(x)  OSSwapBigToHostInt32((x))
1444#define SWAP64(x)  OSSwapBigToHostInt64((x))
1445
1446
1447static u_int32_t emptyfinfo[8] = {0};
1448
1449
1450/*
1451 * Local support routines
1452 */
1453static void  close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context);
1454
1455static int  open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context);
1456
1457static int  create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context);
1458
1459static int  remove_xattrfile(vnode_t xvp, vfs_context_t context);
1460
1461static int  get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context);
1462
1463static void  rel_xattrinfo(attr_info_t *ainfop);
1464
1465static int  write_xattrinfo(attr_info_t *ainfop);
1466
1467static void  init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr);
1468
1469static int  lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context);
1470
1471static int  unlock_xattrfile(vnode_t xvp, vfs_context_t context);
1472
1473
1474#if BYTE_ORDER == LITTLE_ENDIAN
1475  static void  swap_adhdr(apple_double_header_t *adh);
1476  static void  swap_attrhdr(attr_header_t *ah, attr_info_t* info);
1477
1478#else
1479#define swap_adhdr(x)
1480#define swap_attrhdr(x, y)
1481#endif
1482
1483static int  check_and_swap_attrhdr(attr_header_t *ah, attr_info_t* ainfop);
1484static int  shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
1485static int  shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
1486
1487
1488/*
1489 * Sanity check and swap the header of an AppleDouble file.  Assumes the buffer
1490 * is in big endian (as it would exist on disk).  Verifies the following:
1491 * - magic field
1492 * - version field
1493 * - number of entries
1494 * - that each entry fits within the file size
1495 *
1496 * If the header is invalid, ENOATTR is returned.
1497 *
1498 * NOTE: Does not attempt to validate the extended attributes header that
1499 * may be embedded in the Finder Info entry.
1500 */
1501static int check_and_swap_apple_double_header(attr_info_t *ainfop)
1502{
1503	int i, j;
1504	u_int32_t header_end;
1505	u_int32_t entry_end;
1506	size_t rawsize;
1507	apple_double_header_t *header;
1508
1509	rawsize = ainfop->rawsize;
1510	header = (apple_double_header_t *) ainfop->rawdata;
1511
1512	/* Is the file big enough to contain an AppleDouble header? */
1513	if (rawsize < offsetof(apple_double_header_t, entries))
1514		return ENOATTR;
1515
1516	/* Swap the AppleDouble header fields to native order */
1517	header->magic = SWAP32(header->magic);
1518	header->version = SWAP32(header->version);
1519	header->numEntries = SWAP16(header->numEntries);
1520
1521	/* Sanity check the AppleDouble header fields */
1522	if (header->magic != ADH_MAGIC ||
1523	    header->version != ADH_VERSION ||
1524	    header->numEntries < 1 ||
1525	    header->numEntries > 15) {
1526		return ENOATTR;
1527	}
1528
1529	/* Calculate where the entries[] array ends */
1530	header_end = offsetof(apple_double_header_t, entries) +
1531		header->numEntries * sizeof(apple_double_entry_t);
1532
1533	/* Is the file big enough to contain the AppleDouble entries? */
1534	if (rawsize < header_end) {
1535	    	return ENOATTR;
1536	}
1537
1538	/* Swap and sanity check each AppleDouble entry */
1539	for (i=0; i<header->numEntries; i++) {
1540		/* Swap the per-entry fields to native order */
1541		header->entries[i].type   = SWAP32(header->entries[i].type);
1542		header->entries[i].offset = SWAP32(header->entries[i].offset);
1543		header->entries[i].length = SWAP32(header->entries[i].length);
1544
1545		entry_end = header->entries[i].offset + header->entries[i].length;
1546
1547		/*
1548		 * Does the entry's content start within the header itself,
1549		 * did the addition overflow, or does the entry's content
1550		 * extend past the end of the file?
1551		 */
1552		if (header->entries[i].offset < header_end ||
1553		    entry_end < header->entries[i].offset  ||
1554		    entry_end > ainfop->filesize) {
1555			return ENOATTR;
1556		}
1557
1558		/*
1559		 * Does the current entry's content overlap with a previous
1560		 * entry's content?
1561		 *
1562		 * Yes, this is O(N**2), and there are more efficient algorithms
1563		 * for testing pairwise overlap of N ranges when N is large.
1564		 * But we have already ensured N < 16, and N is almost always 2.
1565		 * So there's no point in using a more complex algorithm.
1566		 */
1567
1568		for (j=0; j<i; j++) {
1569			if (entry_end > header->entries[j].offset &&
1570			    header->entries[j].offset + header->entries[j].length > header->entries[i].offset) {
1571				return ENOATTR;
1572			}
1573		}
1574	}
1575
1576	return 0;
1577}
1578
1579
1580
1581/*
1582 * Retrieve the data of an extended attribute.
1583 */
1584static int
1585default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
1586                 __unused int options, vfs_context_t context)
1587{
1588	vnode_t xvp = NULL;
1589	attr_info_t ainfo;
1590	attr_header_t *header;
1591	attr_entry_t *entry;
1592	u_int8_t *attrdata;
1593	size_t datalen;
1594	int namelen;
1595	int isrsrcfork;
1596	int fileflags;
1597	int i;
1598	int error;
1599
1600	fileflags = FREAD;
1601	if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
1602		isrsrcfork = 1;
1603		/*
1604		 * Open the file locked (shared) since the Carbon
1605		 * File Manager may have the Apple Double file open
1606		 * and could be changing the resource fork.
1607		 */
1608		fileflags |= O_SHLOCK;
1609	} else {
1610		isrsrcfork = 0;
1611	}
1612
1613	if ((error = open_xattrfile(vp, fileflags, &xvp, context))) {
1614		return (error);
1615	}
1616	if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
1617		close_xattrfile(xvp, fileflags, context);
1618		return (error);
1619	}
1620
1621	/* Get the Finder Info. */
1622	if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1623
1624		if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
1625			error = ENOATTR;
1626		} else if (uio == NULL) {
1627			*size = FINDERINFOSIZE;
1628			error = 0;
1629		} else if (uio_offset(uio) != 0) {
1630			error = EINVAL;
1631		} else if (uio_resid(uio) < FINDERINFOSIZE) {
1632			error = ERANGE;
1633		} else {
1634			attrdata = (u_int8_t*)ainfo.filehdr + ainfo.finderinfo->offset;
1635			error = uiomove((caddr_t)attrdata, FINDERINFOSIZE, uio);
1636		}
1637		goto out;
1638	}
1639
1640	/* Read the Resource Fork. */
1641	if (isrsrcfork) {
1642		if (!vnode_isreg(vp)) {
1643			error = EPERM;
1644		} else if (ainfo.rsrcfork == NULL) {
1645			error = ENOATTR;
1646		} else if (uio == NULL) {
1647			*size = (size_t)ainfo.rsrcfork->length;
1648		} else {
1649			uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
1650			error = VNOP_READ(xvp, uio, 0, context);
1651			if (error == 0)
1652				uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
1653		}
1654		goto out;
1655	}
1656
1657	if (ainfo.attrhdr == NULL || ainfo.attr_entry == NULL) {
1658		error = ENOATTR;
1659		goto out;
1660	}
1661	if (uio_offset(uio) != 0) {
1662		error = EINVAL;
1663		goto out;
1664	}
1665	error = ENOATTR;
1666	namelen = strlen(name) + 1;
1667	header = ainfo.attrhdr;
1668	entry = ainfo.attr_entry;
1669	/*
1670	 * Search for attribute name in the header.
1671	 */
1672	for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
1673		if (strncmp((const char *)entry->name, name, namelen) == 0) {
1674			datalen = (size_t)entry->length;
1675			if (uio == NULL) {
1676				*size = datalen;
1677				error = 0;
1678				break;
1679			}
1680			if (uio_resid(uio) < (user_ssize_t)datalen) {
1681				error = ERANGE;
1682				break;
1683			}
1684			if (entry->offset + datalen < ATTR_MAX_HDR_SIZE) {
1685				attrdata = ((u_int8_t *)header + entry->offset);
1686				error = uiomove((caddr_t)attrdata, datalen, uio);
1687			} else {
1688				uio_setoffset(uio, entry->offset);
1689				error = VNOP_READ(xvp, uio, 0, context);
1690				uio_setoffset(uio, 0);
1691			}
1692			break;
1693		}
1694		entry = ATTR_NEXT(entry);
1695	}
1696out:
1697	rel_xattrinfo(&ainfo);
1698	close_xattrfile(xvp, fileflags, context);
1699
1700	return (error);
1701}
1702
1703/*
1704 * Set the data of an extended attribute.
1705 */
1706static int
1707default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
1708{
1709	vnode_t xvp = NULL;
1710	attr_info_t ainfo;
1711	attr_header_t *header;
1712	attr_entry_t *entry;
1713	attr_entry_t *lastentry;
1714	u_int8_t *attrdata;
1715	size_t datalen;
1716	size_t entrylen;
1717	size_t datafreespace;
1718	int namelen;
1719	int found = 0;
1720	int i;
1721	int splitdata;
1722	int fileflags;
1723	int error;
1724	char finfo[FINDERINFOSIZE];
1725
1726	datalen = uio_resid(uio);
1727	namelen = strlen(name) + 1;
1728	entrylen = ATTR_ENTRY_LENGTH(namelen);
1729
1730	/*
1731	 * By convention, Finder Info that is all zeroes is equivalent to not
1732	 * having a Finder Info EA.  So if we're trying to set the Finder Info
1733	 * to all zeroes, then delete it instead.  If a file didn't have an
1734	 * AppleDouble file before, this prevents creating an AppleDouble file
1735	 * with no useful content.
1736	 *
1737	 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1738	 * for all zeroes Finder Info before opening the AppleDouble file.
1739	 * But if either of those options were specified, we need to open the
1740	 * AppleDouble file to see whether there was already Finder Info (so we
1741	 * can return an error if needed); this case is handled further below.
1742	 *
1743	 * NOTE: this copies the Finder Info data into the "finfo" local.
1744	 */
1745	if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1746		/*
1747		 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1748		 * That means we probably have to open_xattrfile and get_xattrinfo.
1749		 */
1750		if (uio_offset(uio) != 0 || datalen != FINDERINFOSIZE) {
1751			return EINVAL;
1752		}
1753		error = uiomove(finfo, datalen, uio);
1754		if (error)
1755			return error;
1756		if ((options & (XATTR_CREATE|XATTR_REPLACE)) == 0 &&
1757		    bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1758			error = default_removexattr(vp, name, 0, context);
1759			if (error == ENOATTR)
1760				error = 0;
1761			return error;
1762		}
1763	}
1764
1765start:
1766	/*
1767	 * Open the file locked since setting an attribute
1768	 * can change the layout of the Apple Double file.
1769	 */
1770	fileflags = FREAD | FWRITE | O_EXLOCK;
1771	if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xvp, context))) {
1772		return (error);
1773	}
1774	if ((error = get_xattrinfo(xvp, ATTR_SETTING, &ainfo, context))) {
1775		close_xattrfile(xvp, fileflags, context);
1776		return (error);
1777	}
1778
1779	/* Set the Finder Info. */
1780	if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1781		if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
1782			/* attr exists and "create" was specified? */
1783			if (options & XATTR_CREATE) {
1784				error = EEXIST;
1785				goto out;
1786			}
1787		} else {
1788			/* attr doesn't exists and "replace" was specified? */
1789			if (options & XATTR_REPLACE) {
1790				error = ENOATTR;
1791				goto out;
1792			}
1793		}
1794		if (options != 0 && bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1795			/*
1796			 * Setting the Finder Info to all zeroes is equivalent to
1797			 * removing it.  Close the xattr file and let
1798			 * default_removexattr do the work (including deleting
1799			 * the xattr file if there are no other xattrs).
1800			 *
1801			 * Note that we have to handle the case where the
1802			 * Finder Info was already all zeroes, and we ignore
1803			 * ENOATTR.
1804			 *
1805			 * The common case where options == 0 was handled above.
1806			 */
1807			rel_xattrinfo(&ainfo);
1808			close_xattrfile(xvp, fileflags, context);
1809			error = default_removexattr(vp, name, 0, context);
1810			if (error == ENOATTR)
1811				error = 0;
1812			return error;
1813		}
1814		if (ainfo.finderinfo) {
1815			attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
1816			bcopy(finfo, attrdata, datalen);
1817			ainfo.iosize = sizeof(attr_header_t);
1818			error = write_xattrinfo(&ainfo);
1819			goto out;
1820		}
1821		error = ENOATTR;
1822		goto out;
1823	}
1824
1825	/* Write the Resource Fork. */
1826	if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
1827		u_int32_t endoffset;
1828
1829		if (!vnode_isreg(vp)) {
1830			error = EPERM;
1831			goto out;
1832		}
1833		/* Make sure we have a rsrc fork pointer.. */
1834		if (ainfo.rsrcfork == NULL) {
1835			error = ENOATTR;
1836			goto out;
1837		}
1838		if (ainfo.rsrcfork) {
1839			if (ainfo.rsrcfork->length != 0) {
1840				if (options & XATTR_CREATE) {
1841					/* attr exists, and create specified ? */
1842					error = EEXIST;
1843					goto out;
1844				}
1845			}
1846			else {
1847				/* Zero length AD rsrc fork */
1848				if (options & XATTR_REPLACE) {
1849					/* attr doesn't exist (0-length), but replace specified ? */
1850					error = ENOATTR;
1851					goto out;
1852				}
1853			}
1854		}
1855		else {
1856			/* We can't do much if we somehow didn't get an AD rsrc pointer */
1857			error = ENOATTR;
1858			goto out;
1859		}
1860
1861		endoffset = uio_resid(uio) + uio_offset(uio); /* new size */
1862		uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
1863		error = VNOP_WRITE(xvp, uio, 0, context);
1864		if (error)
1865			goto out;
1866		uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
1867		if (endoffset > ainfo.rsrcfork->length) {
1868			ainfo.rsrcfork->length = endoffset;
1869			ainfo.iosize = sizeof(attr_header_t);
1870			error = write_xattrinfo(&ainfo);
1871			goto out;
1872		}
1873		goto out;
1874	}
1875
1876	if (datalen > ATTR_MAX_SIZE) {
1877		return (E2BIG);  /* EINVAL instead ? */
1878	}
1879
1880	if (ainfo.attrhdr == NULL) {
1881		error = ENOATTR;
1882		goto out;
1883	}
1884	header = ainfo.attrhdr;
1885	entry = ainfo.attr_entry;
1886
1887	/* Check if data area crosses the maximum header size. */
1888	if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE)
1889		splitdata = 1;  /* do data I/O separately */
1890	else
1891		splitdata = 0;
1892
1893	/*
1894	 * See if attribute already exists.
1895	 */
1896	for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
1897		if (strncmp((const char *)entry->name, name, namelen) == 0) {
1898			found = 1;
1899			break;
1900		}
1901		entry = ATTR_NEXT(entry);
1902	}
1903
1904	if (found) {
1905		if (options & XATTR_CREATE) {
1906			error = EEXIST;
1907			goto out;
1908		}
1909		if (datalen == entry->length) {
1910			if (splitdata) {
1911				uio_setoffset(uio, entry->offset);
1912				error = VNOP_WRITE(xvp, uio, 0, context);
1913				uio_setoffset(uio, 0);
1914				if (error) {
1915					printf("setxattr: VNOP_WRITE error %d\n", error);
1916				}
1917			} else {
1918				attrdata = (u_int8_t *)header + entry->offset;
1919				error = uiomove((caddr_t)attrdata, datalen, uio);
1920				if (error)
1921					goto out;
1922				ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
1923				error = write_xattrinfo(&ainfo);
1924				if (error) {
1925					printf("setxattr: write_xattrinfo error %d\n", error);
1926				}
1927			}
1928			goto out;
1929		} else {
1930			/*
1931			 * Brute force approach - just remove old entry and set new entry.
1932			 */
1933			found = 0;
1934			rel_xattrinfo(&ainfo);
1935			close_xattrfile(xvp, fileflags, context);
1936			error = default_removexattr(vp, name, options, context);
1937			if (error) {
1938				return (error);
1939			}
1940			/* Clear XATTR_REPLACE option since we just removed the attribute. */
1941			options &= ~XATTR_REPLACE;
1942			goto start; /* start over */
1943		}
1944
1945	}
1946
1947	if (options & XATTR_REPLACE) {
1948		error = ENOATTR;  /* nothing there to replace */
1949		goto out;
1950	}
1951	/* Check if header size limit has been reached. */
1952	if ((header->data_start + entrylen) > ATTR_MAX_HDR_SIZE) {
1953		error = ENOSPC;
1954		goto out;
1955	}
1956
1957	datafreespace = header->total_size - (header->data_start + header->data_length);
1958
1959	/* Check if we need more space. */
1960	if ((datalen + entrylen) > datafreespace) {
1961		size_t growsize;
1962
1963		growsize = roundup((datalen + entrylen) - datafreespace, ATTR_BUF_SIZE);
1964
1965		/* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1966		if (!splitdata && (header->total_size + growsize) > ATTR_MAX_HDR_SIZE) {
1967			growsize = ATTR_MAX_HDR_SIZE - header->total_size;
1968		}
1969
1970		ainfo.filesize += growsize;
1971		error = vnode_setsize(xvp, ainfo.filesize, 0, context);
1972		if (error) {
1973			printf("setxattr: VNOP_TRUNCATE error %d\n", error);
1974		}
1975		if (error)
1976			goto out;
1977
1978		/*
1979		 * Move the resource fork out of the way.
1980		 */
1981		if (ainfo.rsrcfork) {
1982			if (ainfo.rsrcfork->length != 0) {
1983				shift_data_down(xvp,
1984						ainfo.rsrcfork->offset,
1985						ainfo.rsrcfork->length,
1986						growsize, context);
1987			}
1988			ainfo.rsrcfork->offset += growsize;
1989		}
1990		ainfo.finderinfo->length += growsize;
1991		header->total_size += growsize;
1992	}
1993
1994	/* Make space for a new entry. */
1995	if (splitdata) {
1996		shift_data_down(xvp,
1997				header->data_start,
1998				header->data_length,
1999				entrylen, context);
2000	} else {
2001		bcopy((u_int8_t *)header + header->data_start,
2002		      (u_int8_t *)header + header->data_start + entrylen,
2003		      header->data_length);
2004	}
2005	header->data_start += entrylen;
2006
2007	/* Fix up entry data offsets. */
2008	lastentry = entry;
2009	for (entry = ainfo.attr_entry; entry != lastentry && ATTR_VALID(entry, ainfo); entry = ATTR_NEXT(entry)) {
2010		entry->offset += entrylen;
2011	}
2012
2013	/*
2014	 * If the attribute data area is entirely within
2015	 * the header buffer, then just update the buffer,
2016	 * otherwise we'll write it separately to the file.
2017	 */
2018	if (splitdata) {
2019		off_t offset;
2020
2021		/* Write new attribute data after the end of existing data. */
2022		offset = header->data_start + header->data_length;
2023		uio_setoffset(uio, offset);
2024		error = VNOP_WRITE(xvp, uio, 0, context);
2025		uio_setoffset(uio, 0);
2026		if (error) {
2027			printf("setxattr: VNOP_WRITE error %d\n", error);
2028			goto out;
2029		}
2030	} else {
2031		attrdata = (u_int8_t *)header + header->data_start + header->data_length;
2032
2033		error = uiomove((caddr_t)attrdata, datalen, uio);
2034		if (error) {
2035			printf("setxattr: uiomove error %d\n", error);
2036			goto out;
2037		}
2038	}
2039
2040	/* Create the attribute entry. */
2041	lastentry->length = datalen;
2042	lastentry->offset = header->data_start + header->data_length;
2043	lastentry->namelen = namelen;
2044	lastentry->flags = 0;
2045	bcopy(name, &lastentry->name[0], namelen);
2046
2047	/* Update the attributes header. */
2048	header->num_attrs++;
2049	header->data_length += datalen;
2050
2051	if (splitdata) {
2052		/* Only write the entries, since the data was written separately. */
2053		ainfo.iosize = ainfo.attrhdr->data_start;
2054	} else {
2055		 /* The entry and data are both in the header; write them together. */
2056		ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2057	}
2058	error = write_xattrinfo(&ainfo);
2059	if (error) {
2060		printf("setxattr: write_xattrinfo error %d\n", error);
2061	}
2062
2063out:
2064	rel_xattrinfo(&ainfo);
2065	close_xattrfile(xvp, fileflags, context);
2066
2067	/* Touch the change time if we changed an attribute. */
2068	if (error == 0) {
2069		struct vnode_attr va;
2070
2071		/* Re-write the mtime to cause a ctime change. */
2072		VATTR_INIT(&va);
2073		VATTR_WANTED(&va, va_modify_time);
2074		if (vnode_getattr(vp, &va, context) == 0) {
2075			VATTR_INIT(&va);
2076			VATTR_SET(&va, va_modify_time, va.va_modify_time);
2077			(void) vnode_setattr(vp, &va, context);
2078		}
2079	}
2080
2081	post_event_if_success(vp, error, NOTE_ATTRIB);
2082
2083	return (error);
2084}
2085
2086
2087/*
2088 * Remove an extended attribute.
2089 */
2090static int
2091default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_context_t context)
2092{
2093	vnode_t xvp = NULL;
2094	attr_info_t ainfo;
2095	attr_header_t *header;
2096	attr_entry_t *entry;
2097	attr_entry_t *oldslot;
2098	u_int8_t *attrdata;
2099	u_int32_t dataoff;
2100	size_t datalen;
2101	size_t entrylen;
2102	int namelen;
2103	int found = 0, lastone = 0;
2104	int i;
2105	int splitdata;
2106	int attrcount = 0;
2107	int isrsrcfork;
2108	int fileflags;
2109	int error;
2110
2111	fileflags = FREAD | FWRITE;
2112	if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
2113		isrsrcfork = 1;
2114		/*
2115		 * Open the file locked (exclusive) since the Carbon
2116		 * File Manager may have the Apple Double file open
2117		 * and could be changing the resource fork.
2118		 */
2119		fileflags |= O_EXLOCK;
2120	} else {
2121		isrsrcfork = 0;
2122	}
2123
2124	if ((error = open_xattrfile(vp, fileflags, &xvp, context))) {
2125		return (error);
2126	}
2127	if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
2128		close_xattrfile(xvp, fileflags, context);
2129		return (error);
2130	}
2131	if (ainfo.attrhdr)
2132		attrcount += ainfo.attrhdr->num_attrs;
2133	if (ainfo.rsrcfork)
2134		++attrcount;
2135	if (ainfo.finderinfo && !ainfo.emptyfinderinfo)
2136		++attrcount;
2137
2138	/* Clear the Finder Info. */
2139	if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
2140		if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
2141			error = ENOATTR;
2142			goto out;
2143		}
2144		/* On removal of last attribute the ._ file is removed. */
2145		if (--attrcount == 0)
2146			goto out;
2147		attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
2148		bzero((caddr_t)attrdata, FINDERINFOSIZE);
2149		ainfo.iosize = sizeof(attr_header_t);
2150		error = write_xattrinfo(&ainfo);
2151		goto out;
2152	}
2153
2154	/* Clear the Resource Fork. */
2155	if (isrsrcfork) {
2156		if (!vnode_isreg(vp)) {
2157			error = EPERM;
2158			goto out;
2159		}
2160		if (ainfo.rsrcfork == NULL || ainfo.rsrcfork->length == 0) {
2161			error = ENOATTR;
2162			goto out;
2163		}
2164		/* On removal of last attribute the ._ file is removed. */
2165		if (--attrcount == 0)
2166			goto out;
2167		/*
2168		 * XXX
2169		 * If the resource fork isn't the last AppleDouble
2170		 * entry then the space needs to be reclaimed by
2171		 * shifting the entries after the resource fork.
2172		 */
2173		if ((ainfo.rsrcfork->offset + ainfo.rsrcfork->length) == ainfo.filesize) {
2174			ainfo.filesize -= ainfo.rsrcfork->length;
2175			error = vnode_setsize(xvp, ainfo.filesize, 0, context);
2176		}
2177		if (error == 0) {
2178			ainfo.rsrcfork->length = 0;
2179			ainfo.iosize = sizeof(attr_header_t);
2180			error = write_xattrinfo(&ainfo);
2181		}
2182		goto out;
2183	}
2184
2185	if (ainfo.attrhdr == NULL) {
2186		error = ENOATTR;
2187		goto out;
2188	}
2189	namelen = strlen(name) + 1;
2190	header = ainfo.attrhdr;
2191	entry = ainfo.attr_entry;
2192
2193	/*
2194	 * See if this attribute exists.
2195	 */
2196	for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2197		if (strncmp((const char *)entry->name, name, namelen) == 0) {
2198			found = 1;
2199			if ((i+1) == header->num_attrs)
2200				lastone = 1;
2201			break;
2202		}
2203		entry = ATTR_NEXT(entry);
2204	}
2205	if (!found) {
2206		error = ENOATTR;
2207		goto out;
2208	}
2209	/* On removal of last attribute the ._ file is removed. */
2210	if (--attrcount == 0)
2211		goto out;
2212
2213	datalen = entry->length;
2214	dataoff = entry->offset;
2215	entrylen = ATTR_ENTRY_LENGTH(namelen);
2216	if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE)
2217		splitdata = 1;
2218	else
2219		splitdata = 0;
2220
2221	/* Remove the attribute entry. */
2222	if (!lastone) {
2223		bcopy((u_int8_t *)entry + entrylen, (u_int8_t *)entry,
2224		      ((size_t)header + header->data_start) - ((size_t)entry + entrylen));
2225	}
2226
2227	/* Adjust the attribute data. */
2228	if (splitdata) {
2229		shift_data_up(xvp,
2230		              header->data_start,
2231		              dataoff - header->data_start,
2232		              entrylen,
2233		              context);
2234		if (!lastone) {
2235			shift_data_up(xvp,
2236			              dataoff + datalen,
2237			              (header->data_start + header->data_length) - (dataoff + datalen),
2238			              datalen + entrylen,
2239			              context);
2240		}
2241		/* XXX write zeros to freed space ? */
2242		ainfo.iosize = ainfo.attrhdr->data_start - entrylen;
2243	} else {
2244
2245
2246		bcopy((u_int8_t *)header + header->data_start,
2247		      (u_int8_t *)header + header->data_start - entrylen,
2248		      dataoff - header->data_start);
2249		if (!lastone) {
2250			bcopy((u_int8_t *)header + dataoff + datalen,
2251			      (u_int8_t *)header + dataoff - entrylen,
2252			      (header->data_start + header->data_length) - (dataoff + datalen));
2253		}
2254		bzero (((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen));
2255		ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2256	}
2257
2258	/* Adjust the header values and entry offsets. */
2259	header->num_attrs--;
2260	header->data_start -= entrylen;
2261	header->data_length -= datalen;
2262
2263	oldslot = entry;
2264	entry = ainfo.attr_entry;
2265	for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2266		entry->offset -= entrylen;
2267		if (entry >= oldslot)
2268			entry->offset -= datalen;
2269		entry = ATTR_NEXT(entry);
2270	}
2271	error = write_xattrinfo(&ainfo);
2272	if (error) {
2273		printf("removexattr: write_xattrinfo error %d\n", error);
2274	}
2275out:
2276	rel_xattrinfo(&ainfo);
2277
2278	/* When there are no more attributes remove the ._ file. */
2279	if (attrcount == 0) {
2280		if (fileflags & O_EXLOCK)
2281			(void) unlock_xattrfile(xvp, context);
2282		VNOP_CLOSE(xvp, fileflags, context);
2283		vnode_rele(xvp);
2284		error = remove_xattrfile(xvp, context);
2285		vnode_put(xvp);
2286	} else {
2287		close_xattrfile(xvp, fileflags, context);
2288	}
2289	/* Touch the change time if we changed an attribute. */
2290	if (error == 0) {
2291		struct vnode_attr va;
2292
2293		/* Re-write the mtime to cause a ctime change. */
2294		VATTR_INIT(&va);
2295		VATTR_WANTED(&va, va_modify_time);
2296		if (vnode_getattr(vp, &va, context) == 0) {
2297			VATTR_INIT(&va);
2298			VATTR_SET(&va, va_modify_time, va.va_modify_time);
2299			(void) vnode_setattr(vp, &va, context);
2300		}
2301	}
2302
2303	post_event_if_success(vp, error, NOTE_ATTRIB);
2304
2305	return (error);
2306
2307}
2308
2309
2310/*
2311 * Retrieve the list of extended attribute names.
2312 */
2313static int
2314default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs_context_t context)
2315{
2316	vnode_t xvp = NULL;
2317	attr_info_t ainfo;
2318	attr_entry_t *entry;
2319	int i, count;
2320	int error;
2321
2322	/*
2323	 * We do not zero "*size" here as we don't want to stomp a size set when
2324	 * VNOP_LISTXATTR processed any native EAs.  That size is initially zeroed by the
2325	 * system call layer, up in listxattr or flistxattr.
2326	 */
2327
2328	if ((error = open_xattrfile(vp, FREAD, &xvp, context))) {
2329		if (error == ENOATTR)
2330			error = 0;
2331		return (error);
2332	}
2333	if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
2334		if (error == ENOATTR)
2335			error = 0;
2336		close_xattrfile(xvp, FREAD, context);
2337		return (error);
2338	}
2339
2340	/* Check for Finder Info. */
2341	if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
2342		if (uio == NULL) {
2343			*size += sizeof(XATTR_FINDERINFO_NAME);
2344		} else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_FINDERINFO_NAME)) {
2345			error = ERANGE;
2346			goto out;
2347		} else {
2348			error = uiomove(XATTR_FINDERINFO_NAME,
2349			                sizeof(XATTR_FINDERINFO_NAME), uio);
2350			if (error) {
2351				error = ERANGE;
2352				goto out;
2353			}
2354		}
2355	}
2356
2357	/* Check for Resource Fork. */
2358	if (vnode_isreg(vp) && ainfo.rsrcfork) {
2359		if (uio == NULL) {
2360			*size += sizeof(XATTR_RESOURCEFORK_NAME);
2361		} else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_RESOURCEFORK_NAME)) {
2362			error = ERANGE;
2363			goto out;
2364		} else {
2365			error = uiomove(XATTR_RESOURCEFORK_NAME,
2366			                sizeof(XATTR_RESOURCEFORK_NAME), uio);
2367			if (error) {
2368				error = ERANGE;
2369				goto out;
2370			}
2371		}
2372	}
2373
2374	/* Check for attributes. */
2375	if (ainfo.attrhdr) {
2376		count = ainfo.attrhdr->num_attrs;
2377		for (i = 0, entry = ainfo.attr_entry; i < count && ATTR_VALID(entry, ainfo); i++) {
2378			if (xattr_protected((const char *)entry->name) ||
2379			    xattr_validatename((const char *)entry->name) != 0) {
2380				entry = ATTR_NEXT(entry);
2381				continue;
2382			}
2383			if (uio == NULL) {
2384				*size += entry->namelen;
2385				entry = ATTR_NEXT(entry);
2386				continue;
2387			}
2388			if (uio_resid(uio) < entry->namelen) {
2389				error = ERANGE;
2390				break;
2391			}
2392			error = uiomove((caddr_t) entry->name, entry->namelen, uio);
2393			if (error) {
2394				if (error != EFAULT)
2395					error = ERANGE;
2396				break;
2397			}
2398			entry = ATTR_NEXT(entry);
2399		}
2400	}
2401out:
2402	rel_xattrinfo(&ainfo);
2403	close_xattrfile(xvp, FREAD, context);
2404
2405	return (error);
2406}
2407
2408static int
2409open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context)
2410{
2411	vnode_t xvp = NULLVP;
2412	vnode_t dvp = NULLVP;
2413	struct vnode_attr va;
2414	struct nameidata nd;
2415	char smallname[64];
2416	char *filename = NULL;
2417	const char *basename = NULL;
2418	size_t len;
2419	errno_t error;
2420	int opened = 0;
2421	int referenced = 0;
2422
2423	if (vnode_isvroot(vp) && vnode_isdir(vp)) {
2424		/*
2425		 * For the root directory use "._." to hold the attributes.
2426		 */
2427		filename = &smallname[0];
2428		snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, ".");
2429		dvp = vp;  /* the "._." file resides in the root dir */
2430		goto lookup;
2431	}
2432	if ( (dvp = vnode_getparent(vp)) == NULLVP) {
2433		error = ENOATTR;
2434		goto out;
2435	}
2436	if ( (basename = vnode_getname(vp)) == NULL) {
2437		error = ENOATTR;
2438		goto out;
2439	}
2440
2441	/* "._" Attribute files cannot have attributes */
2442	if (vp->v_type == VREG && strlen(basename) > 2 &&
2443	    basename[0] == '.' && basename[1] == '_') {
2444		error = EPERM;
2445		goto out;
2446	}
2447	filename = &smallname[0];
2448	len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename);
2449	if (len >= sizeof(smallname)) {
2450		len++;  /* snprintf result doesn't include '\0' */
2451		MALLOC(filename, char *, len, M_TEMP, M_WAITOK);
2452		len = snprintf(filename, len, "%s%s", ATTR_FILE_PREFIX, basename);
2453	}
2454	/*
2455	 * Note that the lookup here does not authorize.  Since we are looking
2456	 * up in the same directory that we already have the file vnode in,
2457	 * we must have been given the file vnode legitimately.  Read/write
2458	 * access has already been authorized in layers above for calls from
2459	 * userspace, and the authorization code using this path to read
2460	 * file security from the EA must always get access
2461	 */
2462lookup:
2463	NDINIT(&nd, LOOKUP, OP_OPEN, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH,
2464	       UIO_SYSSPACE, CAST_USER_ADDR_T(filename), context);
2465   	nd.ni_dvp = dvp;
2466
2467	if (fileflags & O_CREAT) {
2468		nd.ni_cnd.cn_nameiop = CREATE;
2469#if CONFIG_TRIGGERS
2470		nd.ni_op = OP_LINK;
2471#endif
2472		if (dvp != vp) {
2473			nd.ni_cnd.cn_flags |= LOCKPARENT;
2474		}
2475		if ( (error = namei(&nd))) {
2476		        nd.ni_dvp = NULLVP;
2477			error = ENOATTR;
2478			goto out;
2479		}
2480		if ( (xvp = nd.ni_vp) == NULLVP) {
2481			uid_t uid;
2482			gid_t gid;
2483			mode_t umode;
2484
2485			/*
2486			 * Pick up uid/gid/mode from target file.
2487			 */
2488			VATTR_INIT(&va);
2489			VATTR_WANTED(&va, va_uid);
2490			VATTR_WANTED(&va, va_gid);
2491			VATTR_WANTED(&va, va_mode);
2492			if (VNOP_GETATTR(vp, &va, context) == 0  &&
2493			    VATTR_IS_SUPPORTED(&va, va_uid)  &&
2494			    VATTR_IS_SUPPORTED(&va, va_gid)  &&
2495			    VATTR_IS_SUPPORTED(&va, va_mode)) {
2496				uid = va.va_uid;
2497				gid = va.va_gid;
2498				umode = va.va_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
2499			} else /* fallback values */ {
2500				uid = KAUTH_UID_NONE;
2501				gid = KAUTH_GID_NONE;
2502				umode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
2503			}
2504
2505			VATTR_INIT(&va);
2506			VATTR_SET(&va, va_type, VREG);
2507			VATTR_SET(&va, va_mode, umode);
2508			if (uid != KAUTH_UID_NONE)
2509				VATTR_SET(&va, va_uid, uid);
2510			if (gid != KAUTH_GID_NONE)
2511				VATTR_SET(&va, va_gid, gid);
2512
2513			error = vn_create(dvp, &nd.ni_vp, &nd, &va,
2514			                  VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL,
2515					  0, NULL,
2516			                  context);
2517			if (error)
2518				error = ENOATTR;
2519			else
2520				xvp = nd.ni_vp;
2521		}
2522		nameidone(&nd);
2523		if (dvp != vp) {
2524			vnode_put(dvp);  /* drop iocount from LOCKPARENT request above */
2525		}
2526		if (error)
2527		        goto out;
2528	} else {
2529		if ((error = namei(&nd))) {
2530			nd.ni_dvp = NULLVP;
2531			error = ENOATTR;
2532			goto out;
2533		}
2534	        xvp = nd.ni_vp;
2535		nameidone(&nd);
2536	}
2537	nd.ni_dvp = NULLVP;
2538
2539	if (xvp->v_type != VREG) {
2540		error = ENOATTR;
2541		goto out;
2542	}
2543	/*
2544	 * Owners must match.
2545	 */
2546	VATTR_INIT(&va);
2547	VATTR_WANTED(&va, va_uid);
2548	if (VNOP_GETATTR(vp, &va, context) == 0 && VATTR_IS_SUPPORTED(&va, va_uid)) {
2549		uid_t owner = va.va_uid;
2550
2551		VATTR_INIT(&va);
2552		VATTR_WANTED(&va, va_uid);
2553		if (VNOP_GETATTR(xvp, &va, context) == 0 && (owner != va.va_uid)) {
2554			error = ENOATTR;  /* don't use this "._" file */
2555			goto out;
2556		}
2557	}
2558
2559	if ( (error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) {
2560		error = ENOATTR;
2561		goto out;
2562	}
2563	opened = 1;
2564
2565	if ((error = vnode_ref(xvp))) {
2566		goto out;
2567	}
2568	referenced = 1;
2569
2570	/* If create was requested, make sure file header exists. */
2571	if (fileflags & O_CREAT) {
2572		VATTR_INIT(&va);
2573		VATTR_WANTED(&va, va_data_size);
2574		VATTR_WANTED(&va, va_fileid);
2575		VATTR_WANTED(&va, va_nlink);
2576		if ( (error = vnode_getattr(xvp, &va, context)) != 0) {
2577			error = EPERM;
2578			goto out;
2579		}
2580
2581		/* If the file is empty then add a default header. */
2582		if (va.va_data_size == 0) {
2583			/* Don't adopt hard-linked "._" files. */
2584			if (VATTR_IS_SUPPORTED(&va, va_nlink) && va.va_nlink > 1) {
2585				error = EPERM;
2586				goto out;
2587			}
2588			if ( (error = create_xattrfile(xvp, (u_int32_t)va.va_fileid, context)))
2589				goto out;
2590		}
2591	}
2592	/* Apply file locking if requested. */
2593	if (fileflags & (O_EXLOCK | O_SHLOCK)) {
2594		short locktype;
2595
2596		locktype = (fileflags & O_EXLOCK) ? F_WRLCK : F_RDLCK;
2597		error = lock_xattrfile(xvp, locktype, context);
2598		if (error)
2599			error = ENOATTR;
2600	}
2601out:
2602	if (error) {
2603		if (xvp != NULLVP) {
2604			if (opened) {
2605				(void) VNOP_CLOSE(xvp, fileflags, context);
2606			}
2607
2608			if (fileflags & O_CREAT) {
2609				/* Delete the xattr file if we encountered any errors */
2610				(void) remove_xattrfile (xvp, context);
2611			}
2612
2613			if (referenced) {
2614				(void) vnode_rele(xvp);
2615			}
2616			(void) vnode_put(xvp);
2617			xvp = NULLVP;
2618		}
2619		if ((error == ENOATTR) && (fileflags & O_CREAT)) {
2620			error = EPERM;
2621		}
2622	}
2623	/* Release resources after error-handling */
2624	if (dvp && (dvp != vp)) {
2625		vnode_put(dvp);
2626	}
2627	if (basename) {
2628		vnode_putname(basename);
2629	}
2630	if (filename && filename != &smallname[0]) {
2631		FREE(filename, M_TEMP);
2632	}
2633
2634	*xvpp = xvp;  /* return a referenced vnode */
2635	return (error);
2636}
2637
2638static void
2639close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context)
2640{
2641//	if (fileflags & FWRITE)
2642//		(void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2643
2644	if (fileflags & (O_EXLOCK | O_SHLOCK))
2645		(void) unlock_xattrfile(xvp, context);
2646
2647	(void) VNOP_CLOSE(xvp, fileflags, context);
2648	(void) vnode_rele(xvp);
2649	(void) vnode_put(xvp);
2650}
2651
2652static int
2653remove_xattrfile(vnode_t xvp, vfs_context_t context)
2654{
2655	vnode_t dvp;
2656	struct nameidata nd;
2657	char *path = NULL;
2658	int pathlen;
2659	int error = 0;
2660
2661	MALLOC_ZONE(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
2662	if (path == NULL)
2663		return ENOMEM;
2664
2665	pathlen = MAXPATHLEN;
2666	error = vn_getpath(xvp, path, &pathlen);
2667	if (error) {
2668		FREE_ZONE(path, MAXPATHLEN, M_NAMEI);
2669		return (error);
2670	}
2671
2672	NDINIT(&nd, DELETE, OP_UNLINK, LOCKPARENT | NOFOLLOW | DONOTAUTH,
2673	       UIO_SYSSPACE, CAST_USER_ADDR_T(path), context);
2674	error = namei(&nd);
2675	FREE_ZONE(path, MAXPATHLEN, M_NAMEI);
2676	if (error) {
2677		return (error);
2678	}
2679	dvp = nd.ni_dvp;
2680	xvp = nd.ni_vp;
2681
2682	error = VNOP_REMOVE(dvp, xvp, &nd.ni_cnd, 0, context);
2683	nameidone(&nd);
2684	vnode_put(dvp);
2685	vnode_put(xvp);
2686
2687	return (error);
2688}
2689
2690/*
2691 * Read in and parse the AppleDouble header and entries, and the extended
2692 * attribute header and entries if any.  Populates the fields of ainfop
2693 * based on the headers and entries found.
2694 *
2695 * The basic idea is to:
2696 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file.  All
2697 *   AppleDouble entries, the extended attribute header, and extended
2698 *   attribute entries must lie within this part of the file; the rest of
2699 *   the AppleDouble handling code assumes this.  Plus it allows us to
2700 *   somewhat optimize by doing a smaller number of larger I/Os.
2701 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2702 *   entries).
2703 * - Find the Finder Info and Resource Fork entries, if any.
2704 * - If we're going to be writing, try to make sure the Finder Info entry has
2705 *   room to store the extended attribute header, plus some space for extended
2706 *   attributes.
2707 * - Swap and sanity check the extended attribute header and entries (if any).
2708 */
2709static int
2710get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context)
2711{
2712	uio_t auio = NULL;
2713	void * buffer = NULL;
2714	apple_double_header_t  *filehdr;
2715	struct vnode_attr va;
2716	size_t iosize;
2717	int i;
2718	int error;
2719
2720	bzero(ainfop, sizeof(attr_info_t));
2721	ainfop->filevp = xvp;
2722	ainfop->context = context;
2723	VATTR_INIT(&va);
2724	VATTR_WANTED(&va, va_data_size);
2725	VATTR_WANTED(&va, va_fileid);
2726	if ((error = vnode_getattr(xvp, &va, context))) {
2727		goto bail;
2728	}
2729	ainfop->filesize = va.va_data_size;
2730
2731	/* When setting attributes, allow room for the header to grow. */
2732	if (setting)
2733		iosize = ATTR_MAX_HDR_SIZE;
2734	else
2735		iosize = MIN(ATTR_MAX_HDR_SIZE, ainfop->filesize);
2736
2737	if (iosize == 0) {
2738		error = ENOATTR;
2739		goto bail;
2740	}
2741	ainfop->iosize = iosize;
2742	MALLOC(buffer, void *, iosize, M_TEMP, M_WAITOK);
2743	if (buffer == NULL){
2744		error = ENOMEM;
2745		goto bail;
2746	}
2747
2748	auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
2749	uio_addiov(auio, (uintptr_t)buffer, iosize);
2750
2751	/* Read the file header. */
2752	error = VNOP_READ(xvp, auio, 0, context);
2753	if (error) {
2754		goto bail;
2755	}
2756	ainfop->rawsize = iosize - uio_resid(auio);
2757	ainfop->rawdata = (u_int8_t *)buffer;
2758
2759	filehdr = (apple_double_header_t *)buffer;
2760
2761	error = check_and_swap_apple_double_header(ainfop);
2762	if (error)
2763		goto bail;
2764
2765	ainfop->filehdr = filehdr;  /* valid AppleDouble header */
2766
2767	/* rel_xattrinfo is responsible for freeing the header buffer */
2768	buffer = NULL;
2769
2770	/* Find the Finder Info and Resource Fork entries, if any */
2771	for (i = 0; i < filehdr->numEntries; ++i) {
2772		if (filehdr->entries[i].type == AD_FINDERINFO &&
2773		    filehdr->entries[i].length >= FINDERINFOSIZE) {
2774			/* We found the Finder Info entry. */
2775			ainfop->finderinfo = &filehdr->entries[i];
2776
2777			/*
2778			 * Is the Finder Info "empty" (all zeroes)?  If so,
2779			 * we'll pretend like the Finder Info extended attribute
2780			 * does not exist.
2781			 *
2782			 * Note: we have to make sure the Finder Info is
2783			 * contained within the buffer we have already read,
2784			 * to avoid accidentally accessing a bogus address.
2785			 * If it is outside the buffer, we just assume the
2786			 * Finder Info is non-empty.
2787			 */
2788			if (ainfop->finderinfo->offset + FINDERINFOSIZE <= ainfop->rawsize &&
2789			    bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) {
2790				ainfop->emptyfinderinfo = 1;
2791			}
2792		}
2793		if (filehdr->entries[i].type == AD_RESOURCE) {
2794			/*
2795			 * Ignore zero-length resource forks when getting.  If setting,
2796			 * we need to remember the resource fork entry so it can be
2797			 * updated once the new content has been written.
2798			 */
2799			if (filehdr->entries[i].length == 0 && !setting)
2800				continue;
2801
2802			/*
2803			 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2804			 *
2805			 * The "empty" resource headers we created have a system data tag of:
2806			 * "This resource fork intentionally left blank   "
2807			 */
2808			if (filehdr->entries[i].length == sizeof(rsrcfork_header_t) && !setting) {
2809				uio_t  rf_uio;
2810				u_int8_t  systemData[64];
2811				int  rf_err;
2812
2813
2814				/* Read the system data which starts at byte 16 */
2815				rf_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
2816				uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData));
2817				uio_setoffset(rf_uio, filehdr->entries[i].offset + 16);
2818				rf_err = VNOP_READ(xvp, rf_uio, 0, context);
2819				uio_free(rf_uio);
2820
2821				if (rf_err != 0 ||
2822				    bcmp(systemData, RF_EMPTY_TAG, sizeof(RF_EMPTY_TAG)) == 0) {
2823					continue;  /* skip this resource fork */
2824				}
2825			}
2826			ainfop->rsrcfork = &filehdr->entries[i];
2827			if (i != (filehdr->numEntries - 1)) {
2828				printf("get_xattrinfo: resource fork not last entry\n");
2829				ainfop->readonly = 1;
2830			}
2831			continue;
2832		}
2833	}
2834
2835	/*
2836	 * See if this file looks like it is laid out correctly to contain
2837	 * extended attributes.  If so, then do the following:
2838	 *
2839	 * - If we're going to be writing, try to make sure the Finder Info
2840	 *   entry has room to store the extended attribute header, plus some
2841	 *   space for extended attributes.
2842	 *
2843	 * - Swap and sanity check the extended attribute header and entries
2844	 *   (if any).
2845	 */
2846	if (filehdr->numEntries == 2 &&
2847	    ainfop->finderinfo == &filehdr->entries[0] &&
2848	    ainfop->rsrcfork == &filehdr->entries[1] &&
2849	    ainfop->finderinfo->offset == offsetof(apple_double_header_t, finfo)) {
2850		attr_header_t *attrhdr;
2851		attrhdr = (attr_header_t *)filehdr;
2852		/*
2853		 * If we're going to be writing, try to make sure the Finder
2854		 * Info entry has room to store the extended attribute header,
2855		 * plus some space for extended attributes.
2856		 */
2857		if (setting && ainfop->finderinfo->length == FINDERINFOSIZE) {
2858			size_t delta;
2859			size_t writesize;
2860
2861			delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE);
2862			if (ainfop->rsrcfork && filehdr->entries[1].length) {
2863				/* Make some room before existing resource fork. */
2864				shift_data_down(xvp,
2865						filehdr->entries[1].offset,
2866						filehdr->entries[1].length,
2867						delta, context);
2868				writesize = sizeof(attr_header_t);
2869			} else {
2870				/* Create a new, empty resource fork. */
2871				rsrcfork_header_t *rsrcforkhdr;
2872
2873				vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context);
2874
2875				/* Steal some space for an empty RF header. */
2876				delta -= sizeof(rsrcfork_header_t);
2877
2878				bzero(&attrhdr->appledouble.pad[0], delta);
2879				rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta);
2880
2881				/* Fill in Empty Resource Fork Header. */
2882				init_empty_resource_fork(rsrcforkhdr);
2883
2884				filehdr->entries[1].length = sizeof(rsrcfork_header_t);
2885				writesize = ATTR_BUF_SIZE;
2886			}
2887			filehdr->entries[0].length += delta;
2888			filehdr->entries[1].offset += delta;
2889
2890			/* Fill in Attribute Header. */
2891			attrhdr->magic       = ATTR_HDR_MAGIC;
2892			attrhdr->debug_tag   = (u_int32_t)va.va_fileid;
2893			attrhdr->total_size  = filehdr->entries[1].offset;
2894			attrhdr->data_start  = sizeof(attr_header_t);
2895			attrhdr->data_length = 0;
2896			attrhdr->reserved[0] = 0;
2897			attrhdr->reserved[1] = 0;
2898			attrhdr->reserved[2] = 0;
2899			attrhdr->flags       = 0;
2900			attrhdr->num_attrs   = 0;
2901
2902			/* Push out new header */
2903			uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE);
2904			uio_addiov(auio, (uintptr_t)filehdr, writesize);
2905
2906			swap_adhdr(filehdr);	/* to big endian */
2907			swap_attrhdr(attrhdr, ainfop);	/* to big endian */
2908			error = VNOP_WRITE(xvp, auio, 0, context);
2909			swap_adhdr(filehdr);	/* back to native */
2910			/* The attribute header gets swapped below. */
2911		}
2912	}
2913	/*
2914	 * Swap and sanity check the extended attribute header and
2915	 * entries (if any).  The Finder Info content must be big enough
2916	 * to include the extended attribute header; if not, we just
2917	 * ignore it.
2918	 *
2919	 * Note that we're passing the offset + length (i.e. the end)
2920	 * of the Finder Info instead of rawsize to validate_attrhdr.
2921	 * This ensures that all extended attributes lie within the
2922	 * Finder Info content according to the AppleDouble entry.
2923	 *
2924	 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2925	 * header was found.
2926	 */
2927	if (ainfop->finderinfo &&
2928		ainfop->finderinfo == &filehdr->entries[0] &&
2929		ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) {
2930		attr_header_t *attrhdr = (attr_header_t*)filehdr;
2931
2932		if ((error = check_and_swap_attrhdr(attrhdr, ainfop)) == 0) {
2933			ainfop->attrhdr = attrhdr;  /* valid attribute header */
2934			/* First attr_entry starts immediately following attribute header */
2935			ainfop->attr_entry = (attr_entry_t *)&attrhdr[1];
2936		}
2937	}
2938
2939	error = 0;
2940bail:
2941	if (auio != NULL)
2942		uio_free(auio);
2943	if (buffer != NULL)
2944		FREE(buffer, M_TEMP);
2945	return (error);
2946}
2947
2948
2949static int
2950create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context)
2951{
2952	attr_header_t *xah;
2953	rsrcfork_header_t *rsrcforkhdr;
2954	void * buffer;
2955	uio_t auio;
2956	int rsrcforksize;
2957	int error;
2958
2959	MALLOC(buffer, void *, ATTR_BUF_SIZE, M_TEMP, M_WAITOK);
2960	bzero(buffer, ATTR_BUF_SIZE);
2961
2962	xah = (attr_header_t *)buffer;
2963	auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
2964	uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE);
2965	rsrcforksize = sizeof(rsrcfork_header_t);
2966	rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize);
2967
2968	/* Fill in Apple Double Header. */
2969	xah->appledouble.magic             = SWAP32 (ADH_MAGIC);
2970	xah->appledouble.version           = SWAP32 (ADH_VERSION);
2971	xah->appledouble.numEntries        = SWAP16 (2);
2972	xah->appledouble.entries[0].type   = SWAP32 (AD_FINDERINFO);
2973	xah->appledouble.entries[0].offset = SWAP32 (offsetof(apple_double_header_t, finfo));
2974	xah->appledouble.entries[0].length = SWAP32 (ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize);
2975	xah->appledouble.entries[1].type   = SWAP32 (AD_RESOURCE);
2976	xah->appledouble.entries[1].offset = SWAP32 (ATTR_BUF_SIZE - rsrcforksize);
2977	xah->appledouble.entries[1].length = SWAP32 (rsrcforksize);
2978	bcopy(ADH_MACOSX, xah->appledouble.filler, sizeof(xah->appledouble.filler));
2979
2980	/* Fill in Attribute Header. */
2981	xah->magic       = SWAP32 (ATTR_HDR_MAGIC);
2982	xah->debug_tag   = SWAP32 (fileid);
2983	xah->total_size  = SWAP32 (ATTR_BUF_SIZE - rsrcforksize);
2984	xah->data_start  = SWAP32 (sizeof(attr_header_t));
2985
2986	/* Fill in Empty Resource Fork Header. */
2987	init_empty_resource_fork(rsrcforkhdr);
2988
2989	/* Push it out. */
2990	error = VNOP_WRITE(xvp, auio, IO_UNIT, context);
2991
2992	/* Did we write out the full uio? */
2993	if (uio_resid(auio) > 0) {
2994		error = ENOSPC;
2995	}
2996
2997	uio_free(auio);
2998	FREE(buffer, M_TEMP);
2999
3000	return (error);
3001}
3002
3003static void
3004init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr)
3005{
3006	bzero(rsrcforkhdr, sizeof(rsrcfork_header_t));
3007	rsrcforkhdr->fh_DataOffset = SWAP32 (RF_FIRST_RESOURCE);
3008	rsrcforkhdr->fh_MapOffset  = SWAP32 (RF_FIRST_RESOURCE);
3009	rsrcforkhdr->fh_MapLength  = SWAP32 (RF_NULL_MAP_LENGTH);
3010	rsrcforkhdr->mh_DataOffset = SWAP32 (RF_FIRST_RESOURCE);
3011	rsrcforkhdr->mh_MapOffset  = SWAP32 (RF_FIRST_RESOURCE);
3012	rsrcforkhdr->mh_MapLength  = SWAP32 (RF_NULL_MAP_LENGTH);
3013	rsrcforkhdr->mh_Types      = SWAP16 (RF_NULL_MAP_LENGTH - 2 );
3014	rsrcforkhdr->mh_Names      = SWAP16 (RF_NULL_MAP_LENGTH);
3015	rsrcforkhdr->typeCount     = SWAP16 (-1);
3016	bcopy(RF_EMPTY_TAG, rsrcforkhdr->systemData, sizeof(RF_EMPTY_TAG));
3017}
3018
3019static void
3020rel_xattrinfo(attr_info_t *ainfop)
3021{
3022	FREE(ainfop->filehdr, M_TEMP);
3023	bzero(ainfop, sizeof(attr_info_t));
3024}
3025
3026static int
3027write_xattrinfo(attr_info_t *ainfop)
3028{
3029	uio_t auio;
3030	int error;
3031
3032	auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
3033	uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize);
3034
3035	swap_adhdr(ainfop->filehdr);
3036	if (ainfop->attrhdr != NULL) {
3037		swap_attrhdr(ainfop->attrhdr, ainfop);
3038	}
3039
3040	error = VNOP_WRITE(ainfop->filevp, auio, 0, ainfop->context);
3041
3042	swap_adhdr(ainfop->filehdr);
3043	if (ainfop->attrhdr != NULL) {
3044		swap_attrhdr(ainfop->attrhdr, ainfop);
3045	}
3046	uio_free(auio);
3047
3048	return (error);
3049}
3050
3051#if BYTE_ORDER == LITTLE_ENDIAN
3052/*
3053 * Endian swap apple double header
3054 */
3055static void
3056swap_adhdr(apple_double_header_t *adh)
3057{
3058	int count;
3059	int i;
3060
3061	count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries);
3062
3063	adh->magic      = SWAP32 (adh->magic);
3064	adh->version    = SWAP32 (adh->version);
3065	adh->numEntries = SWAP16 (adh->numEntries);
3066
3067	for (i = 0; i < count; i++) {
3068		adh->entries[i].type   = SWAP32 (adh->entries[i].type);
3069		adh->entries[i].offset = SWAP32 (adh->entries[i].offset);
3070		adh->entries[i].length = SWAP32 (adh->entries[i].length);
3071	}
3072}
3073
3074/*
3075 * Endian swap extended attributes header
3076 */
3077static void
3078swap_attrhdr(attr_header_t *ah, attr_info_t* info)
3079{
3080	attr_entry_t *ae;
3081	int count;
3082	int i;
3083
3084	count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
3085
3086	ah->magic       = SWAP32 (ah->magic);
3087	ah->debug_tag   = SWAP32 (ah->debug_tag);
3088	ah->total_size  = SWAP32 (ah->total_size);
3089	ah->data_start  = SWAP32 (ah->data_start);
3090	ah->data_length = SWAP32 (ah->data_length);
3091	ah->flags       = SWAP16 (ah->flags);
3092	ah->num_attrs   = SWAP16 (ah->num_attrs);
3093
3094	ae = (attr_entry_t *)(&ah[1]);
3095	for (i = 0; i < count && ATTR_VALID(ae, *info); i++, ae = ATTR_NEXT(ae)) {
3096		ae->offset = SWAP32 (ae->offset);
3097		ae->length = SWAP32 (ae->length);
3098		ae->flags  = SWAP16 (ae->flags);
3099	}
3100}
3101#endif
3102
3103/*
3104 * Validate and swap the attributes header contents, and each attribute's
3105 * attr_entry_t.
3106 *
3107 * Note: Assumes the caller has verified that the Finder Info content is large
3108 * enough to contain the attr_header structure itself.  Therefore, we can
3109 * swap the header fields before sanity checking them.
3110 */
3111static int
3112check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop)
3113{
3114	attr_entry_t *ae;
3115	u_int8_t *buf_end;
3116	u_int32_t end;
3117	int count;
3118	int i;
3119
3120	if (ah == NULL)
3121		return EINVAL;
3122
3123	if (SWAP32(ah->magic) != ATTR_HDR_MAGIC)
3124		return EINVAL;
3125
3126	/* Swap the basic header fields */
3127	ah->magic	= SWAP32(ah->magic);
3128	ah->debug_tag   = SWAP32 (ah->debug_tag);
3129	ah->total_size  = SWAP32 (ah->total_size);
3130	ah->data_start  = SWAP32 (ah->data_start);
3131	ah->data_length = SWAP32 (ah->data_length);
3132	ah->flags       = SWAP16 (ah->flags);
3133	ah->num_attrs   = SWAP16 (ah->num_attrs);
3134
3135	/*
3136	 * Make sure the total_size fits within the Finder Info area, and the
3137	 * extended attribute data area fits within total_size.
3138	 */
3139	end = ah->data_start + ah->data_length;
3140	if (ah->total_size > ainfop->finderinfo->offset + ainfop->finderinfo->length ||
3141	    end < ah->data_start ||
3142	    end > ah->total_size) {
3143		return EINVAL;
3144	}
3145
3146	/*
3147	 * Make sure each of the attr_entry_t's fits within total_size.
3148	 */
3149	buf_end = ainfop->rawdata + ah->total_size;
3150	count = ah->num_attrs;
3151	ae = (attr_entry_t *)(&ah[1]);
3152
3153	for (i=0; i<count; i++) {
3154		/* Make sure the fixed-size part of this attr_entry_t fits. */
3155		if ((u_int8_t *) &ae[1] > buf_end)
3156			return EINVAL;
3157
3158		/* Make sure the variable-length name fits (+1 is for NUL terminator) */
3159		/* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3160		if (&ae->name[ae->namelen+1] > buf_end)
3161			return EINVAL;
3162
3163		/* Swap the attribute entry fields */
3164		ae->offset	= SWAP32(ae->offset);
3165		ae->length	= SWAP32(ae->length);
3166		ae->flags	= SWAP16(ae->flags);
3167
3168		/* Make sure the attribute content fits. */
3169		end = ae->offset + ae->length;
3170		if (end < ae->offset || end > ah->total_size)
3171			return EINVAL;
3172
3173		ae = ATTR_NEXT(ae);
3174	}
3175
3176	/*
3177	 * TODO: Make sure the contents of attributes don't overlap the header
3178	 * and don't overlap each other.  The hard part is that we don't know
3179	 * what the actual header size is until we have looped over all of the
3180	 * variable-sized attribute entries.
3181	 *
3182	 * XXX  Is there any guarantee that attribute entries are stored in
3183	 * XXX  order sorted by the contents' file offset?  If so, that would
3184	 * XXX  make the pairwise overlap check much easier.
3185	 */
3186
3187	return 0;
3188}
3189
3190//
3191// "start" & "end" are byte offsets in the file.
3192// "to" is the byte offset we want to move the
3193// data to.  "to" should be > "start".
3194//
3195// we do the copy backwards to avoid problems if
3196// there's an overlap.
3197//
3198static int
3199shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
3200{
3201	int ret, iolen;
3202	size_t chunk, orig_chunk;
3203	char *buff;
3204	off_t pos;
3205	kauth_cred_t ucred = vfs_context_ucred(context);
3206	proc_t p = vfs_context_proc(context);
3207
3208	if (delta == 0 || len == 0) {
3209		return 0;
3210	}
3211
3212	chunk = 4096;
3213	if (len < chunk) {
3214		chunk = len;
3215	}
3216	orig_chunk = chunk;
3217
3218	if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk)) {
3219		return ENOMEM;
3220	}
3221
3222	for(pos=start+len-chunk; pos >= start; pos-=chunk) {
3223		ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p);
3224		if (iolen != 0) {
3225			printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3226				pos, ret, chunk, ret);
3227			break;
3228		}
3229
3230		ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p);
3231		if (iolen != 0) {
3232			printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3233				pos+delta, ret, chunk, ret);
3234			break;
3235		}
3236
3237		if ((pos - (off_t)chunk) < start) {
3238			chunk = pos - start;
3239
3240			if (chunk == 0) {   // we're all done
3241				break;
3242			}
3243		}
3244	}
3245	kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk);
3246
3247	return 0;
3248}
3249
3250
3251static int
3252shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
3253{
3254	int ret, iolen;
3255	size_t chunk, orig_chunk;
3256	char *buff;
3257	off_t pos;
3258	off_t end;
3259	kauth_cred_t ucred = vfs_context_ucred(context);
3260	proc_t p = vfs_context_proc(context);
3261
3262	if (delta == 0 || len == 0) {
3263		return 0;
3264	}
3265
3266	chunk = 4096;
3267	if (len < chunk) {
3268		chunk = len;
3269	}
3270	orig_chunk = chunk;
3271	end = start + len;
3272
3273	if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk)) {
3274		return ENOMEM;
3275	}
3276
3277	for(pos = start; pos < end; pos += chunk) {
3278		ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p);
3279		if (iolen != 0) {
3280			printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3281				pos, ret, chunk, ret);
3282			break;
3283		}
3284
3285		ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p);
3286		if (iolen != 0) {
3287			printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3288				pos+delta, ret, chunk, ret);
3289			break;
3290		}
3291
3292		if ((pos + (off_t)chunk) > end) {
3293			chunk = end - pos;
3294
3295			if (chunk == 0) {   // we're all done
3296				break;
3297			}
3298		}
3299	}
3300	kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk);
3301
3302	return 0;
3303}
3304
3305static int
3306lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context)
3307{
3308	struct flock lf;
3309	int error;
3310
3311	lf.l_whence = SEEK_SET;
3312	lf.l_start = 0;
3313	lf.l_len = 0;
3314	lf.l_type = locktype; /* F_WRLCK or F_RDLCK */
3315	/* Note: id is just a kernel address that's not a proc */
3316	error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_SETLK, &lf, F_FLOCK|F_WAIT, context, NULL);
3317	return (error == ENOTSUP ? 0 : error);
3318}
3319
3320 int
3321unlock_xattrfile(vnode_t xvp, vfs_context_t context)
3322{
3323	struct flock lf;
3324	int error;
3325
3326	lf.l_whence = SEEK_SET;
3327	lf.l_start = 0;
3328	lf.l_len = 0;
3329	lf.l_type = F_UNLCK;
3330	/* Note: id is just a kernel address that's not a proc */
3331	error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_UNLCK, &lf, F_FLOCK, context, NULL);
3332	return (error == ENOTSUP ? 0 : error);
3333}
3334
3335#else /* CONFIG_APPLEDOUBLE */
3336
3337
3338static int
3339default_getxattr(__unused vnode_t vp, __unused const char *name,
3340    __unused uio_t uio, __unused size_t *size, __unused int options,
3341    __unused vfs_context_t context)
3342{
3343	return (ENOTSUP);
3344}
3345
3346static int
3347default_setxattr(__unused vnode_t vp, __unused const char *name,
3348    __unused uio_t uio, __unused int options, __unused vfs_context_t context)
3349{
3350	return (ENOTSUP);
3351}
3352
3353static int
3354default_listxattr(__unused vnode_t vp,
3355    __unused uio_t uio, __unused size_t *size, __unused int options,
3356    __unused vfs_context_t context)
3357{
3358	return (ENOTSUP);
3359}
3360
3361static int
3362default_removexattr(__unused vnode_t vp, __unused const char *name,
3363   __unused int options, __unused vfs_context_t context)
3364{
3365	return (ENOTSUP);
3366}
3367
3368#endif /* CONFIG_APPLEDOUBLE */
3369