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