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