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