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