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