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