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