xref: /xnu-8019.80.24/bsd/vfs/vfs_attrlist.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1 /*
2  * Copyright (c) 1995-2019 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 #include <sys/systm.h>
37 #include <sys/namei.h>
38 #include <sys/kernel.h>
39 #include <sys/stat.h>
40 #include <sys/syslog.h>
41 #include <sys/vnode_internal.h>
42 #include <sys/mount_internal.h>
43 #include <sys/proc_internal.h>
44 #include <sys/file_internal.h>
45 #include <sys/kauth.h>
46 #include <sys/uio_internal.h>
47 #include <kern/kalloc.h>
48 #include <sys/attr.h>
49 #include <sys/sysproto.h>
50 #include <sys/xattr.h>
51 #include <sys/fsevents.h>
52 #include <kern/zalloc.h>
53 #include <miscfs/specfs/specdev.h>
54 #include <security/audit/audit.h>
55 
56 #if CONFIG_MACF
57 #include <security/mac_framework.h>
58 #endif
59 
60 #define ATTR_TIME_SIZE  -1
61 
62 static int readdirattr(vnode_t, struct fd_vn_data *, uio_t, struct attrlist *,
63     uint64_t, int *, int *, vfs_context_t ctx) __attribute__((noinline));
64 
65 static void
66 vattr_get_alt_data(vnode_t, struct attrlist *, struct vnode_attr *, int, int,
67     int, vfs_context_t) __attribute__((noinline));
68 
69 static void get_error_attributes(vnode_t, struct attrlist *, uint64_t, user_addr_t,
70     size_t, int, caddr_t, vfs_context_t) __attribute__((noinline));
71 
72 static int getvolattrlist(vfs_context_t, vnode_t, struct attrlist *, user_addr_t,
73     size_t, uint64_t, enum uio_seg, int) __attribute__((noinline));
74 
75 static int get_direntry(vfs_context_t, vnode_t, struct fd_vn_data *, int *,
76     struct direntry **) __attribute__((noinline));
77 
78 /*
79  * Structure describing the state of an in-progress attrlist operation.
80  */
81 struct _attrlist_buf {
82 	char    *base;
83 	char    *fixedcursor;
84 	char    *varcursor;
85 	ssize_t allocated;
86 	ssize_t needed;
87 	attribute_set_t actual;
88 	attribute_set_t valid;
89 };
90 
91 #define _ATTRLIST_BUF_INIT(a)  do {(a)->base = (a)->fixedcursor = (a)->varcursor = NULL; (a)->allocated = (a)->needed = 0l; ATTRIBUTE_SET_INIT(&((a)->actual)); ATTRIBUTE_SET_INIT(&((a)->valid));} while(0)
92 
93 
94 /*
95  * Attempt to pack a fixed width attribute of size (count) bytes from
96  * source to our attrlist buffer.
97  */
98 static void
attrlist_pack_fixed(struct _attrlist_buf * ab,void * source,ssize_t count)99 attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
100 {
101 	/*
102 	 * Use ssize_t for pointer math purposes,
103 	 * since a ssize_t is a signed long
104 	 */
105 	ssize_t fit;
106 
107 	/*
108 	 * Compute the amount of remaining space in the attrlist buffer
109 	 * based on how much we've used for fixed width fields vs. the
110 	 * start of the attributes.
111 	 *
112 	 * If we've still got room, then 'fit' will contain the amount of
113 	 * remaining space.
114 	 *
115 	 * Note that this math is safe because, in the event that the
116 	 * fixed-width cursor has moved beyond the end of the buffer,
117 	 * then, the second input into lmin() below will be negative, and
118 	 * we will fail the (fit > 0) check below.
119 	 */
120 	fit = lmin(count, ab->allocated - (ab->fixedcursor - ab->base));
121 	if (fit > 0) {
122 		/* Copy in as much as we can */
123 		bcopy(source, ab->fixedcursor, fit);
124 	}
125 
126 	/* always move in increments of 4, even if we didn't pack an attribute. */
127 	ab->fixedcursor += roundup(count, 4);
128 }
129 
130 /*
131  * Attempt to pack one (or two) variable width attributes into the attrlist
132  * buffer.  If we are trying to pack two variable width attributes, they are treated
133  * as a single variable-width attribute from the POV of the system call caller.
134  *
135  * Recall that a variable-width attribute has two components: the fixed-width
136  * attribute that tells the caller where to look, and the actual variable width data.
137  */
138 static void
attrlist_pack_variable2(struct _attrlist_buf * ab,const void * source,ssize_t count,const void * ext,ssize_t extcount)139 attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count,
140     const void *ext, ssize_t extcount)
141 {
142 	/* Use ssize_t's for pointer math ease */
143 	struct attrreference ar;
144 	ssize_t fit;
145 
146 	/*
147 	 * Pack the fixed-width component to the variable object.
148 	 * Note that we may be able to pack the fixed width attref, but not
149 	 * the variable (if there's no room).
150 	 */
151 	ar.attr_dataoffset = (int32_t)(ab->varcursor - ab->fixedcursor);
152 	ar.attr_length = (u_int32_t)(count + extcount);
153 	attrlist_pack_fixed(ab, &ar, sizeof(ar));
154 
155 	/*
156 	 * Use an lmin() to do a signed comparison. We use a signed comparison
157 	 * to detect the 'out of memory' conditions as described above in the
158 	 * fixed width check above.
159 	 *
160 	 * Then pack the first variable attribute as space allows.  Note that we advance
161 	 * the variable cursor only if we we had some available space.
162 	 */
163 	fit = lmin(count, ab->allocated - (ab->varcursor - ab->base));
164 	if (fit > 0) {
165 		if (source != NULL) {
166 			bcopy(source, ab->varcursor, fit);
167 		}
168 		ab->varcursor += fit;
169 	}
170 
171 	/* Compute the available space for the second attribute */
172 	fit = lmin(extcount, ab->allocated - (ab->varcursor - ab->base));
173 	if (fit > 0) {
174 		/* Copy in data for the second attribute (if needed) if there is room */
175 		if (ext != NULL) {
176 			bcopy(ext, ab->varcursor, fit);
177 		}
178 		ab->varcursor += fit;
179 	}
180 	/* always move in increments of 4 */
181 	ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4);
182 }
183 
184 /*
185  * Packing a single variable-width attribute is the same as calling the two, but with
186  * an invalid 2nd attribute.
187  */
188 static void
attrlist_pack_variable(struct _attrlist_buf * ab,const void * source,ssize_t count)189 attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
190 {
191 	attrlist_pack_variable2(ab, source, count, NULL, 0);
192 }
193 
194 /*
195  * Attempt to pack a string. This is a special case of a variable width attribute.
196  *
197  * If "source" is NULL, then an empty string ("") will be packed.  If "source" is
198  * not NULL, but "count" is zero, then "source" is assumed to be a NUL-terminated
199  * C-string.  If "source" is not NULL and "count" is not zero, then only the first
200  * "count" bytes of "source" will be copied, and a NUL terminator will be added.
201  *
202  * If the attrlist buffer doesn't have enough room to hold the entire string (including
203  * NUL terminator), then copy as much as will fit.  The attrlist buffer's "varcursor"
204  * will always be updated based on the entire length of the string (including NUL
205  * terminator); this means "varcursor" may end up pointing beyond the end of the
206  * allocated buffer space.
207  */
208 static void
attrlist_pack_string(struct _attrlist_buf * ab,const char * source,size_t count)209 attrlist_pack_string(struct _attrlist_buf *ab, const char *source, size_t count)
210 {
211 	struct attrreference ar;
212 	ssize_t fit, space;
213 
214 	/*
215 	 * Supplied count is character count of string text, excluding trailing nul
216 	 * which we always supply here.
217 	 */
218 	if (source == NULL) {
219 		count = 0;
220 	} else if (count == 0) {
221 		count = strlen(source);
222 	}
223 
224 	/*
225 	 * Construct the fixed-width attribute that refers to this string.
226 	 */
227 	ar.attr_dataoffset = (int32_t)(ab->varcursor - ab->fixedcursor);
228 	ar.attr_length = (u_int32_t)count + 1;
229 	attrlist_pack_fixed(ab, &ar, sizeof(ar));
230 
231 	/*
232 	 * Now compute how much available memory we have to copy the string text.
233 	 *
234 	 * space = the number of bytes available in the attribute buffer to hold the
235 	 *         string's value.
236 	 *
237 	 * fit = the number of bytes to copy from the start of the string into the
238 	 *       attribute buffer, NOT including the NUL terminator.  If the attribute
239 	 *       buffer is large enough, this will be the string's length; otherwise, it
240 	 *       will be equal to "space".
241 	 */
242 	space = ab->allocated - (ab->varcursor - ab->base);
243 	fit = lmin(count, space);
244 	if (space > 0) {
245 		long bytes_to_zero;
246 
247 		/*
248 		 * If there is space remaining, copy data in, and
249 		 * accommodate the trailing NUL terminator.
250 		 *
251 		 * NOTE: if "space" is too small to hold the string and its NUL
252 		 * terminator (space < fit + 1), then the string value in the attribute
253 		 * buffer will NOT be NUL terminated!
254 		 *
255 		 * NOTE 2: bcopy() will do nothing if the length ("fit") is zero.
256 		 * Therefore, we don't bother checking for that here.
257 		 */
258 		bcopy(source, ab->varcursor, fit);
259 		/* is there room for our trailing nul? */
260 		if (space > fit) {
261 			ab->varcursor[fit++] = '\0';
262 			/* 'fit' now the number of bytes AFTER adding in the NUL */
263 			/*
264 			 * Zero out any additional bytes we might have as a
265 			 * result of rounding up.
266 			 */
267 			bytes_to_zero = lmin((roundup(fit, 4) - fit),
268 			    space - fit);
269 			if (bytes_to_zero) {
270 				bzero(&(ab->varcursor[fit]), bytes_to_zero);
271 			}
272 		}
273 	}
274 	/*
275 	 * always move in increments of 4 (including the trailing NUL)
276 	 */
277 	ab->varcursor += roundup((count + 1), 4);
278 }
279 
280 #define ATTR_PACK4(AB, V)                                                 \
281 	do {                                                              \
282 	        if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) {   \
283 	                *(uint32_t *)AB.fixedcursor = V;                  \
284 	                AB.fixedcursor += 4;                              \
285 	        }                                                         \
286 	} while (0)
287 
288 #define ATTR_PACK8(AB, V)                                                 \
289 	do {                                                              \
290 	        if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) {   \
291 	                memcpy(AB.fixedcursor, &V, 8);                    \
292 	                AB.fixedcursor += 8;                              \
293 	        }                                                         \
294 	} while (0)
295 
296 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
297 #define ATTR_PACK_CAST(b, t, v)                                         \
298 	do {                                                            \
299 	        t _f = (t)v;                                            \
300 	        ATTR_PACK(b, _f);                                       \
301 	} while (0)
302 
303 #define ATTR_PACK_TIME(b, v, is64)                                                      \
304 	do {                                                                            \
305 	        if (is64) {                                                             \
306 	                struct user64_timespec us = {.tv_sec = v.tv_sec, .tv_nsec = v.tv_nsec};         \
307 	                ATTR_PACK(&b, us);                                              \
308 	        } else {                                                                \
309 	                struct user32_timespec us = {.tv_sec = (user32_time_t)v.tv_sec, .tv_nsec = (user32_long_t)v.tv_nsec};         \
310 	                ATTR_PACK(&b, us);                                              \
311 	        }                                                                       \
312 	} while(0)
313 
314 
315 /*
316  * Table-driven setup for all valid common/volume attributes.
317  */
318 struct getvolattrlist_attrtab {
319 	attrgroup_t     attr;
320 	uint64_t        bits;
321 #define VFSATTR_BIT(b)  (VFSATTR_ ## b)
322 	ssize_t         size;
323 };
324 static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = {
325 	{.attr = ATTR_CMN_NAME, .bits = 0, .size = sizeof(struct attrreference)},
326 	{.attr = ATTR_CMN_DEVID, .bits = 0, .size = sizeof(dev_t)},
327 	{.attr = ATTR_CMN_FSID, .bits = 0, .size = sizeof(fsid_t)},
328 	{.attr = ATTR_CMN_OBJTYPE, .bits = 0, .size = sizeof(fsobj_type_t)},
329 	{.attr = ATTR_CMN_OBJTAG, .bits = 0, .size = sizeof(fsobj_tag_t)},
330 	{.attr = ATTR_CMN_OBJID, .bits = 0, .size = sizeof(fsobj_id_t)},
331 	{.attr = ATTR_CMN_OBJPERMANENTID, .bits = 0, .size = sizeof(fsobj_id_t)},
332 	{.attr = ATTR_CMN_PAROBJID, .bits = 0, .size = sizeof(fsobj_id_t)},
333 	{.attr = ATTR_CMN_SCRIPT, .bits = 0, .size = sizeof(text_encoding_t)},
334 	{.attr = ATTR_CMN_CRTIME, .bits = VFSATTR_BIT(f_create_time), .size = ATTR_TIME_SIZE},
335 	{.attr = ATTR_CMN_MODTIME, .bits = VFSATTR_BIT(f_modify_time), .size = ATTR_TIME_SIZE},
336 	{.attr = ATTR_CMN_CHGTIME, .bits = VFSATTR_BIT(f_modify_time), .size = ATTR_TIME_SIZE},
337 	{.attr = ATTR_CMN_ACCTIME, .bits = VFSATTR_BIT(f_access_time), .size = ATTR_TIME_SIZE},
338 	{.attr = ATTR_CMN_BKUPTIME, .bits = VFSATTR_BIT(f_backup_time), .size = ATTR_TIME_SIZE},
339 	{.attr = ATTR_CMN_FNDRINFO, .bits = 0, .size = 32},
340 	{.attr = ATTR_CMN_OWNERID, .bits = 0, .size = sizeof(uid_t)},
341 	{.attr = ATTR_CMN_GRPID, .bits = 0, .size = sizeof(gid_t)},
342 	{.attr = ATTR_CMN_ACCESSMASK, .bits = 0, .size = sizeof(uint32_t)},
343 	{.attr = ATTR_CMN_FLAGS, .bits = 0, .size = sizeof(uint32_t)},
344 	{.attr = ATTR_CMN_USERACCESS, .bits = 0, .size = sizeof(uint32_t)},
345 	{.attr = ATTR_CMN_EXTENDED_SECURITY, .bits = 0, .size = sizeof(struct attrreference)},
346 	{.attr = ATTR_CMN_UUID, .bits = 0, .size = sizeof(guid_t)},
347 	{.attr = ATTR_CMN_GRPUUID, .bits = 0, .size = sizeof(guid_t)},
348 	{.attr = ATTR_CMN_FILEID, .bits = 0, .size = sizeof(uint64_t)},
349 	{.attr = ATTR_CMN_PARENTID, .bits = 0, .size = sizeof(uint64_t)},
350 	{.attr = ATTR_CMN_RETURNED_ATTRS, .bits = 0, .size = sizeof(attribute_set_t)},
351 	{.attr = ATTR_CMN_ERROR, .bits = 0, .size = sizeof(uint32_t)},
352 	{.attr = 0, .bits = 0, .size = 0}
353 };
354 #define ATTR_CMN_VOL_INVALID \
355 	(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID | \
356 	 ATTR_CMN_FILEID | ATTR_CMN_PARENTID)
357 
358 static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
359 	{.attr = ATTR_VOL_FSTYPE, .bits = 0, .size = sizeof(uint32_t)},
360 	{.attr = ATTR_VOL_SIGNATURE, .bits = VFSATTR_BIT(f_signature), .size = sizeof(uint32_t)},
361 	{.attr = ATTR_VOL_SIZE, .bits = VFSATTR_BIT(f_blocks)  |  VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
362 	{.attr = ATTR_VOL_SPACEFREE, .bits = VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
363 	{.attr = ATTR_VOL_SPACEAVAIL, .bits = VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
364 	{.attr = ATTR_VOL_MINALLOCATION, .bits = VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
365 	{.attr = ATTR_VOL_ALLOCATIONCLUMP, .bits = VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
366 	{.attr = ATTR_VOL_IOBLOCKSIZE, .bits = VFSATTR_BIT(f_iosize), .size = sizeof(uint32_t)},
367 	{.attr = ATTR_VOL_OBJCOUNT, .bits = VFSATTR_BIT(f_objcount), .size = sizeof(uint32_t)},
368 	{.attr = ATTR_VOL_FILECOUNT, .bits = VFSATTR_BIT(f_filecount), .size = sizeof(uint32_t)},
369 	{.attr = ATTR_VOL_DIRCOUNT, .bits = VFSATTR_BIT(f_dircount), .size = sizeof(uint32_t)},
370 	{.attr = ATTR_VOL_MAXOBJCOUNT, .bits = VFSATTR_BIT(f_maxobjcount), .size = sizeof(uint32_t)},
371 	{.attr = ATTR_VOL_MOUNTPOINT, .bits = 0, .size = sizeof(struct attrreference)},
372 	{.attr = ATTR_VOL_NAME, .bits = VFSATTR_BIT(f_vol_name), .size = sizeof(struct attrreference)},
373 	{.attr = ATTR_VOL_MOUNTFLAGS, .bits = 0, .size = sizeof(uint32_t)},
374 	{.attr = ATTR_VOL_MOUNTEDDEVICE, .bits = 0, .size = sizeof(struct attrreference)},
375 	{.attr = ATTR_VOL_ENCODINGSUSED, .bits = 0, .size = sizeof(uint64_t)},
376 	{.attr = ATTR_VOL_CAPABILITIES, .bits = VFSATTR_BIT(f_capabilities), .size = sizeof(vol_capabilities_attr_t)},
377 	{.attr = ATTR_VOL_UUID, .bits = VFSATTR_BIT(f_uuid), .size = sizeof(uuid_t)},
378 	{.attr = ATTR_VOL_SPACEUSED, .bits = VFSATTR_BIT(f_bused) | VFSATTR_BIT(f_bsize) | VFSATTR_BIT(f_bfree), .size = sizeof(off_t)},
379 	{.attr = ATTR_VOL_QUOTA_SIZE, .bits = VFSATTR_BIT(f_quota) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
380 	{.attr = ATTR_VOL_RESERVED_SIZE, .bits = VFSATTR_BIT(f_reserved) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
381 	{.attr = ATTR_VOL_ATTRIBUTES, .bits = VFSATTR_BIT(f_attributes), .size = sizeof(vol_attributes_attr_t)},
382 	{.attr = ATTR_VOL_INFO, .bits = 0, .size = 0},
383 	{.attr = 0, .bits = 0, .size = 0}
384 };
385 
386 static int
getvolattrlist_parsetab(struct getvolattrlist_attrtab * tab,attrgroup_t attrs,struct vfs_attr * vsp,ssize_t * sizep,int is_64bit,unsigned int maxiter)387 getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, struct vfs_attr *vsp,
388     ssize_t *sizep, int is_64bit, unsigned int maxiter)
389 {
390 	attrgroup_t     recognised;
391 
392 	recognised = 0;
393 	do {
394 		/* is this attribute set? */
395 		if (tab->attr & attrs) {
396 			recognised |= tab->attr;
397 			vsp->f_active |= tab->bits;
398 			if (tab->size == ATTR_TIME_SIZE) {
399 				if (is_64bit) {
400 					*sizep += sizeof(struct user64_timespec);
401 				} else {
402 					*sizep += sizeof(struct user32_timespec);
403 				}
404 			} else {
405 				*sizep += tab->size;
406 			}
407 		}
408 	} while (((++tab)->attr != 0) && (--maxiter > 0));
409 
410 	/* check to make sure that we recognised all of the passed-in attributes */
411 	if (attrs & ~recognised) {
412 		return EINVAL;
413 	}
414 	return 0;
415 }
416 
417 /*
418  * Given the attributes listed in alp, configure vap to request
419  * the data from a filesystem.
420  */
421 static int
getvolattrlist_setupvfsattr(struct attrlist * alp,struct vfs_attr * vsp,ssize_t * sizep,int is_64bit)422 getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
423 {
424 	int     error;
425 	if (!alp) {
426 		return EINVAL;
427 	}
428 
429 	/*
430 	 * Parse the above tables.
431 	 */
432 	*sizep = sizeof(uint32_t);      /* length count */
433 	if (alp->commonattr) {
434 		if ((alp->commonattr & ATTR_CMN_VOL_INVALID) &&
435 		    (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) == 0) {
436 			return EINVAL;
437 		}
438 		if ((error = getvolattrlist_parsetab(getvolattrlist_common_tab,
439 		    alp->commonattr, vsp, sizep,
440 		    is_64bit,
441 		    sizeof(getvolattrlist_common_tab) / sizeof(getvolattrlist_common_tab[0]))) != 0) {
442 			return error;
443 		}
444 	}
445 	if (alp->volattr &&
446 	    (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit, sizeof(getvolattrlist_vol_tab) / sizeof(getvolattrlist_vol_tab[0]))) != 0) {
447 		return error;
448 	}
449 
450 	return 0;
451 }
452 
453 /*
454  * Given the attributes listed in asp and those supported
455  * in the vsp, fixup the asp attributes to reflect any
456  * missing attributes from the file system
457  */
458 static void
getvolattrlist_fixupattrs(attribute_set_t * asp,struct vfs_attr * vsp)459 getvolattrlist_fixupattrs(attribute_set_t *asp, struct vfs_attr *vsp)
460 {
461 	struct getvolattrlist_attrtab *tab;
462 
463 	if (asp->commonattr) {
464 		tab = getvolattrlist_common_tab;
465 		do {
466 			if ((tab->attr & asp->commonattr) &&
467 			    (tab->bits != 0) &&
468 			    ((tab->bits & vsp->f_supported) == 0)) {
469 				asp->commonattr &= ~tab->attr;
470 			}
471 		} while ((++tab)->attr != 0);
472 	}
473 	if (asp->volattr) {
474 		tab = getvolattrlist_vol_tab;
475 		do {
476 			if ((tab->attr & asp->volattr) &&
477 			    (tab->bits != 0) &&
478 			    ((tab->bits & vsp->f_supported) == 0)) {
479 				asp->volattr &= ~tab->attr;
480 			}
481 		} while ((++tab)->attr != 0);
482 	}
483 }
484 
485 /*
486  * Table-driven setup for all valid common/dir/file/fork attributes against files.
487  */
488 struct getattrlist_attrtab {
489 	attrgroup_t     attr;
490 	uint64_t        bits;
491 #define VATTR_BIT(b)    (VNODE_ATTR_ ## b)
492 	ssize_t         size;
493 	kauth_action_t  action;
494 };
495 
496 /*
497  * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this
498  * information, and we will synthesize it at the VFS level.
499  */
500 static struct getattrlist_attrtab getattrlist_common_tab[] = {
501 	{.attr = ATTR_CMN_NAME, .bits = VATTR_BIT(va_name), .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
502 	{.attr = ATTR_CMN_DEVID, .bits = VATTR_BIT(va_fsid), .size = sizeof(dev_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
503 	{.attr = ATTR_CMN_FSID, .bits = VATTR_BIT(va_fsid64), .size = sizeof(fsid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
504 	{.attr = ATTR_CMN_OBJTYPE, .bits = 0, .size = sizeof(fsobj_type_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
505 	{.attr = ATTR_CMN_OBJTAG, .bits = 0, .size = sizeof(fsobj_tag_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
506 	{.attr = ATTR_CMN_OBJID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
507 	{.attr = ATTR_CMN_OBJPERMANENTID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
508 	{.attr = ATTR_CMN_PAROBJID, .bits = VATTR_BIT(va_parentid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
509 	{.attr = ATTR_CMN_SCRIPT, .bits = VATTR_BIT(va_encoding), .size = sizeof(text_encoding_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
510 	{.attr = ATTR_CMN_CRTIME, .bits = VATTR_BIT(va_create_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
511 	{.attr = ATTR_CMN_MODTIME, .bits = VATTR_BIT(va_modify_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
512 	{.attr = ATTR_CMN_CHGTIME, .bits = VATTR_BIT(va_change_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
513 	{.attr = ATTR_CMN_ACCTIME, .bits = VATTR_BIT(va_access_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
514 	{.attr = ATTR_CMN_BKUPTIME, .bits = VATTR_BIT(va_backup_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
515 	{.attr = ATTR_CMN_FNDRINFO, .bits = 0, .size = 32, .action = KAUTH_VNODE_READ_ATTRIBUTES},
516 	{.attr = ATTR_CMN_OWNERID, .bits = VATTR_BIT(va_uid), .size = sizeof(uid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
517 	{.attr = ATTR_CMN_GRPID, .bits = VATTR_BIT(va_gid), .size = sizeof(gid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
518 	{.attr = ATTR_CMN_ACCESSMASK, .bits = VATTR_BIT(va_mode), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
519 	{.attr = ATTR_CMN_FLAGS, .bits = VATTR_BIT(va_flags), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
520 	{.attr = ATTR_CMN_GEN_COUNT, .bits = VATTR_BIT(va_write_gencount), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
521 	{.attr = ATTR_CMN_DOCUMENT_ID, .bits = VATTR_BIT(va_document_id), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
522 	{.attr = ATTR_CMN_USERACCESS, .bits = 0, .size = sizeof(uint32_t), .action = 0},
523 	{.attr = ATTR_CMN_EXTENDED_SECURITY, .bits = VATTR_BIT(va_acl), .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_SECURITY},
524 	{.attr = ATTR_CMN_UUID, .bits = VATTR_BIT(va_uuuid), .size = sizeof(guid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
525 	{.attr = ATTR_CMN_GRPUUID, .bits = VATTR_BIT(va_guuid), .size = sizeof(guid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
526 	{.attr = ATTR_CMN_FILEID, .bits = VATTR_BIT(va_fileid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
527 	{.attr = ATTR_CMN_PARENTID, .bits = VATTR_BIT(va_parentid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
528 	{.attr = ATTR_CMN_FULLPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
529 	{.attr = ATTR_CMN_ADDEDTIME, .bits = VATTR_BIT(va_addedtime), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
530 	{.attr = ATTR_CMN_RETURNED_ATTRS, .bits = 0, .size = sizeof(attribute_set_t), .action = 0},
531 	{.attr = ATTR_CMN_ERROR, .bits = 0, .size = sizeof(uint32_t), .action = 0},
532 	{.attr = ATTR_CMN_DATA_PROTECT_FLAGS, .bits = VATTR_BIT(va_dataprotect_class), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
533 	{.attr = 0, .bits = 0, .size = 0, .action = 0}
534 };
535 
536 static struct getattrlist_attrtab getattrlist_dir_tab[] = {
537 	{.attr = ATTR_DIR_LINKCOUNT, .bits = VATTR_BIT(va_dirlinkcount), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
538 	{.attr = ATTR_DIR_ENTRYCOUNT, .bits = VATTR_BIT(va_nchildren), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
539 	{.attr = ATTR_DIR_MOUNTSTATUS, .bits = 0, .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
540 	{.attr = ATTR_DIR_ALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
541 	{.attr = ATTR_DIR_IOBLOCKSIZE, .bits = VATTR_BIT(va_iosize), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
542 	{.attr = ATTR_DIR_DATALENGTH, .bits = VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
543 	{.attr = 0, .bits = 0, .size = 0, .action = 0}
544 };
545 static struct getattrlist_attrtab getattrlist_file_tab[] = {
546 	{.attr = ATTR_FILE_LINKCOUNT, .bits = VATTR_BIT(va_nlink), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
547 	{.attr = ATTR_FILE_TOTALSIZE, .bits = VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
548 	{.attr = ATTR_FILE_ALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
549 	{.attr = ATTR_FILE_IOBLOCKSIZE, .bits = VATTR_BIT(va_iosize), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
550 	{.attr = ATTR_FILE_CLUMPSIZE, .bits = 0, .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
551 	{.attr = ATTR_FILE_DEVTYPE, .bits = VATTR_BIT(va_rdev), .size = sizeof(dev_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
552 	{.attr = ATTR_FILE_DATALENGTH, .bits = VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
553 	{.attr = ATTR_FILE_DATAALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_data_alloc), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
554 	{.attr = ATTR_FILE_RSRCLENGTH, .bits = 0, .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
555 	{.attr = ATTR_FILE_RSRCALLOCSIZE, .bits = 0, .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
556 	{.attr = 0, .bits = 0, .size = 0, .action = 0}
557 };
558 
559 //for forkattr bits repurposed as new common attributes
560 static struct getattrlist_attrtab getattrlist_common_extended_tab[] = {
561 	{.attr = ATTR_CMNEXT_RELPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
562 	{.attr = ATTR_CMNEXT_PRIVATESIZE, .bits = VATTR_BIT(va_private_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
563 	{.attr = ATTR_CMNEXT_LINKID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
564 	{.attr = ATTR_CMNEXT_NOFIRMLINKPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
565 	{.attr = ATTR_CMNEXT_REALDEVID, .bits = VATTR_BIT(va_devid), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
566 	{.attr = ATTR_CMNEXT_REALFSID, .bits = VATTR_BIT(va_fsid64), .size = sizeof(fsid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
567 	{.attr = ATTR_CMNEXT_CLONEID, .bits = VATTR_BIT(va_clone_id), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
568 	{.attr = ATTR_CMNEXT_EXT_FLAGS, .bits = VATTR_BIT(va_extflags), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
569 	{.attr = ATTR_CMNEXT_RECURSIVE_GENCOUNT, .bits = VATTR_BIT(va_recursive_gencount), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
570 	{.attr = 0, .bits = 0, .size = 0, .action = 0}
571 };
572 
573 /*
574  * This table is for attributes which are only set from the getattrlistbulk(2)
575  * call. These attributes have already been set from the common, file and
576  * directory tables but the vattr bits have not been recorded. Since these
577  * vattr bits are only used from the bulk call, we have a seperate table for
578  * these.
579  * The sizes are not returned from here since the sizes have already been
580  * accounted from the common, file and directory tables.
581  */
582 static struct getattrlist_attrtab getattrlistbulk_common_tab[] = {
583 	{.attr = ATTR_CMN_DEVID, .bits = VATTR_BIT(va_devid), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
584 	{.attr = ATTR_CMN_FSID, .bits = VATTR_BIT(va_fsid64), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
585 	{.attr = ATTR_CMN_OBJTYPE, .bits = VATTR_BIT(va_objtype), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
586 	{.attr = ATTR_CMN_OBJTAG, .bits = VATTR_BIT(va_objtag), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
587 	{.attr = ATTR_CMN_USERACCESS, .bits = VATTR_BIT(va_user_access), .size = 0, .action = 0},
588 	{.attr = ATTR_CMN_FNDRINFO, .bits = VATTR_BIT(va_finderinfo), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
589 	{.attr = 0, .bits = 0, .size = 0, .action = 0}
590 };
591 
592 static struct getattrlist_attrtab getattrlistbulk_file_tab[] = {
593 	{.attr = ATTR_FILE_RSRCLENGTH, .bits = VATTR_BIT(va_rsrc_length), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
594 	{.attr = ATTR_FILE_RSRCALLOCSIZE, .bits = VATTR_BIT(va_rsrc_alloc), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
595 	{.attr = 0, .bits = 0, .size = 0, .action = 0}
596 };
597 
598 static struct getattrlist_attrtab getattrlistbulk_common_extended_tab[] = {
599 	/* getattrlist_parsetab() expects > 1 entries */
600 	{.attr = 0, .bits = 0, .size = 0, .action = 0},
601 	{.attr = 0, .bits = 0, .size = 0, .action = 0}
602 };
603 
604 /*
605  * The following are attributes that VFS can derive.
606  *
607  * A majority of them are the same attributes that are required for stat(2) and statfs(2).
608  */
609 #define VFS_DFLT_ATTR_VOL       (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE |  \
610 	                         ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE |  ATTR_VOL_QUOTA_SIZE | ATTR_VOL_RESERVED_SIZE | \
611 	                         ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION |  \
612 	                         ATTR_VOL_ALLOCATIONCLUMP |  ATTR_VOL_IOBLOCKSIZE |  \
613 	                         ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |  \
614 	                         ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |  \
615 	                         ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
616 
617 #define VFS_DFLT_ATTR_CMN       (ATTR_CMN_NAME | ATTR_CMN_DEVID |  \
618 	                         ATTR_CMN_FSID | ATTR_CMN_OBJTYPE |  \
619 	                         ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |  \
620 	                         ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT |  \
621 	                         ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME |  \
622 	                         ATTR_CMN_FNDRINFO |  \
623 	                         ATTR_CMN_OWNERID  | ATTR_CMN_GRPID |  \
624 	                         ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS |  \
625 	                         ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
626 	                         ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS | \
627 	                         ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT | \
628 	                         ATTR_CMN_DATA_PROTECT_FLAGS)
629 
630 #define VFS_DFLT_ATTR_CMN_EXT   (ATTR_CMNEXT_PRIVATESIZE | ATTR_CMNEXT_LINKID |  \
631 	                         ATTR_CMNEXT_NOFIRMLINKPATH | ATTR_CMNEXT_REALDEVID |  \
632 	                         ATTR_CMNEXT_REALFSID | ATTR_CMNEXT_CLONEID | \
633 	                         ATTR_CMNEXT_EXT_FLAGS)
634 
635 #define VFS_DFLT_ATTR_DIR       (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
636 
637 #define VFS_DFLT_ATTR_FILE      (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |  \
638 	                         ATTR_FILE_ALLOCSIZE  | ATTR_FILE_IOBLOCKSIZE |  \
639 	                         ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH |  \
640 	                         ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH |  \
641 	                         ATTR_FILE_RSRCALLOCSIZE)
642 
643 static int
getattrlist_parsetab(struct getattrlist_attrtab * tab,attrgroup_t attrs,struct vnode_attr * vap,ssize_t * sizep,kauth_action_t * actionp,int is_64bit,unsigned int maxiter)644 getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs,
645     struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp,
646     int is_64bit, unsigned int maxiter)
647 {
648 	attrgroup_t     recognised;
649 	recognised = 0;
650 	if (!tab) {
651 		return EINVAL;
652 	}
653 
654 	do {
655 		/* is this attribute set? */
656 		if (tab->attr & attrs) {
657 			recognised |= tab->attr;
658 			if (vap) {
659 				vap->va_active |= tab->bits;
660 			}
661 			if (sizep) {
662 				if (tab->size == ATTR_TIME_SIZE) {
663 					if (is_64bit) {
664 						*sizep += sizeof(
665 							struct user64_timespec);
666 					} else {
667 						*sizep += sizeof(
668 							struct user32_timespec);
669 					}
670 				} else {
671 					*sizep += tab->size;
672 				}
673 			}
674 			if (actionp) {
675 				*actionp |= tab->action;
676 			}
677 			if (attrs == recognised) {
678 				break;  /* all done, get out */
679 			}
680 		}
681 	} while (((++tab)->attr != 0) && (--maxiter > 0));
682 
683 	/* check to make sure that we recognised all of the passed-in attributes */
684 	if (attrs & ~recognised) {
685 		return EINVAL;
686 	}
687 	return 0;
688 }
689 
690 /*
691  * Given the attributes listed in alp, configure vap to request
692  * the data from a filesystem.
693  */
694 static int
getattrlist_setupvattr(struct attrlist * alp,struct vnode_attr * vap,ssize_t * sizep,kauth_action_t * actionp,int is_64bit,int isdir,int use_fork)695 getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir, int use_fork)
696 {
697 	int     error;
698 
699 	/*
700 	 * Parse the above tables.
701 	 */
702 	*sizep = sizeof(uint32_t);      /* length count */
703 	*actionp = 0;
704 	if (alp->commonattr &&
705 	    (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_common_tab) / sizeof(getattrlist_common_tab[0]))) != 0) {
706 		return error;
707 	}
708 	if (isdir && alp->dirattr &&
709 	    (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_dir_tab) / sizeof(getattrlist_dir_tab[0]))) != 0) {
710 		return error;
711 	}
712 	if (!isdir && alp->fileattr &&
713 	    (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_file_tab) / sizeof(getattrlist_file_tab[0]))) != 0) {
714 		return error;
715 	}
716 	if (use_fork && alp->forkattr &&
717 	    (error = getattrlist_parsetab(getattrlist_common_extended_tab, alp->forkattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_common_extended_tab) / sizeof(getattrlist_common_extended_tab[0]))) != 0) {
718 		return error;
719 	}
720 
721 	return 0;
722 }
723 
724 /*
725  * Given the attributes listed in alp, configure vap to request
726  * the data from a filesystem.
727  */
728 static int
getattrlist_setupvattr_all(struct attrlist * alp,struct vnode_attr * vap,enum vtype obj_type,ssize_t * fixedsize,int is_64bit,int use_fork)729 getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap,
730     enum vtype obj_type, ssize_t *fixedsize, int is_64bit, int use_fork)
731 {
732 	int     error = 0;
733 
734 	/*
735 	 * Parse the above tables.
736 	 */
737 	if (fixedsize) {
738 		*fixedsize = sizeof(uint32_t);
739 	}
740 	if (alp->commonattr) {
741 		error = getattrlist_parsetab(getattrlist_common_tab,
742 		    alp->commonattr, vap, fixedsize, NULL, is_64bit,
743 		    sizeof(getattrlist_common_tab) / sizeof(getattrlist_common_tab[0]));
744 
745 		if (!error) {
746 			/* Ignore any errrors from the bulk table */
747 			(void)getattrlist_parsetab(getattrlistbulk_common_tab,
748 			    alp->commonattr, vap, fixedsize, NULL, is_64bit,
749 			    sizeof(getattrlistbulk_common_tab) / sizeof(getattrlistbulk_common_tab[0]));
750 		}
751 	}
752 
753 	if (!error && (obj_type == VNON || obj_type == VDIR) && alp->dirattr) {
754 		error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr,
755 		    vap, fixedsize, NULL, is_64bit,
756 		    sizeof(getattrlist_dir_tab) / sizeof(getattrlist_dir_tab[0]));
757 	}
758 
759 	if (!error && (obj_type != VDIR) && alp->fileattr) {
760 		error = getattrlist_parsetab(getattrlist_file_tab,
761 		    alp->fileattr, vap, fixedsize, NULL, is_64bit,
762 		    sizeof(getattrlist_file_tab) / sizeof(getattrlist_file_tab[0]));
763 
764 		if (!error) {
765 			/*Ignore any errors from the bulk table */
766 			(void)getattrlist_parsetab(getattrlistbulk_file_tab,
767 			    alp->fileattr, vap, fixedsize, NULL, is_64bit,
768 			    sizeof(getattrlistbulk_file_tab) / sizeof(getattrlistbulk_file_tab[0]));
769 		}
770 	}
771 
772 	/* fork attributes are like extended common attributes if enabled*/
773 	if (!error && use_fork && alp->forkattr) {
774 		error = getattrlist_parsetab(getattrlist_common_extended_tab,
775 		    alp->forkattr, vap, fixedsize, NULL, is_64bit,
776 		    sizeof(getattrlist_common_extended_tab) / sizeof(getattrlist_common_extended_tab[0]));
777 
778 		if (!error) {
779 			(void)getattrlist_parsetab(getattrlistbulk_common_extended_tab,
780 			    alp->forkattr, vap, fixedsize, NULL, is_64bit,
781 			    sizeof(getattrlistbulk_common_extended_tab) / sizeof(getattrlistbulk_common_extended_tab[0]));
782 		}
783 	}
784 
785 	return error;
786 }
787 
788 int
vfs_setup_vattr_from_attrlist(struct attrlist * alp,struct vnode_attr * vap,enum vtype obj_vtype,ssize_t * attrs_fixed_sizep,vfs_context_t ctx)789 vfs_setup_vattr_from_attrlist(struct attrlist *alp, struct vnode_attr *vap,
790     enum vtype obj_vtype, ssize_t *attrs_fixed_sizep, vfs_context_t ctx)
791 {
792 	VATTR_INIT(vap);
793 
794 	// the caller passes us no options, we assume the caller wants the new fork
795 	// attr behavior, hence the hardcoded 1
796 	return getattrlist_setupvattr_all(alp, vap, obj_vtype,
797 	           attrs_fixed_sizep, vfs_context_is64bit(ctx), 1);
798 }
799 
800 
801 
802 
803 /*
804  * Given the attributes listed in asp and those supported
805  * in the vap, fixup the asp attributes to reflect any
806  * missing attributes from the file system
807  */
808 static void
getattrlist_fixupattrs(attribute_set_t * asp,struct vnode_attr * vap,int use_fork)809 getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap, int use_fork)
810 {
811 	struct getattrlist_attrtab *tab;
812 
813 	if (asp->commonattr) {
814 		tab = getattrlist_common_tab;
815 		do {
816 			/*
817 			 * This if() statement is slightly confusing. We're trying to
818 			 * iterate through all of the bits listed in the array
819 			 * getattr_common_tab, and see if the filesystem was expected
820 			 * to support it, and whether or not we need to do anything about this.
821 			 *
822 			 * This array is full of structs that have 4 fields (attr, bits, size, action).
823 			 * The first is used to store the ATTR_CMN_* bit that was being requested
824 			 * from userland.  The second stores the VATTR_BIT corresponding to the field
825 			 * filled in vnode_attr struct.  If it is 0, then we don't typically expect
826 			 * the filesystem to fill in this field.  The third is the size of the field,
827 			 * and the fourth is the type of kauth actions needed.
828 			 *
829 			 * So, for all of the ATTR_CMN bits listed in this array, we iterate through
830 			 * them, and check to see if it was both passed down to the filesystem via the
831 			 * va_active bitfield, and whether or not we expect it to be emitted from
832 			 * the filesystem.  If it wasn't supported, then we un-twiddle the bit and move
833 			 * on.  This is done so that we can uncheck those bits and re-request
834 			 * a vnode_getattr from the filesystem again.
835 			 */
836 			if ((tab->attr & asp->commonattr) &&
837 			    (tab->bits & vap->va_active) &&
838 			    (tab->bits & vap->va_supported) == 0) {
839 				asp->commonattr &= ~tab->attr;
840 			}
841 		} while ((++tab)->attr != 0);
842 	}
843 	if (asp->dirattr) {
844 		tab = getattrlist_dir_tab;
845 		do {
846 			if ((tab->attr & asp->dirattr) &&
847 			    (tab->bits & vap->va_active) &&
848 			    (vap->va_supported & tab->bits) == 0) {
849 				asp->dirattr &= ~tab->attr;
850 			}
851 		} while ((++tab)->attr != 0);
852 	}
853 	if (asp->fileattr) {
854 		tab = getattrlist_file_tab;
855 		do {
856 			if ((tab->attr & asp->fileattr) &&
857 			    (tab->bits & vap->va_active) &&
858 			    (vap->va_supported & tab->bits) == 0) {
859 				asp->fileattr &= ~tab->attr;
860 			}
861 		} while ((++tab)->attr != 0);
862 	}
863 	if (use_fork && asp->forkattr) {
864 		tab = getattrlist_common_extended_tab;
865 		do {
866 			if ((tab->attr & asp->forkattr) &&
867 			    (tab->bits & vap->va_active) &&
868 			    (vap->va_supported & tab->bits) == 0) {
869 				asp->forkattr &= ~tab->attr;
870 			}
871 		} while ((++tab)->attr != 0);
872 	}
873 }
874 
875 static int
setattrlist_setfinderinfo(vnode_t vp,char * fndrinfo,struct vfs_context * ctx)876 setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
877 {
878 	uio_t   auio;
879 	uio_stackbuf_t    uio_buf[UIO_SIZEOF(1)];
880 	int     error;
881 
882 	if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
883 		error = ENOMEM;
884 	} else {
885 		uio_addiov(auio, CAST_USER_ADDR_T(fndrinfo), 32);
886 		error = vn_setxattr(vp, XATTR_FINDERINFO_NAME, auio, XATTR_NOSECURITY, ctx);
887 		uio_free(auio);
888 	}
889 
890 #if CONFIG_FSE
891 	if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) {
892 		add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
893 	}
894 #endif
895 	return error;
896 }
897 
898 
899 /*
900  * Find something resembling a terminal component name in the mountedonname for vp
901  *
902  */
903 static void
getattrlist_findnamecomp(const char * mn,const char ** np,ssize_t * nl)904 getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
905 {
906 	int             counting;
907 	const char      *cp;
908 
909 	/*
910 	 * We're looking for the last sequence of non / characters, but
911 	 * not including any trailing / characters.
912 	 */
913 	*np = NULL;
914 	*nl = 0;
915 	counting = 0;
916 	for (cp = mn; *cp != 0; cp++) {
917 		if (!counting) {
918 			/* start of run of chars */
919 			if (*cp != '/') {
920 				*np = cp;
921 				counting = 1;
922 			}
923 		} else {
924 			/* end of run of chars */
925 			if (*cp == '/') {
926 				*nl = cp - *np;
927 				counting = 0;
928 			}
929 		}
930 	}
931 	/* need to close run? */
932 	if (counting) {
933 		*nl = cp - *np;
934 	}
935 }
936 
937 
938 static int
getvolattrlist(vfs_context_t ctx,vnode_t vp,struct attrlist * alp,user_addr_t attributeBuffer,size_t bufferSize,uint64_t options,enum uio_seg segflg,int is_64bit)939 getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
940     user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
941     enum uio_seg segflg, int is_64bit)
942 {
943 	struct vfs_attr vs = {};
944 	struct vnode_attr va;
945 	struct _attrlist_buf ab;
946 	int             error;
947 	ssize_t         fixedsize, varsize;
948 	const char      *cnp = NULL;    /* protected by ATTR_CMN_NAME */
949 	ssize_t         cnl = 0;        /* protected by ATTR_CMN_NAME */
950 	int             release_str = 0;
951 	mount_t         mnt;
952 	int             return_valid;
953 	int             pack_invalid;
954 	vnode_t         root_vp = NULL;
955 
956 	_ATTRLIST_BUF_INIT(&ab);
957 	VATTR_INIT(&va);
958 	VFSATTR_INIT(&vs);
959 	vs.f_vol_name = NULL;
960 	mnt = vp->v_mount;
961 
962 
963 	/* Check for special packing semantics */
964 	return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS);
965 	pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS);
966 	if (pack_invalid) {
967 		/* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
968 		if (!return_valid) {
969 			error = EINVAL;
970 			goto out;
971 		}
972 		/* Keep invalid attrs from being uninitialized */
973 		bzero(&vs, sizeof(vs));
974 		/* Generate a valid mask for post processing */
975 		bcopy(&alp->commonattr, &ab.valid, sizeof(attribute_set_t));
976 	}
977 
978 	/* If we do not have root vnode, look it up and substitute it in */
979 	if (!vnode_isvroot(vp)) {
980 		if (mnt != NULL) {
981 			error = VFS_ROOT(mnt, &root_vp, ctx);
982 			if (error) {
983 				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but got an error getting root.");
984 				goto out;
985 			}
986 			vp = root_vp;
987 		} else {
988 			error = EINVAL;
989 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but no backpointer to mount.");
990 			goto out;
991 		}
992 	}
993 
994 	/*
995 	 * Set up the vfs_attr structure and call the filesystem.
996 	 */
997 	if ((error = getvolattrlist_setupvfsattr(alp, &vs, &fixedsize, is_64bit)) != 0) {
998 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
999 		goto out;
1000 	}
1001 	if (vs.f_active != 0) {
1002 		/* If we're going to ask for f_vol_name, allocate a buffer to point it at */
1003 		if (VFSATTR_IS_ACTIVE(&vs, f_vol_name)) {
1004 			vs.f_vol_name = (char *) zalloc(ZV_NAMEI);
1005 			vs.f_vol_name[0] = '\0';
1006 		}
1007 
1008 		VFS_DEBUG(ctx, vp, "ATTRLIST -       calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported);
1009 		if ((error = vfs_getattr(mnt, &vs, ctx)) != 0) {
1010 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
1011 			goto out;
1012 		}
1013 #if CONFIG_MACF
1014 		error = mac_mount_check_getattr(ctx, mnt, &vs);
1015 		if (error != 0) {
1016 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d", error);
1017 			goto out;
1018 		}
1019 #endif
1020 		/*
1021 		 * Did we ask for something the filesystem doesn't support?
1022 		 */
1023 		if (!VFSATTR_ALL_SUPPORTED(&vs)) {
1024 			/* default value for volume subtype */
1025 			if (VFSATTR_IS_ACTIVE(&vs, f_fssubtype)
1026 			    && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype)) {
1027 				VFSATTR_RETURN(&vs, f_fssubtype, 0);
1028 			}
1029 
1030 			/*
1031 			 * If the file system didn't supply f_signature, then
1032 			 * default it to 'BD', which is the generic signature
1033 			 * that most Carbon file systems should return.
1034 			 */
1035 			if (VFSATTR_IS_ACTIVE(&vs, f_signature)
1036 			    && !VFSATTR_IS_SUPPORTED(&vs, f_signature)) {
1037 				VFSATTR_RETURN(&vs, f_signature, 0x4244);
1038 			}
1039 
1040 			/* default for block size */
1041 			if (VFSATTR_IS_ACTIVE(&vs, f_bsize)
1042 			    && !VFSATTR_IS_SUPPORTED(&vs, f_bsize)) {
1043 				VFSATTR_RETURN(&vs, f_bsize, mnt->mnt_devblocksize);
1044 			}
1045 
1046 			/* default value for blocks used */
1047 			if (VFSATTR_IS_ACTIVE(&vs, f_bused)
1048 			    && !VFSATTR_IS_SUPPORTED(&vs, f_bused)) {
1049 				VFSATTR_RETURN(&vs, f_bused, mnt->mnt_vfsstat.f_blocks - vs.f_bfree);
1050 			}
1051 
1052 			/* default value for volume f_attributes */
1053 			if (VFSATTR_IS_ACTIVE(&vs, f_attributes)
1054 			    && !VFSATTR_IS_SUPPORTED(&vs, f_attributes)) {
1055 				vol_attributes_attr_t *attrp = &vs.f_attributes;
1056 
1057 				attrp->validattr.commonattr = VFS_DFLT_ATTR_CMN;
1058 				attrp->validattr.volattr = VFS_DFLT_ATTR_VOL;
1059 				attrp->validattr.dirattr = VFS_DFLT_ATTR_DIR;
1060 				attrp->validattr.fileattr = VFS_DFLT_ATTR_FILE;
1061 				attrp->validattr.forkattr = VFS_DFLT_ATTR_CMN_EXT;
1062 
1063 				attrp->nativeattr.commonattr =  0;
1064 				attrp->nativeattr.volattr = 0;
1065 				attrp->nativeattr.dirattr = 0;
1066 				attrp->nativeattr.fileattr = 0;
1067 				attrp->nativeattr.forkattr = 0;
1068 				VFSATTR_SET_SUPPORTED(&vs, f_attributes);
1069 			}
1070 
1071 			/* default value for volume f_capabilities */
1072 			if (VFSATTR_IS_ACTIVE(&vs, f_capabilities)) {
1073 				/* getattrlist is always supported now. */
1074 				if (!VFSATTR_IS_SUPPORTED(&vs, f_capabilities)) {
1075 					vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = 0;
1076 					vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
1077 					vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
1078 					vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
1079 
1080 					vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0;
1081 					vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
1082 					vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
1083 					vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
1084 					VFSATTR_SET_SUPPORTED(&vs, f_capabilities);
1085 				} else {
1086 					/* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
1087 					vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
1088 					vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
1089 				}
1090 			}
1091 
1092 			/* check to see if our fixups were enough */
1093 			if (!VFSATTR_ALL_SUPPORTED(&vs)) {
1094 				if (return_valid) {
1095 					if (pack_invalid) {
1096 						/* Fix up valid mask for post processing */
1097 						getvolattrlist_fixupattrs(&ab.valid, &vs);
1098 
1099 						/* Force packing of everything asked for */
1100 						vs.f_supported = vs.f_active;
1101 					} else {
1102 						/* Adjust the requested attributes */
1103 						getvolattrlist_fixupattrs((attribute_set_t *)&alp->commonattr, &vs);
1104 					}
1105 				} else {
1106 					error = EINVAL;
1107 					goto out;
1108 				}
1109 			}
1110 		}
1111 	}
1112 
1113 	/*
1114 	 * Some fields require data from the root vp
1115 	 */
1116 	if (alp->commonattr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | ATTR_CMN_SCRIPT)) {
1117 		VATTR_WANTED(&va, va_uid);
1118 		VATTR_WANTED(&va, va_gid);
1119 		VATTR_WANTED(&va, va_mode);
1120 		VATTR_WANTED(&va, va_flags);
1121 		VATTR_WANTED(&va, va_encoding);
1122 
1123 		if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
1124 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp);
1125 			goto out;
1126 		}
1127 #if CONFIG_MACF
1128 		error = mac_vnode_check_getattr(ctx, NOCRED, vp, &va);
1129 		if (error != 0) {
1130 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d for root vnode", error);
1131 			goto out;
1132 		}
1133 #endif
1134 		if (VATTR_IS_ACTIVE(&va, va_encoding) &&
1135 		    !VATTR_IS_SUPPORTED(&va, va_encoding)) {
1136 			if (!return_valid || pack_invalid) {
1137 				/* use kTextEncodingMacUnicode */
1138 				VATTR_RETURN(&va, va_encoding, 0x7e);
1139 			} else {
1140 				/* don't use a default */
1141 				alp->commonattr &= ~ATTR_CMN_SCRIPT;
1142 			}
1143 		}
1144 	}
1145 
1146 	/*
1147 	 * Compute variable-size buffer requirements.
1148 	 */
1149 	varsize = 0;
1150 	if (alp->commonattr & ATTR_CMN_NAME) {
1151 		if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
1152 		    vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
1153 			/* special case for boot volume.  Use root name when it's
1154 			 * available (which is the volume name) or just the mount on
1155 			 * name of "/".  we must do this for binary compatibility with
1156 			 * pre Tiger code.  returning nothing for the boot volume name
1157 			 * breaks installers - 3961058
1158 			 */
1159 			cnp = vnode_getname(vp);
1160 			if (cnp == NULL) {
1161 				/* just use "/" as name */
1162 				cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
1163 			} else {
1164 				release_str = 1;
1165 			}
1166 			cnl = strlen(cnp);
1167 		} else {
1168 			getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
1169 		}
1170 		if (alp->commonattr & ATTR_CMN_NAME) {
1171 			varsize += roundup(cnl + 1, 4);
1172 		}
1173 	}
1174 	if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
1175 		varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntonname) + 1, 4);
1176 	}
1177 	if (alp->volattr & ATTR_VOL_NAME) {
1178 		vs.f_vol_name[MAXPATHLEN - 1] = '\0'; /* Ensure nul-termination */
1179 		varsize += roundup(strlen(vs.f_vol_name) + 1, 4);
1180 	}
1181 	if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
1182 		varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4);
1183 	}
1184 
1185 	/*
1186 	 * Allocate a target buffer for attribute results.
1187 	 * Note that since we won't ever copy out more than the caller requested,
1188 	 * we never need to allocate more than they offer.
1189 	 */
1190 	ab.allocated = fixedsize + varsize;
1191 	if (((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
1192 		error = ENOMEM;
1193 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
1194 		goto out;
1195 	}
1196 
1197 	if (return_valid &&
1198 	    (ab.allocated < (ssize_t)(sizeof(uint32_t) + sizeof(attribute_set_t))) &&
1199 	    !(options & FSOPT_REPORT_FULLSIZE)) {
1200 		uint32_t num_bytes_valid = sizeof(uint32_t);
1201 		/*
1202 		 * Not enough to return anything and we don't have to report
1203 		 * how much space is needed. Get out now.
1204 		 * N.B. - We have only been called after having verified that
1205 		 * attributeBuffer is at least sizeof(uint32_t);
1206 		 */
1207 		if (UIO_SEG_IS_USER_SPACE(segflg)) {
1208 			error = copyout(&num_bytes_valid,
1209 			    CAST_USER_ADDR_T(attributeBuffer), num_bytes_valid);
1210 		} else {
1211 			bcopy(&num_bytes_valid, (void *)attributeBuffer,
1212 			    (size_t)num_bytes_valid);
1213 		}
1214 		goto out;
1215 	}
1216 
1217 	ab.base = kalloc_data(ab.allocated, Z_ZERO | Z_WAITOK);
1218 	if (ab.base == NULL) {
1219 		error = ENOMEM;
1220 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
1221 		goto out;
1222 	}
1223 
1224 	/*
1225 	 * Pack results into the destination buffer.
1226 	 */
1227 	ab.fixedcursor = ab.base + sizeof(uint32_t);
1228 	if (return_valid) {
1229 		ab.fixedcursor += sizeof(attribute_set_t);
1230 	}
1231 
1232 	bzero(&ab.actual, sizeof(ab.actual));
1233 
1234 	ab.varcursor = ab.base + fixedsize;
1235 	ab.needed = fixedsize + varsize;
1236 
1237 	/* common attributes **************************************************/
1238 	if (alp->commonattr & ATTR_CMN_ERROR) {
1239 		ATTR_PACK4(ab, 0);
1240 		ab.actual.commonattr |= ATTR_CMN_ERROR;
1241 	}
1242 	if (alp->commonattr & ATTR_CMN_NAME) {
1243 		attrlist_pack_string(&ab, cnp, cnl);
1244 		ab.actual.commonattr |= ATTR_CMN_NAME;
1245 	}
1246 	if (alp->commonattr & ATTR_CMN_DEVID) {
1247 		ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]);
1248 		ab.actual.commonattr |= ATTR_CMN_DEVID;
1249 	}
1250 	if (alp->commonattr & ATTR_CMN_FSID) {
1251 		ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid);
1252 		ab.actual.commonattr |= ATTR_CMN_FSID;
1253 	}
1254 	if (alp->commonattr & ATTR_CMN_OBJTYPE) {
1255 		if (!return_valid || pack_invalid) {
1256 			ATTR_PACK4(ab, 0);
1257 		}
1258 	}
1259 	if (alp->commonattr & ATTR_CMN_OBJTAG) {
1260 		ATTR_PACK4(ab, vp->v_tag);
1261 		ab.actual.commonattr |= ATTR_CMN_OBJTAG;
1262 	}
1263 	if (alp->commonattr & ATTR_CMN_OBJID) {
1264 		if (!return_valid || pack_invalid) {
1265 			fsobj_id_t f = {0, 0};
1266 			ATTR_PACK8(ab, f);
1267 		}
1268 	}
1269 	if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
1270 		if (!return_valid || pack_invalid) {
1271 			fsobj_id_t f = {0, 0};
1272 			ATTR_PACK8(ab, f);
1273 		}
1274 	}
1275 	if (alp->commonattr & ATTR_CMN_PAROBJID) {
1276 		if (!return_valid || pack_invalid) {
1277 			fsobj_id_t f = {0, 0};
1278 			ATTR_PACK8(ab, f);
1279 		}
1280 	}
1281 	/* note that this returns the encoding for the volume name, not the node name */
1282 	if (alp->commonattr & ATTR_CMN_SCRIPT) {
1283 		ATTR_PACK4(ab, va.va_encoding);
1284 		ab.actual.commonattr |= ATTR_CMN_SCRIPT;
1285 	}
1286 	if (alp->commonattr & ATTR_CMN_CRTIME) {
1287 		ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit);
1288 		ab.actual.commonattr |= ATTR_CMN_CRTIME;
1289 	}
1290 	if (alp->commonattr & ATTR_CMN_MODTIME) {
1291 		ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
1292 		ab.actual.commonattr |= ATTR_CMN_MODTIME;
1293 	}
1294 	if (alp->commonattr & ATTR_CMN_CHGTIME) {
1295 		if (!return_valid || pack_invalid) {
1296 			ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
1297 		}
1298 	}
1299 	if (alp->commonattr & ATTR_CMN_ACCTIME) {
1300 		ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
1301 		ab.actual.commonattr |= ATTR_CMN_ACCTIME;
1302 	}
1303 	if (alp->commonattr & ATTR_CMN_BKUPTIME) {
1304 		ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit);
1305 		ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
1306 	}
1307 	if (alp->commonattr & ATTR_CMN_FNDRINFO) {
1308 		char f[32];
1309 		/*
1310 		 * This attribute isn't really Finder Info, at least for HFS.
1311 		 */
1312 		if (vp->v_tag == VT_HFS) {
1313 #define HFS_GET_BOOT_INFO   (FCNTL_FS_SPECIFIC_BASE + 0x00004)
1314 			error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx);
1315 			if (error == 0) {
1316 				attrlist_pack_fixed(&ab, f, sizeof(f));
1317 				ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
1318 			} else if (!return_valid) {
1319 				goto out;
1320 			}
1321 		} else if (!return_valid || pack_invalid) {
1322 			/* XXX we could at least pass out the volume UUID here */
1323 			bzero(&f, sizeof(f));
1324 			attrlist_pack_fixed(&ab, f, sizeof(f));
1325 		}
1326 	}
1327 	if (alp->commonattr & ATTR_CMN_OWNERID) {
1328 		ATTR_PACK4(ab, va.va_uid);
1329 		ab.actual.commonattr |= ATTR_CMN_OWNERID;
1330 	}
1331 	if (alp->commonattr & ATTR_CMN_GRPID) {
1332 		ATTR_PACK4(ab, va.va_gid);
1333 		ab.actual.commonattr |= ATTR_CMN_GRPID;
1334 	}
1335 	if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
1336 		ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
1337 		ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
1338 	}
1339 	if (alp->commonattr & ATTR_CMN_FLAGS) {
1340 		ATTR_PACK4(ab, va.va_flags);
1341 		ab.actual.commonattr |= ATTR_CMN_FLAGS;
1342 	}
1343 	if (alp->commonattr & ATTR_CMN_USERACCESS) {    /* XXX this is expensive and also duplicate work */
1344 		uint32_t        perms = 0;
1345 		if (vnode_isdir(vp)) {
1346 			if (vnode_authorize(vp, NULL,
1347 			    KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0) {
1348 				perms |= W_OK;
1349 			}
1350 			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0) {
1351 				perms |= R_OK;
1352 			}
1353 			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0) {
1354 				perms |= X_OK;
1355 			}
1356 		} else {
1357 			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0) {
1358 				perms |= W_OK;
1359 			}
1360 			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0) {
1361 				perms |= R_OK;
1362 			}
1363 			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0) {
1364 				perms |= X_OK;
1365 			}
1366 		}
1367 #if CONFIG_MACF
1368 		/*
1369 		 * Rather than MAC preceding DAC, in this case we want
1370 		 * the smallest set of permissions granted by both MAC & DAC
1371 		 * checks.  We won't add back any permissions.
1372 		 */
1373 		if (perms & W_OK) {
1374 			if (mac_vnode_check_access(ctx, vp, W_OK) != 0) {
1375 				perms &= ~W_OK;
1376 			}
1377 		}
1378 		if (perms & R_OK) {
1379 			if (mac_vnode_check_access(ctx, vp, R_OK) != 0) {
1380 				perms &= ~R_OK;
1381 			}
1382 		}
1383 		if (perms & X_OK) {
1384 			if (mac_vnode_check_access(ctx, vp, X_OK) != 0) {
1385 				perms &= ~X_OK;
1386 			}
1387 		}
1388 #endif /* MAC */
1389 		KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
1390 		ATTR_PACK4(ab, perms);
1391 		ab.actual.commonattr |= ATTR_CMN_USERACCESS;
1392 	}
1393 	/*
1394 	 * The following common volume attributes are only
1395 	 * packed when the pack_invalid mode is enabled.
1396 	 */
1397 	if (pack_invalid) {
1398 		uint64_t fid = 0;
1399 
1400 		if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1401 			attrlist_pack_variable(&ab, NULL, 0);
1402 		}
1403 		if (alp->commonattr & ATTR_CMN_UUID) {
1404 			ATTR_PACK(&ab, kauth_null_guid);
1405 		}
1406 		if (alp->commonattr & ATTR_CMN_GRPUUID) {
1407 			ATTR_PACK(&ab, kauth_null_guid);
1408 		}
1409 		if (alp->commonattr & ATTR_CMN_FILEID) {
1410 			ATTR_PACK8(ab, fid);
1411 		}
1412 		if (alp->commonattr & ATTR_CMN_PARENTID) {
1413 			ATTR_PACK8(ab, fid);
1414 		}
1415 	}
1416 
1417 	/* volume attributes **************************************************/
1418 
1419 	if (alp->volattr & ATTR_VOL_FSTYPE) {
1420 		ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
1421 		ab.actual.volattr |= ATTR_VOL_FSTYPE;
1422 	}
1423 	if (alp->volattr & ATTR_VOL_SIGNATURE) {
1424 		ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
1425 		ab.actual.volattr |= ATTR_VOL_SIGNATURE;
1426 	}
1427 	if (alp->volattr & ATTR_VOL_SIZE) {
1428 		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
1429 		ab.actual.volattr |= ATTR_VOL_SIZE;
1430 	}
1431 	if (alp->volattr & ATTR_VOL_SPACEFREE) {
1432 		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
1433 		ab.actual.volattr |= ATTR_VOL_SPACEFREE;
1434 	}
1435 	if (alp->volattr & ATTR_VOL_SPACEAVAIL) {
1436 		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
1437 		ab.actual.volattr |= ATTR_VOL_SPACEAVAIL;
1438 	}
1439 	if (alp->volattr & ATTR_VOL_SPACEUSED) {
1440 		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bused);
1441 		ab.actual.volattr |= ATTR_VOL_SPACEUSED;
1442 	}
1443 	if (alp->volattr & ATTR_VOL_MINALLOCATION) {
1444 		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
1445 		ab.actual.volattr |= ATTR_VOL_MINALLOCATION;
1446 	}
1447 	if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP) {
1448 		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);                 /* not strictly true */
1449 		ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP;
1450 	}
1451 	if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) {
1452 		ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
1453 		ab.actual.volattr |= ATTR_VOL_IOBLOCKSIZE;
1454 	}
1455 	if (alp->volattr & ATTR_VOL_OBJCOUNT) {
1456 		ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
1457 		ab.actual.volattr |= ATTR_VOL_OBJCOUNT;
1458 	}
1459 	if (alp->volattr & ATTR_VOL_FILECOUNT) {
1460 		ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
1461 		ab.actual.volattr |= ATTR_VOL_FILECOUNT;
1462 	}
1463 	if (alp->volattr & ATTR_VOL_DIRCOUNT) {
1464 		ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
1465 		ab.actual.volattr |= ATTR_VOL_DIRCOUNT;
1466 	}
1467 	if (alp->volattr & ATTR_VOL_MAXOBJCOUNT) {
1468 		ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
1469 		ab.actual.volattr |= ATTR_VOL_MAXOBJCOUNT;
1470 	}
1471 	if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
1472 		attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
1473 		ab.actual.volattr |= ATTR_VOL_MOUNTPOINT;
1474 	}
1475 	if (alp->volattr & ATTR_VOL_NAME) {
1476 		attrlist_pack_string(&ab, vs.f_vol_name, 0);
1477 		ab.actual.volattr |= ATTR_VOL_NAME;
1478 	}
1479 	if (alp->volattr & ATTR_VOL_MOUNTFLAGS) {
1480 		ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
1481 		ab.actual.volattr |= ATTR_VOL_MOUNTFLAGS;
1482 	}
1483 	if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
1484 		attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
1485 		ab.actual.volattr |= ATTR_VOL_MOUNTEDDEVICE;
1486 	}
1487 	if (alp->volattr & ATTR_VOL_ENCODINGSUSED) {
1488 		if (!return_valid || pack_invalid) {
1489 			ATTR_PACK_CAST(&ab, uint64_t, ~0LL);  /* return all encodings */
1490 		}
1491 	}
1492 	if (alp->volattr & ATTR_VOL_CAPABILITIES) {
1493 		/* fix up volume capabilities */
1494 		if (vfs_extendedsecurity(mnt)) {
1495 			vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
1496 		} else {
1497 			vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
1498 		}
1499 		vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
1500 
1501 		/*
1502 		 * if the filesystem doesn't mark either VOL_CAP_FMT_NO_IMMUTABLE_FILES
1503 		 * or VOL_CAP_FMT_NO_PERMISSIONS as valid, assume they're not supported
1504 		 */
1505 		if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_IMMUTABLE_FILES)) {
1506 			vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_IMMUTABLE_FILES;
1507 			vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_IMMUTABLE_FILES;
1508 		}
1509 
1510 		if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_PERMISSIONS)) {
1511 			vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_PERMISSIONS;
1512 			vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_PERMISSIONS;
1513 		}
1514 
1515 		/*
1516 		 * ATTR_CMN_USERACCESS attribute was previously set by file-system drivers, thus volume capabilitiy
1517 		 * VOL_CAP_INT_USERACCESS was conditionally enabled. ATTR_CMN_USERACCESS is now set inside VFS,
1518 		 * regardless of underlying volume type thus we always set VOL_CAP_INT_USERACCESS.
1519 		 */
1520 		vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_USERACCESS;
1521 		vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_USERACCESS;
1522 
1523 		ATTR_PACK(&ab, vs.f_capabilities);
1524 		ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
1525 	}
1526 	if (alp->volattr & ATTR_VOL_UUID) {
1527 		ATTR_PACK(&ab, vs.f_uuid);
1528 		ab.actual.volattr |= ATTR_VOL_UUID;
1529 	}
1530 	if (alp->volattr & ATTR_VOL_QUOTA_SIZE) {
1531 		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_quota);
1532 		ab.actual.volattr |= ATTR_VOL_QUOTA_SIZE;
1533 	}
1534 	if (alp->volattr & ATTR_VOL_RESERVED_SIZE) {
1535 		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_reserved);
1536 		ab.actual.volattr |= ATTR_VOL_RESERVED_SIZE;
1537 	}
1538 	if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
1539 		/* fix up volume attribute information */
1540 
1541 		vs.f_attributes.validattr.commonattr |= VFS_DFLT_ATTR_CMN;
1542 		vs.f_attributes.validattr.volattr |= VFS_DFLT_ATTR_VOL;
1543 		vs.f_attributes.validattr.dirattr |= VFS_DFLT_ATTR_DIR;
1544 		vs.f_attributes.validattr.fileattr |= VFS_DFLT_ATTR_FILE;
1545 
1546 		if (vfs_extendedsecurity(mnt)) {
1547 			vs.f_attributes.validattr.commonattr |= (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1548 		} else {
1549 			vs.f_attributes.validattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1550 			vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1551 		}
1552 		ATTR_PACK(&ab, vs.f_attributes);
1553 		ab.actual.volattr |= ATTR_VOL_ATTRIBUTES;
1554 	}
1555 
1556 	/* diagnostic */
1557 	if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize) {
1558 		panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
1559 		    fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
1560 	}
1561 	if (!return_valid && ab.varcursor != (ab.base + ab.needed)) {
1562 		panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
1563 	}
1564 
1565 	/*
1566 	 * In the compatible case, we report the smaller of the required and returned sizes.
1567 	 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1568 	 * of the result buffer, even if we copied less out.  The caller knows how big a buffer
1569 	 * they gave us, so they can always check for truncation themselves.
1570 	 */
1571 	*(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? (uint32_t)ab.needed : (uint32_t)lmin(bufferSize, ab.needed);
1572 
1573 	/* Return attribute set output if requested. */
1574 	if (return_valid &&
1575 	    (ab.allocated >= (ssize_t)(sizeof(uint32_t) + sizeof(ab.actual)))) {
1576 		ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
1577 		if (pack_invalid) {
1578 			/* Only report the attributes that are valid */
1579 			ab.actual.commonattr &= ab.valid.commonattr;
1580 			ab.actual.volattr &= ab.valid.volattr;
1581 		}
1582 		bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof(ab.actual));
1583 	}
1584 
1585 	if (UIO_SEG_IS_USER_SPACE(segflg)) {
1586 		error = copyout(ab.base, CAST_USER_ADDR_T(attributeBuffer),
1587 		    ulmin((uint32_t)bufferSize, (uint32_t)ab.needed));
1588 	} else {
1589 		bcopy(ab.base, (void *)attributeBuffer, (size_t)ulmin((uint32_t)bufferSize, (uint32_t)ab.needed));
1590 	}
1591 
1592 out:
1593 	if (vs.f_vol_name != NULL) {
1594 		zfree(ZV_NAMEI, vs.f_vol_name);
1595 	}
1596 	if (release_str) {
1597 		vnode_putname(cnp);
1598 	}
1599 	kfree_data(ab.base, ab.allocated);
1600 	VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
1601 
1602 	if (root_vp != NULL) {
1603 		vnode_put(root_vp);
1604 	}
1605 	return error;
1606 }
1607 
1608 /*
1609  * Pack ATTR_COMMON attributes into a user buffer.
1610  * alp is a pointer to the bitmap of attributes required.
1611  * abp is the state of the attribute filling operation.
1612  * The attribute data (along with some other fields that are required
1613  * are in ad.
1614  */
1615 static errno_t
attr_pack_common(vfs_context_t ctx,mount_t mp,vnode_t vp,struct attrlist * alp,struct _attrlist_buf * abp,struct vnode_attr * vap,int proc_is64,const char * cnp,ssize_t cnl,const char * fullpathptr,ssize_t fullpathlen,int return_valid,int pack_invalid,int vtype,int is_bulk)1616 attr_pack_common(vfs_context_t ctx, mount_t mp, vnode_t vp, struct attrlist *alp,
1617     struct _attrlist_buf *abp, struct vnode_attr *vap, int proc_is64,
1618     const char *cnp, ssize_t cnl, const char *fullpathptr,
1619     ssize_t fullpathlen, int return_valid, int pack_invalid, int vtype,
1620     int is_bulk)
1621 {
1622 	uint32_t        perms = 0;
1623 	int             error = 0;
1624 
1625 	if ((alp->commonattr & ATTR_CMN_ERROR) &&
1626 	    (!return_valid || pack_invalid)) {
1627 		ATTR_PACK4((*abp), 0);
1628 		abp->actual.commonattr |= ATTR_CMN_ERROR;
1629 	}
1630 	if (alp->commonattr & ATTR_CMN_NAME) {
1631 		attrlist_pack_string(abp, cnp, cnl);
1632 		abp->actual.commonattr |= ATTR_CMN_NAME;
1633 	}
1634 	if (alp->commonattr & ATTR_CMN_DEVID) {
1635 		if (mp) { /* caller needs real devid */
1636 			ATTR_PACK4((*abp),
1637 			    mp->mnt_vfsstat.f_fsid.val[0]);
1638 			abp->actual.commonattr |= ATTR_CMN_DEVID;
1639 		} else if (VATTR_IS_ACTIVE(vap, va_fsid) && VATTR_IS_SUPPORTED(vap, va_fsid)) {
1640 			ATTR_PACK4((*abp), vap->va_fsid);
1641 			abp->actual.commonattr |= ATTR_CMN_DEVID;
1642 		} else if (vp) {
1643 			ATTR_PACK4((*abp),
1644 			    vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
1645 			abp->actual.commonattr |= ATTR_CMN_DEVID;
1646 		} else if (VATTR_IS_SUPPORTED(vap, va_devid)) {
1647 			ATTR_PACK4((*abp), vap->va_devid);
1648 			abp->actual.commonattr |= ATTR_CMN_DEVID;
1649 		} else if (!return_valid || pack_invalid) {
1650 			ATTR_PACK4((*abp), 0);
1651 		}
1652 	}
1653 	if (alp->commonattr & ATTR_CMN_FSID) {
1654 		if (mp) { /* caller needs real fsid */
1655 			ATTR_PACK8((*abp),
1656 			    mp->mnt_vfsstat.f_fsid);
1657 			abp->actual.commonattr |= ATTR_CMN_FSID;
1658 		} else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) {
1659 			ATTR_PACK8((*abp), vap->va_fsid64);
1660 			abp->actual.commonattr |= ATTR_CMN_FSID;
1661 		} else if (vp) {
1662 			ATTR_PACK8((*abp),
1663 			    vp->v_mount->mnt_vfsstat.f_fsid);
1664 			abp->actual.commonattr |= ATTR_CMN_FSID;
1665 		} else if (!return_valid || pack_invalid) {
1666 			fsid_t fsid = {{0}};
1667 			ATTR_PACK8((*abp), fsid);
1668 		}
1669 	}
1670 	if (alp->commonattr & ATTR_CMN_OBJTYPE) {
1671 		if (vp) {
1672 			ATTR_PACK4((*abp), vtype);
1673 			abp->actual.commonattr |= ATTR_CMN_OBJTYPE;
1674 		} else if (VATTR_IS_SUPPORTED(vap, va_objtype)) {
1675 			ATTR_PACK4((*abp), vap->va_objtype);
1676 			abp->actual.commonattr |= ATTR_CMN_OBJTYPE;
1677 		} else if (!return_valid || pack_invalid) {
1678 			ATTR_PACK4((*abp), 0);
1679 		}
1680 	}
1681 	if (alp->commonattr & ATTR_CMN_OBJTAG) {
1682 		if (vp) {
1683 			ATTR_PACK4((*abp), vp->v_tag);
1684 			abp->actual.commonattr |= ATTR_CMN_OBJTAG;
1685 		} else if (VATTR_IS_SUPPORTED(vap, va_objtag)) {
1686 			ATTR_PACK4((*abp), vap->va_objtag);
1687 			abp->actual.commonattr |= ATTR_CMN_OBJTAG;
1688 		} else if (!return_valid || pack_invalid) {
1689 			ATTR_PACK4((*abp), 0);
1690 		}
1691 	}
1692 	if (alp->commonattr & ATTR_CMN_OBJID) {
1693 		/*
1694 		 * Carbon can't deal with us reporting the target ID
1695 		 * for links.  So we ask the filesystem to give us the
1696 		 * source ID as well, and if it gives us one, we use
1697 		 * it instead.
1698 		 */
1699 		if (vap->va_vaflags & VA_64BITOBJIDS) {
1700 			if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
1701 				ATTR_PACK8((*abp), vap->va_linkid);
1702 			} else {
1703 				ATTR_PACK8((*abp), vap->va_fileid);
1704 			}
1705 		} else {
1706 			fsobj_id_t f;
1707 			if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
1708 				f.fid_objno = (uint32_t)vap->va_linkid;
1709 			} else {
1710 				f.fid_objno = (uint32_t)vap->va_fileid;
1711 			}
1712 			f.fid_generation = 0;
1713 			ATTR_PACK8((*abp), f);
1714 		}
1715 		abp->actual.commonattr |= ATTR_CMN_OBJID;
1716 	}
1717 	if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
1718 		/*
1719 		 * Carbon can't deal with us reporting the target ID
1720 		 * for links.  So we ask the filesystem to give us the
1721 		 * source ID as well, and if it gives us one, we use
1722 		 * it instead.
1723 		 */
1724 		if (vap->va_vaflags & VA_64BITOBJIDS) {
1725 			if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
1726 				ATTR_PACK8((*abp), vap->va_linkid);
1727 			} else {
1728 				ATTR_PACK8((*abp), vap->va_fileid);
1729 			}
1730 		} else {
1731 			fsobj_id_t f;
1732 			if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
1733 				f.fid_objno = (uint32_t)vap->va_linkid;
1734 			} else {
1735 				f.fid_objno = (uint32_t)vap->va_fileid;
1736 			}
1737 			f.fid_generation = 0;
1738 			ATTR_PACK8((*abp), f);
1739 		}
1740 		abp->actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
1741 	}
1742 	if (alp->commonattr & ATTR_CMN_PAROBJID) {
1743 		if (vap->va_vaflags & VA_64BITOBJIDS) {
1744 			ATTR_PACK8((*abp), vap->va_parentid);
1745 		} else {
1746 			fsobj_id_t f;
1747 			f.fid_objno = (uint32_t)vap->va_parentid;
1748 			f.fid_generation = 0;
1749 			ATTR_PACK8((*abp), f);
1750 		}
1751 		abp->actual.commonattr |= ATTR_CMN_PAROBJID;
1752 	}
1753 	if (alp->commonattr & ATTR_CMN_SCRIPT) {
1754 		if (VATTR_IS_SUPPORTED(vap, va_encoding)) {
1755 			ATTR_PACK4((*abp), vap->va_encoding);
1756 			abp->actual.commonattr |= ATTR_CMN_SCRIPT;
1757 		} else if (!return_valid || pack_invalid) {
1758 			ATTR_PACK4((*abp), 0x7e);
1759 		}
1760 	}
1761 	if (alp->commonattr & ATTR_CMN_CRTIME) {
1762 		ATTR_PACK_TIME((*abp), vap->va_create_time, proc_is64);
1763 		abp->actual.commonattr |= ATTR_CMN_CRTIME;
1764 	}
1765 	if (alp->commonattr & ATTR_CMN_MODTIME) {
1766 		ATTR_PACK_TIME((*abp), vap->va_modify_time, proc_is64);
1767 		abp->actual.commonattr |= ATTR_CMN_MODTIME;
1768 	}
1769 	if (alp->commonattr & ATTR_CMN_CHGTIME) {
1770 		ATTR_PACK_TIME((*abp), vap->va_change_time, proc_is64);
1771 		abp->actual.commonattr |= ATTR_CMN_CHGTIME;
1772 	}
1773 	if (alp->commonattr & ATTR_CMN_ACCTIME) {
1774 		ATTR_PACK_TIME((*abp), vap->va_access_time, proc_is64);
1775 		abp->actual.commonattr |= ATTR_CMN_ACCTIME;
1776 	}
1777 	if (alp->commonattr & ATTR_CMN_BKUPTIME) {
1778 		ATTR_PACK_TIME((*abp), vap->va_backup_time, proc_is64);
1779 		abp->actual.commonattr |= ATTR_CMN_BKUPTIME;
1780 	}
1781 	/*
1782 	 * They are requesting user access, we should obtain this before getting
1783 	 * the finder info. For some network file systems this is a performance
1784 	 * improvement.
1785 	 */
1786 	if (alp->commonattr & ATTR_CMN_USERACCESS) {    /* this is expensive */
1787 		if (vp && !is_bulk) {
1788 			if (vtype == VDIR) {
1789 				if (vnode_authorize(vp, NULL,
1790 				    KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE |
1791 				    KAUTH_VNODE_ADD_SUBDIRECTORY |
1792 				    KAUTH_VNODE_DELETE_CHILD, ctx) == 0) {
1793 					perms |= W_OK;
1794 				}
1795 
1796 				if (vnode_authorize(vp, NULL,
1797 				    KAUTH_VNODE_ACCESS |
1798 				    KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0) {
1799 					perms |= R_OK;
1800 				}
1801 
1802 				if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS |
1803 				    KAUTH_VNODE_SEARCH, ctx) == 0) {
1804 					perms |= X_OK;
1805 				}
1806 			} else {
1807 				if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS |
1808 				    KAUTH_VNODE_WRITE_DATA, ctx) == 0) {
1809 					perms |= W_OK;
1810 				}
1811 
1812 				if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0) {
1813 					perms |= R_OK;
1814 				}
1815 				if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0) {
1816 					perms |= X_OK;
1817 				}
1818 			}
1819 		} else if (is_bulk &&
1820 		    VATTR_IS_SUPPORTED(vap, va_user_access)) {
1821 			perms = vap->va_user_access;
1822 		}
1823 	}
1824 	if (alp->commonattr & ATTR_CMN_FNDRINFO) {
1825 		size_t  fisize = 32;
1826 
1827 		error = 0;
1828 		if (vp && !is_bulk) {
1829 			uio_t   auio;
1830 			uio_stackbuf_t    uio_buf[UIO_SIZEOF(1)];
1831 
1832 			if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE,
1833 			    UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
1834 				error = ENOMEM;
1835 				goto out;
1836 			}
1837 			uio_addiov(auio, CAST_USER_ADDR_T(abp->fixedcursor),
1838 			    fisize);
1839 			/* fisize may be reset to 0 after this call */
1840 			error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio,
1841 			    &fisize, XATTR_NOSECURITY, ctx);
1842 			uio_free(auio);
1843 
1844 			/*
1845 			 * Default to zeros if its not available,
1846 			 * unless ATTR_CMN_RETURNED_ATTRS was requested.
1847 			 */
1848 			if (error &&
1849 			    (!return_valid || pack_invalid) &&
1850 			    ((error == ENOATTR) || (error == ENOENT) ||
1851 			    (error == ENOTSUP) || (error == EPERM))) {
1852 				VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1853 				bzero(abp->fixedcursor, 32);
1854 				error = 0;
1855 			}
1856 
1857 			if (error == 0) {
1858 				abp->fixedcursor += 32;
1859 				abp->actual.commonattr |= ATTR_CMN_FNDRINFO;
1860 			} else if (!return_valid) {
1861 				goto out;
1862 			} else {
1863 				/*
1864 				 * If we can inform the caller that we can't
1865 				 * return this attribute, reset error and
1866 				 * continue with the rest of the attributes.
1867 				 */
1868 				error = 0;
1869 			}
1870 		} else if (VATTR_IS_SUPPORTED(vap, va_finderinfo)) {
1871 			bcopy(&vap->va_finderinfo[0], abp->fixedcursor, fisize);
1872 			abp->fixedcursor += fisize;
1873 			abp->actual.commonattr |= ATTR_CMN_FNDRINFO;
1874 		} else if (!return_valid || pack_invalid) {
1875 			bzero(abp->fixedcursor, fisize);
1876 			abp->fixedcursor += fisize;
1877 		}
1878 	}
1879 	if (alp->commonattr & ATTR_CMN_OWNERID) {
1880 		ATTR_PACK4((*abp), vap->va_uid);
1881 		abp->actual.commonattr |= ATTR_CMN_OWNERID;
1882 	}
1883 	if (alp->commonattr & ATTR_CMN_GRPID) {
1884 		ATTR_PACK4((*abp), vap->va_gid);
1885 		abp->actual.commonattr |= ATTR_CMN_GRPID;
1886 	}
1887 	if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
1888 		ATTR_PACK4((*abp), vap->va_mode);
1889 		abp->actual.commonattr |= ATTR_CMN_ACCESSMASK;
1890 	}
1891 	if (alp->commonattr & ATTR_CMN_FLAGS) {
1892 		ATTR_PACK4((*abp), vap->va_flags);
1893 		abp->actual.commonattr |= ATTR_CMN_FLAGS;
1894 	}
1895 	if (alp->commonattr & ATTR_CMN_GEN_COUNT) {
1896 		if (VATTR_IS_SUPPORTED(vap, va_write_gencount)) {
1897 			ATTR_PACK4((*abp), vap->va_write_gencount);
1898 			abp->actual.commonattr |= ATTR_CMN_GEN_COUNT;
1899 		} else if (!return_valid || pack_invalid) {
1900 			ATTR_PACK4((*abp), 0);
1901 		}
1902 	}
1903 
1904 	if (alp->commonattr & ATTR_CMN_DOCUMENT_ID) {
1905 		if (VATTR_IS_SUPPORTED(vap, va_document_id)) {
1906 			ATTR_PACK4((*abp), vap->va_document_id);
1907 			abp->actual.commonattr |= ATTR_CMN_DOCUMENT_ID;
1908 		} else if (!return_valid || pack_invalid) {
1909 			ATTR_PACK4((*abp), 0);
1910 		}
1911 	}
1912 	/* We already obtain the user access, so just fill in the buffer here */
1913 	if (alp->commonattr & ATTR_CMN_USERACCESS) {
1914 #if CONFIG_MACF
1915 		if (!is_bulk && vp) {
1916 			/*
1917 			 * Rather than MAC preceding DAC, in this case we want
1918 			 * the smallest set of permissions granted by both MAC &
1919 			 * DAC checks.  We won't add back any permissions.
1920 			 */
1921 			if (perms & W_OK) {
1922 				if (mac_vnode_check_access(ctx, vp, W_OK) != 0) {
1923 					perms &= ~W_OK;
1924 				}
1925 			}
1926 			if (perms & R_OK) {
1927 				if (mac_vnode_check_access(ctx, vp, R_OK) != 0) {
1928 					perms &= ~R_OK;
1929 				}
1930 			}
1931 			if (perms & X_OK) {
1932 				if (mac_vnode_check_access(ctx, vp, X_OK) != 0) {
1933 					perms &= ~X_OK;
1934 				}
1935 			}
1936 		}
1937 #endif /* MAC */
1938 		VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
1939 		if (!is_bulk && vp) {
1940 			ATTR_PACK4((*abp), perms);
1941 			abp->actual.commonattr |= ATTR_CMN_USERACCESS;
1942 		} else if (is_bulk && VATTR_IS_SUPPORTED(vap, va_user_access)) {
1943 			ATTR_PACK4((*abp), perms);
1944 			abp->actual.commonattr |= ATTR_CMN_USERACCESS;
1945 		} else if (!return_valid || pack_invalid) {
1946 			ATTR_PACK4((*abp), 0);
1947 		}
1948 	}
1949 	if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1950 		if (VATTR_IS_SUPPORTED(vap, va_acl) && (vap->va_acl != NULL)) {
1951 			struct kauth_filesec fsec;
1952 			/*
1953 			 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1954 			 */
1955 			fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
1956 			fsec.fsec_owner = kauth_null_guid;
1957 			fsec.fsec_group = kauth_null_guid;
1958 			attrlist_pack_variable2(abp, &fsec, __offsetof(struct kauth_filesec, fsec_acl), vap->va_acl, KAUTH_ACL_COPYSIZE(vap->va_acl));
1959 			abp->actual.commonattr |= ATTR_CMN_EXTENDED_SECURITY;
1960 		} else if (!return_valid || pack_invalid) {
1961 			attrlist_pack_variable(abp, NULL, 0);
1962 		}
1963 	}
1964 	if (alp->commonattr & ATTR_CMN_UUID) {
1965 		if (VATTR_IS_SUPPORTED(vap, va_uuuid)) {
1966 			ATTR_PACK(abp, vap->va_uuuid);
1967 			abp->actual.commonattr |= ATTR_CMN_UUID;
1968 		} else if (!return_valid || pack_invalid) {
1969 			ATTR_PACK(abp, kauth_null_guid);
1970 		}
1971 	}
1972 	if (alp->commonattr & ATTR_CMN_GRPUUID) {
1973 		if (VATTR_IS_SUPPORTED(vap, va_guuid)) {
1974 			ATTR_PACK(abp, vap->va_guuid);
1975 			abp->actual.commonattr |= ATTR_CMN_GRPUUID;
1976 		} else if (!return_valid || pack_invalid) {
1977 			ATTR_PACK(abp, kauth_null_guid);
1978 		}
1979 	}
1980 	if (alp->commonattr & ATTR_CMN_FILEID) {
1981 		ATTR_PACK8((*abp), vap->va_fileid);
1982 		abp->actual.commonattr |= ATTR_CMN_FILEID;
1983 	}
1984 	if (alp->commonattr & ATTR_CMN_PARENTID) {
1985 		ATTR_PACK8((*abp), vap->va_parentid);
1986 		abp->actual.commonattr |= ATTR_CMN_PARENTID;
1987 	}
1988 
1989 	if (alp->commonattr & ATTR_CMN_FULLPATH) {
1990 		if (vp) {
1991 			attrlist_pack_string(abp, fullpathptr, fullpathlen);
1992 			abp->actual.commonattr |= ATTR_CMN_FULLPATH;
1993 		}
1994 	}
1995 
1996 	if (alp->commonattr & ATTR_CMN_ADDEDTIME) {
1997 		if (VATTR_IS_SUPPORTED(vap, va_addedtime)) {
1998 			ATTR_PACK_TIME((*abp), vap->va_addedtime, proc_is64);
1999 			abp->actual.commonattr |= ATTR_CMN_ADDEDTIME;
2000 		} else if (!return_valid || pack_invalid) {
2001 			struct timespec zerotime = {.tv_sec = 0, .tv_nsec = 0};
2002 
2003 			ATTR_PACK_TIME((*abp), zerotime, proc_is64);
2004 		}
2005 	}
2006 	if (alp->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) {
2007 		if (VATTR_IS_SUPPORTED(vap, va_dataprotect_class)) {
2008 			ATTR_PACK4((*abp), vap->va_dataprotect_class);
2009 			abp->actual.commonattr |= ATTR_CMN_DATA_PROTECT_FLAGS;
2010 		} else if (!return_valid || pack_invalid) {
2011 			ATTR_PACK4((*abp), 0);
2012 		}
2013 	}
2014 out:
2015 	return error;
2016 }
2017 
2018 static errno_t
attr_pack_dir(struct vnode * vp,struct attrlist * alp,struct _attrlist_buf * abp,struct vnode_attr * vap,int return_valid,int pack_invalid)2019 attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp,
2020     struct vnode_attr *vap, int return_valid, int pack_invalid)
2021 {
2022 	if (alp->dirattr & ATTR_DIR_LINKCOUNT) {  /* full count of entries */
2023 		ATTR_PACK4((*abp), (uint32_t)vap->va_dirlinkcount);
2024 		abp->actual.dirattr |= ATTR_DIR_LINKCOUNT;
2025 	}
2026 	if (alp->dirattr & ATTR_DIR_ENTRYCOUNT) {
2027 		ATTR_PACK4((*abp), (uint32_t)vap->va_nchildren);
2028 		abp->actual.dirattr |= ATTR_DIR_ENTRYCOUNT;
2029 	}
2030 	if (alp->dirattr & ATTR_DIR_MOUNTSTATUS) {
2031 		uint32_t mntstat;
2032 
2033 		if (vp) {
2034 			/*
2035 			 * The vnode that is passed down may either be a
2036 			 * top level vnode of a mount stack or a mounted
2037 			 * on vnode. In either case, the directory should
2038 			 * be reported as a mount point.
2039 			 */
2040 			if ((vp->v_flag & VROOT) || vnode_mountedhere(vp)) {
2041 				mntstat = DIR_MNTSTATUS_MNTPOINT;
2042 			} else {
2043 				mntstat = 0;
2044 			}
2045 #if CONFIG_TRIGGERS
2046 			/*
2047 			 * Report back on active vnode triggers
2048 			 * that can directly trigger a mount
2049 			 */
2050 			if (vp->v_resolve &&
2051 			    !(vp->v_resolve->vr_flags & VNT_NO_DIRECT_MOUNT)) {
2052 				mntstat |= DIR_MNTSTATUS_TRIGGER;
2053 			}
2054 #endif
2055 		} else {
2056 			mntstat = 0;
2057 		}
2058 
2059 		ATTR_PACK4((*abp), mntstat);
2060 		abp->actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
2061 	}
2062 	if (alp->dirattr & ATTR_DIR_ALLOCSIZE) {
2063 		if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
2064 			ATTR_PACK8((*abp), vap->va_data_alloc);
2065 			abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE;
2066 		} else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
2067 			ATTR_PACK8((*abp), vap->va_total_alloc);
2068 			abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE;
2069 		} else if (!return_valid || pack_invalid) {
2070 			uint64_t zero_val = 0;
2071 			ATTR_PACK8((*abp), zero_val);
2072 		}
2073 	}
2074 	if (alp->dirattr & ATTR_DIR_IOBLOCKSIZE) {
2075 		if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
2076 			ATTR_PACK4((*abp), vap->va_iosize);
2077 			abp->actual.dirattr |= ATTR_DIR_IOBLOCKSIZE;
2078 		} else if (!return_valid || pack_invalid) {
2079 			ATTR_PACK4((*abp), 0);
2080 		}
2081 	}
2082 	/*
2083 	 * If the filesystem does not support datalength
2084 	 * or dataallocsize, then we infer that totalsize and
2085 	 * totalalloc are substitutes.
2086 	 */
2087 	if (alp->dirattr & ATTR_DIR_DATALENGTH) {
2088 		if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
2089 			ATTR_PACK8((*abp), vap->va_data_size);
2090 			abp->actual.dirattr |= ATTR_DIR_DATALENGTH;
2091 		} else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
2092 			ATTR_PACK8((*abp), vap->va_total_size);
2093 			abp->actual.dirattr |= ATTR_DIR_DATALENGTH;
2094 		} else if (!return_valid || pack_invalid) {
2095 			uint64_t zero_val = 0;
2096 			ATTR_PACK8((*abp), zero_val);
2097 		}
2098 	}
2099 
2100 	return 0;
2101 }
2102 
2103 /*
2104  * The is_bulk parameter differentiates whether the function is called from
2105  * getattrlist or getattrlistbulk. When coming in from getattrlistbulk,
2106  * the corresponding va_* values are expected to be the values filled and no
2107  * attempt is made to retrieve them by calling back into the filesystem.
2108  */
2109 static errno_t
attr_pack_file(vfs_context_t ctx,struct vnode * vp,struct attrlist * alp,struct _attrlist_buf * abp,struct vnode_attr * vap,int return_valid,int pack_invalid,int is_bulk)2110 attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp,
2111     struct _attrlist_buf *abp, struct vnode_attr *vap, int return_valid,
2112     int pack_invalid, int is_bulk)
2113 {
2114 	size_t  rsize = 0;
2115 	uint64_t rlength = 0;
2116 	uint64_t ralloc = 0;
2117 	int error = 0;
2118 
2119 	/*
2120 	 * Pre-fetch the rsrc attributes now so we only get them once.
2121 	 * Fetch the resource fork size/allocation via xattr interface
2122 	 */
2123 	if (vp && !is_bulk &&
2124 	    (alp->fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE |
2125 	    ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE))) {
2126 		error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL,
2127 		    &rsize, XATTR_NOSECURITY, ctx);
2128 		if (error) {
2129 			if ((error == ENOENT) || (error == ENOATTR) ||
2130 			    (error == ENOTSUP) || (error == EPERM) ||
2131 			    (error == EACCES)) {
2132 				rsize = 0;
2133 				error = 0;
2134 			} else {
2135 				goto out;
2136 			}
2137 		}
2138 		rlength = rsize;
2139 
2140 		if (alp->fileattr & (ATTR_FILE_RSRCALLOCSIZE |
2141 		    ATTR_FILE_ALLOCSIZE)) {
2142 			uint32_t  blksize;
2143 
2144 			blksize = vp->v_mount->mnt_vfsstat.f_bsize;
2145 
2146 			if (blksize == 0) {
2147 				blksize = 512;
2148 			}
2149 			ralloc = roundup(rsize, blksize);
2150 		}
2151 	}
2152 
2153 	if (alp->fileattr & ATTR_FILE_LINKCOUNT) {
2154 		ATTR_PACK4((*abp), (uint32_t)vap->va_nlink);
2155 		abp->actual.fileattr |= ATTR_FILE_LINKCOUNT;
2156 	}
2157 	/*
2158 	 * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes:
2159 	 * We infer that if the filesystem does not support va_data_size or va_data_alloc
2160 	 * it must not know about alternate forks.  So when we need to gather
2161 	 * the total size or total alloc, it's OK to substitute the total size for
2162 	 * the data size below.  This is because it is likely a flat filesystem and we must
2163 	 * be using AD files to store the rsrc fork and EAs.
2164 	 *
2165 	 * Additionally, note that getattrlist is barred from being called on
2166 	 * resource fork paths. (Search for CN_ALLOWRSRCFORK).  So if the filesystem does
2167 	 * support va_data_size, it is guaranteed to represent the data fork's size.  This
2168 	 * is an important distinction to make because when we call vnode_getattr on
2169 	 * an HFS resource fork vnode, to get the size, it will vend out the resource
2170 	 * fork's size (it only gets the size of the passed-in vnode).
2171 	 */
2172 	if (alp->fileattr & ATTR_FILE_TOTALSIZE) {
2173 		if (!is_bulk) {
2174 			uint64_t totalsize = rlength;
2175 
2176 			if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
2177 				totalsize += vap->va_data_size;
2178 			} else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
2179 				totalsize += vap->va_total_size;
2180 			}
2181 
2182 			ATTR_PACK8((*abp), totalsize);
2183 			abp->actual.fileattr |= ATTR_FILE_TOTALSIZE;
2184 		} else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
2185 			ATTR_PACK8((*abp), vap->va_total_size);
2186 			abp->actual.fileattr |= ATTR_FILE_TOTALSIZE;
2187 		} else if (!return_valid || pack_invalid) {
2188 			uint64_t zero_val = 0;
2189 
2190 			ATTR_PACK8((*abp), zero_val);
2191 		}
2192 	}
2193 	if (alp->fileattr & ATTR_FILE_ALLOCSIZE) {
2194 		if (!is_bulk) {
2195 			uint64_t totalalloc = ralloc;
2196 
2197 			/*
2198 			 * If data_alloc is supported, then it must represent the
2199 			 * data fork size.
2200 			 */
2201 			if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
2202 				totalalloc += vap->va_data_alloc;
2203 			} else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
2204 				totalalloc += vap->va_total_alloc;
2205 			}
2206 
2207 			ATTR_PACK8((*abp), totalalloc);
2208 			abp->actual.fileattr |= ATTR_FILE_ALLOCSIZE;
2209 		} else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
2210 			ATTR_PACK8((*abp), vap->va_total_alloc);
2211 			abp->actual.fileattr |= ATTR_FILE_ALLOCSIZE;
2212 		} else if (!return_valid || pack_invalid) {
2213 			uint64_t zero_val = 0;
2214 
2215 			ATTR_PACK8((*abp), zero_val);
2216 		}
2217 	}
2218 	if (alp->fileattr & ATTR_FILE_IOBLOCKSIZE) {
2219 		if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
2220 			ATTR_PACK4((*abp), vap->va_iosize);
2221 			abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
2222 		} else if (!return_valid || pack_invalid) {
2223 			ATTR_PACK4((*abp), 0);
2224 		}
2225 	}
2226 	if (alp->fileattr & ATTR_FILE_CLUMPSIZE) {
2227 		if (!return_valid || pack_invalid) {
2228 			ATTR_PACK4((*abp), 0);     /* this value is deprecated */
2229 			abp->actual.fileattr |= ATTR_FILE_CLUMPSIZE;
2230 		}
2231 	}
2232 	if (alp->fileattr & ATTR_FILE_DEVTYPE) {
2233 		if (vp && (vp->v_type == VCHR || vp->v_type == VBLK)) {
2234 			uint32_t dev;
2235 
2236 			if (vp->v_specinfo != NULL) {
2237 				dev = vp->v_specinfo->si_rdev;
2238 			} else if (VATTR_IS_SUPPORTED(vap, va_rdev)) {
2239 				dev = vap->va_rdev;
2240 			} else {
2241 				dev = 0;
2242 			}
2243 			ATTR_PACK4((*abp), dev);
2244 			abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
2245 		} else if (vp) {
2246 			ATTR_PACK4((*abp), 0);
2247 			abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
2248 		} else if (VATTR_IS_SUPPORTED(vap, va_rdev)) {
2249 			ATTR_PACK4((*abp), vap->va_rdev);
2250 			abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
2251 		} else if (!return_valid || pack_invalid) {
2252 			ATTR_PACK4((*abp), 0);
2253 		}
2254 	}
2255 	/*
2256 	 * If the filesystem does not support datalength
2257 	 * or dataallocsize, then we infer that totalsize and
2258 	 * totalalloc are substitutes.
2259 	 */
2260 	if (alp->fileattr & ATTR_FILE_DATALENGTH) {
2261 		if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
2262 			ATTR_PACK8((*abp), vap->va_data_size);
2263 			abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
2264 		} else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
2265 			ATTR_PACK8((*abp), vap->va_total_size);
2266 			abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
2267 		} else if (!return_valid || pack_invalid) {
2268 			uint64_t zero_val = 0;
2269 			ATTR_PACK8((*abp), zero_val);
2270 		}
2271 	}
2272 	if (alp->fileattr & ATTR_FILE_DATAALLOCSIZE) {
2273 		if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
2274 			ATTR_PACK8((*abp), vap->va_data_alloc);
2275 			abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
2276 		} else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
2277 			ATTR_PACK8((*abp), vap->va_total_alloc);
2278 			abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
2279 		} else if (!return_valid || pack_invalid) {
2280 			uint64_t zero_val = 0;
2281 			ATTR_PACK8((*abp), zero_val);
2282 		}
2283 	}
2284 	/* already got the resource fork size/allocation above */
2285 	if (alp->fileattr & ATTR_FILE_RSRCLENGTH) {
2286 		if (!is_bulk) {
2287 			ATTR_PACK8((*abp), rlength);
2288 			abp->actual.fileattr |= ATTR_FILE_RSRCLENGTH;
2289 		} else if (VATTR_IS_SUPPORTED(vap, va_rsrc_length)) {
2290 			ATTR_PACK8((*abp), vap->va_rsrc_length);
2291 			abp->actual.fileattr |= ATTR_FILE_RSRCLENGTH;
2292 		} else if (!return_valid || pack_invalid) {
2293 			uint64_t zero_val = 0;
2294 
2295 			ATTR_PACK8((*abp), zero_val);
2296 		}
2297 	}
2298 	if (alp->fileattr & ATTR_FILE_RSRCALLOCSIZE) {
2299 		if (!is_bulk) {
2300 			ATTR_PACK8((*abp), ralloc);
2301 			abp->actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
2302 		} else if (VATTR_IS_SUPPORTED(vap, va_rsrc_alloc)) {
2303 			ATTR_PACK8((*abp), vap->va_rsrc_alloc);
2304 			abp->actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
2305 		} else if (!return_valid || pack_invalid) {
2306 			uint64_t zero_val = 0;
2307 
2308 			ATTR_PACK8((*abp), zero_val);
2309 		}
2310 	}
2311 out:
2312 	return error;
2313 }
2314 
2315 /*
2316  * Pack FORKATTR attributes into a user buffer.
2317  * alp is a pointer to the bitmap of attributes required.
2318  * abp is the state of the attribute filling operation.
2319  * The attribute data (along with some other fields that are required
2320  * are in ad.
2321  */
2322 static errno_t
attr_pack_common_extended(mount_t mp,struct vnode * vp,struct attrlist * alp,struct _attrlist_buf * abp,const char * relpathptr,ssize_t relpathlen,const char * REALpathptr,ssize_t REALpathlen,struct vnode_attr * vap,int return_valid,int pack_invalid)2323 attr_pack_common_extended(mount_t mp, struct vnode *vp, struct attrlist *alp,
2324     struct _attrlist_buf *abp, const char *relpathptr, ssize_t relpathlen,
2325     const char *REALpathptr, ssize_t REALpathlen,
2326     struct vnode_attr *vap, int return_valid, int pack_invalid)
2327 {
2328 	if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) {
2329 		attrlist_pack_string(abp, relpathptr, relpathlen);
2330 		abp->actual.forkattr |= ATTR_CMNEXT_RELPATH;
2331 	}
2332 
2333 	if (alp->forkattr & ATTR_CMNEXT_PRIVATESIZE) {
2334 		if (VATTR_IS_SUPPORTED(vap, va_private_size)) {
2335 			ATTR_PACK8((*abp), vap->va_private_size);
2336 			abp->actual.forkattr |= ATTR_CMNEXT_PRIVATESIZE;
2337 		} else if (!return_valid || pack_invalid) {
2338 			uint64_t zero_val = 0;
2339 			ATTR_PACK8((*abp), zero_val);
2340 		}
2341 	}
2342 
2343 	if (alp->forkattr & ATTR_CMNEXT_LINKID) {
2344 		uint64_t linkid;
2345 
2346 		if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
2347 			linkid = vap->va_linkid;
2348 		} else {
2349 			linkid = vap->va_fileid;
2350 		}
2351 
2352 		ATTR_PACK8((*abp), linkid);
2353 		abp->actual.forkattr |= ATTR_CMNEXT_LINKID;
2354 	}
2355 
2356 	if (vp && (alp->forkattr & ATTR_CMNEXT_NOFIRMLINKPATH)) {
2357 		attrlist_pack_string(abp, REALpathptr, REALpathlen);
2358 		abp->actual.forkattr |= ATTR_CMNEXT_NOFIRMLINKPATH;
2359 	}
2360 
2361 	if (alp->forkattr & ATTR_CMNEXT_REALDEVID) {
2362 		if (mp) {
2363 			ATTR_PACK4((*abp),
2364 			    mp->mnt_vfsstat.f_fsid.val[0]);
2365 			abp->actual.forkattr |= ATTR_CMNEXT_REALDEVID;
2366 		} else if (vp) {
2367 			ATTR_PACK4((*abp),
2368 			    vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
2369 			abp->actual.forkattr |= ATTR_CMNEXT_REALDEVID;
2370 		} else if (VATTR_IS_SUPPORTED(vap, va_fsid)) {
2371 			ATTR_PACK4((*abp), vap->va_fsid);
2372 			abp->actual.forkattr |= ATTR_CMN_DEVID;
2373 		} else if (!return_valid || pack_invalid) {
2374 			ATTR_PACK4((*abp), 0);
2375 		}
2376 	}
2377 
2378 	if (alp->forkattr & ATTR_CMNEXT_REALFSID) {
2379 		if (mp) {
2380 			ATTR_PACK8((*abp),
2381 			    mp->mnt_vfsstat.f_fsid);
2382 			abp->actual.forkattr |= ATTR_CMNEXT_REALFSID;
2383 		} else if (vp) {
2384 			ATTR_PACK8((*abp),
2385 			    vp->v_mount->mnt_vfsstat.f_fsid);
2386 			abp->actual.forkattr |= ATTR_CMNEXT_REALFSID;
2387 		} else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) {
2388 			ATTR_PACK8((*abp), vap->va_fsid64);
2389 			abp->actual.forkattr |= ATTR_CMN_FSID;
2390 		} else if (!return_valid || pack_invalid) {
2391 			fsid_t fsid = {{0}};
2392 
2393 			ATTR_PACK8((*abp), fsid);
2394 		}
2395 	}
2396 
2397 	if (alp->forkattr & ATTR_CMNEXT_CLONEID) {
2398 		if (VATTR_IS_SUPPORTED(vap, va_clone_id)) {
2399 			ATTR_PACK8((*abp), vap->va_clone_id);
2400 			abp->actual.forkattr |= ATTR_CMNEXT_CLONEID;
2401 		} else if (!return_valid || pack_invalid) {
2402 			uint64_t zero_val = 0;
2403 			ATTR_PACK8((*abp), zero_val);
2404 		}
2405 	}
2406 
2407 	if (alp->forkattr & ATTR_CMNEXT_EXT_FLAGS) {
2408 		if (VATTR_IS_SUPPORTED(vap, va_extflags)) {
2409 			ATTR_PACK8((*abp), vap->va_extflags);
2410 			abp->actual.forkattr |= ATTR_CMNEXT_EXT_FLAGS;
2411 		} else if (!return_valid || pack_invalid) {
2412 			uint64_t zero_val = 0;
2413 			ATTR_PACK8((*abp), zero_val);
2414 		}
2415 	}
2416 
2417 	if (alp->forkattr & ATTR_CMNEXT_RECURSIVE_GENCOUNT) {
2418 		if (VATTR_IS_SUPPORTED(vap, va_recursive_gencount)) {
2419 			ATTR_PACK8((*abp), vap->va_recursive_gencount);
2420 			abp->actual.forkattr |= ATTR_CMNEXT_RECURSIVE_GENCOUNT;
2421 		} else if (!return_valid || pack_invalid) {
2422 			uint64_t zero_val = 0;
2423 			ATTR_PACK8((*abp), zero_val);
2424 		}
2425 	}
2426 
2427 	return 0;
2428 }
2429 
2430 static void
vattr_get_alt_data(vnode_t vp,struct attrlist * alp,struct vnode_attr * vap,int return_valid,int is_bulk,__unused int is_realdev,vfs_context_t ctx)2431 vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
2432     int return_valid, int is_bulk,
2433 #if !CONFIG_FIRMLINKS
2434     __unused
2435 #endif
2436     int is_realdev, vfs_context_t ctx)
2437 {
2438 	/*
2439 	 * There are a couple of special cases.
2440 	 * If we are after object IDs, we can make do with va_fileid.
2441 	 */
2442 	if ((alp->commonattr &
2443 	    (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) &&
2444 	    !VATTR_IS_SUPPORTED(vap, va_linkid)) {
2445 		/* forget we wanted this */
2446 		VATTR_CLEAR_ACTIVE(vap, va_linkid);
2447 	}
2448 
2449 	/*
2450 	 * A filesystem may not support va_fsid64.  If it is not available, then we'll
2451 	 * synthesize it from the mount.
2452 	 */
2453 	if ((alp->commonattr & ATTR_CMN_FSID) && !VATTR_IS_SUPPORTED(vap, va_fsid64)) {
2454 		VATTR_CLEAR_ACTIVE(vap, va_fsid64);
2455 	}
2456 
2457 	/* Same for fsid */
2458 	if ((alp->commonattr & ATTR_CMN_FSID) && !VATTR_IS_SUPPORTED(vap, va_fsid)) {
2459 		VATTR_CLEAR_ACTIVE(vap, va_fsid);
2460 	}
2461 
2462 	/* We request the fsid64 for the devid */
2463 	if ((alp->commonattr & ATTR_CMN_DEVID) && !VATTR_IS_SUPPORTED(vap, va_fsid)) {
2464 		VATTR_CLEAR_ACTIVE(vap, va_fsid);
2465 	}
2466 
2467 
2468 	/*
2469 	 * Many filesystems don't know their parent object id.
2470 	 * If necessary, attempt to derive it from the vnode.
2471 	 */
2472 	if ((alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) && vp) {
2473 		vnode_t dvp;
2474 
2475 #if CONFIG_FIRMLINKS
2476 		/* If this is a firmlink target, we get the fileid of the firmlink parent. */
2477 		if (!is_realdev && (vp->v_flag & VFMLINKTARGET) && ((dvp = vp->v_fmlink) != NULL) && (vnode_get(dvp) == 0)) {
2478 			struct vnode_attr lva;
2479 
2480 			VATTR_INIT(&lva);
2481 			VATTR_WANTED(&lva, va_parentid);
2482 			VATTR_WANTED(&lva, va_fsid);
2483 			if (vnode_getattr(dvp, &lva, ctx) == 0 &&
2484 			    VATTR_IS_SUPPORTED(&lva, va_parentid) &&
2485 			    VATTR_IS_SUPPORTED(&lva, va_fsid) &&
2486 			    (lva.va_fsid == (uint32_t)vp->v_mount->mnt_vfsstat.f_fsid.val[0])) {
2487 				vap->va_parentid = lva.va_parentid;
2488 				VATTR_SET_SUPPORTED(vap, va_parentid);
2489 			}
2490 			vnode_put(dvp);
2491 		} else
2492 #endif /* CONFIG_FIRMLINKS */
2493 		if (!VATTR_IS_SUPPORTED(vap, va_parentid) && !is_bulk) {
2494 			if ((dvp = vnode_getparent(vp)) != NULLVP) {
2495 				struct vnode_attr lva;
2496 
2497 				VATTR_INIT(&lva);
2498 				VATTR_WANTED(&lva, va_fileid);
2499 				if (vnode_getattr(dvp, &lva, ctx) == 0 &&
2500 				    VATTR_IS_SUPPORTED(vap, va_fileid)) {
2501 					vap->va_parentid = lva.va_fileid;
2502 					VATTR_SET_SUPPORTED(vap, va_parentid);
2503 				}
2504 				vnode_put(dvp);
2505 			}
2506 		}
2507 	}
2508 
2509 	/*
2510 	 * And we can report datasize/alloc from total.
2511 	 */
2512 	if ((alp->fileattr & ATTR_FILE_DATALENGTH) &&
2513 	    !VATTR_IS_SUPPORTED(vap, va_data_size)) {
2514 		VATTR_CLEAR_ACTIVE(vap, va_data_size);
2515 	}
2516 
2517 	if ((alp->fileattr & ATTR_FILE_DATAALLOCSIZE) &&
2518 	    !VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
2519 		VATTR_CLEAR_ACTIVE(vap, va_data_alloc);
2520 	}
2521 
2522 	/*
2523 	 * If we don't have an encoding, go with UTF-8
2524 	 */
2525 	if ((alp->commonattr & ATTR_CMN_SCRIPT) &&
2526 	    !VATTR_IS_SUPPORTED(vap, va_encoding) && !return_valid) {
2527 		VATTR_RETURN(vap, va_encoding,
2528 		    0x7e /* kTextEncodingMacUnicode */);
2529 	}
2530 
2531 	/*
2532 	 * If we don't have a name, we'll get one from the vnode or
2533 	 * mount point.
2534 	 */
2535 	if ((alp->commonattr & ATTR_CMN_NAME) &&
2536 	    !VATTR_IS_SUPPORTED(vap, va_name)) {
2537 		VATTR_CLEAR_ACTIVE(vap, va_name);
2538 	}
2539 
2540 	/* If va_dirlinkcount isn't supported use a default of 1. */
2541 	if ((alp->dirattr & ATTR_DIR_LINKCOUNT) &&
2542 	    !VATTR_IS_SUPPORTED(vap, va_dirlinkcount)) {
2543 		VATTR_RETURN(vap, va_dirlinkcount, 1);
2544 	}
2545 }
2546 
2547 struct _attrlist_paths {
2548 	char *fullpathptr;
2549 	ssize_t *fullpathlenp;
2550 	char *relpathptr;
2551 	ssize_t *relpathlenp;
2552 	char *REALpathptr;
2553 	ssize_t *REALpathlenp;
2554 };
2555 
2556 static errno_t
calc_varsize(vnode_t vp,struct attrlist * alp,struct vnode_attr * vap,ssize_t * varsizep,struct _attrlist_paths * pathsp,const char ** vnamep,const char ** cnpp,ssize_t * cnlp)2557 calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
2558     ssize_t *varsizep, struct _attrlist_paths *pathsp, const char **vnamep,
2559     const char **cnpp, ssize_t *cnlp)
2560 {
2561 	int error = 0;
2562 
2563 	*varsizep = 0; /* length count */
2564 	/* We may need to fix up the name attribute if requested */
2565 	if (alp->commonattr & ATTR_CMN_NAME) {
2566 		if (VATTR_IS_SUPPORTED(vap, va_name)) {
2567 			vap->va_name[MAXPATHLEN - 1] = '\0';      /* Ensure nul-termination */
2568 			*cnpp = vap->va_name;
2569 			*cnlp = strlen(*cnpp);
2570 		} else if (vp) {
2571 			/* Filesystem did not support getting the name */
2572 			if (vnode_isvroot(vp)) {
2573 				if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
2574 				    vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
2575 					/* special case for boot volume.  Use root name when it's
2576 					 * available (which is the volume name) or just the mount on
2577 					 * name of "/".  we must do this for binary compatibility with
2578 					 * pre Tiger code.  returning nothing for the boot volume name
2579 					 * breaks installers - 3961058
2580 					 */
2581 					*cnpp = *vnamep = vnode_getname(vp);
2582 					if (*cnpp == NULL) {
2583 						/* just use "/" as name */
2584 						*cnpp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
2585 					}
2586 					*cnlp = strlen(*cnpp);
2587 				} else {
2588 					getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, cnpp, cnlp);
2589 				}
2590 			} else {
2591 				*cnpp = *vnamep = vnode_getname(vp);
2592 				*cnlp = 0;
2593 				if (*cnpp != NULL) {
2594 					*cnlp = strlen(*cnpp);
2595 				}
2596 			}
2597 		} else {
2598 			*cnlp = 0;
2599 		}
2600 		*varsizep += roundup(*cnlp + 1, 4);
2601 	}
2602 
2603 	/*
2604 	 * Compute the full path to this vnode, if necessary. This attribute is almost certainly
2605 	 * not supported by any filesystem, so build the path to this vnode at this time.
2606 	 */
2607 	if (vp && (alp->commonattr & ATTR_CMN_FULLPATH)) {
2608 		int len = MAXPATHLEN;
2609 		int err;
2610 
2611 		/* call build_path making sure NOT to use the cache-only behavior */
2612 		err = build_path(vp, pathsp->fullpathptr, len, &len, 0, vfs_context_current());
2613 		if (err) {
2614 			error = err;
2615 			goto out;
2616 		}
2617 		if (pathsp->fullpathptr) {
2618 			*(pathsp->fullpathlenp) = strlen(pathsp->fullpathptr);
2619 		} else {
2620 			*(pathsp->fullpathlenp) = 0;
2621 		}
2622 		*varsizep += roundup(((*(pathsp->fullpathlenp)) + 1), 4);
2623 	}
2624 
2625 	/*
2626 	 * Compute this vnode's volume relative path.
2627 	 */
2628 	if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) {
2629 		int len;
2630 		int err;
2631 
2632 		/* call build_path making sure NOT to use the cache-only behavior */
2633 		err = build_path(vp, pathsp->relpathptr, MAXPATHLEN, &len, BUILDPATH_VOLUME_RELATIVE, vfs_context_current());
2634 		if (err) {
2635 			error = err;
2636 			goto out;
2637 		}
2638 
2639 		//`len' includes trailing null
2640 		*(pathsp->relpathlenp) = len - 1;
2641 		*varsizep += roundup(len, 4);
2642 	}
2643 
2644 	/*
2645 	 * Compute this vnode's real (firmlink free) path.
2646 	 */
2647 	if (vp && (alp->forkattr & ATTR_CMNEXT_NOFIRMLINKPATH)) {
2648 		int len;
2649 		int err;
2650 
2651 		/* call build_path making sure NOT to use the cache-only behavior */
2652 		err = build_path(vp, pathsp->REALpathptr, MAXPATHLEN, &len, BUILDPATH_NO_FIRMLINK, vfs_context_current());
2653 		if (err) {
2654 			error = err;
2655 			goto out;
2656 		}
2657 
2658 		//`len' includes trailing null
2659 		*(pathsp->REALpathlenp) = len - 1;
2660 		*varsizep += roundup(len, 4);
2661 	}
2662 
2663 	/*
2664 	 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
2665 	 *
2666 	 * XXX This needs to change at some point; since the blob is opaque in
2667 	 * user-space this is OK.
2668 	 */
2669 	if ((alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
2670 	    VATTR_IS_SUPPORTED(vap, va_acl) &&
2671 	    (vap->va_acl != NULL)) {
2672 		/*
2673 		 * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
2674 		 * KAUTH_FILESEC_NOACL ourselves
2675 		 */
2676 		if (vap->va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) {
2677 			*varsizep += roundup((KAUTH_FILESEC_SIZE(0)), 4);
2678 		} else {
2679 			*varsizep += roundup((KAUTH_FILESEC_SIZE(vap->va_acl->acl_entrycount)), 4);
2680 		}
2681 	}
2682 
2683 out:
2684 	return error;
2685 }
2686 
2687 static errno_t
vfs_attr_pack_internal(mount_t mp,vnode_t vp,uio_t auio,struct attrlist * alp,uint64_t options,struct vnode_attr * vap,__unused void * fndesc,vfs_context_t ctx,int is_bulk,enum vtype vtype,ssize_t fixedsize)2688 vfs_attr_pack_internal(mount_t mp, vnode_t vp, uio_t auio, struct attrlist *alp,
2689     uint64_t options, struct vnode_attr *vap, __unused void *fndesc,
2690     vfs_context_t ctx, int is_bulk, enum vtype vtype, ssize_t fixedsize)
2691 {
2692 	struct _attrlist_buf ab;
2693 	struct _attrlist_paths apaths = {.fullpathptr = NULL, .fullpathlenp = NULL,
2694 		                         .relpathptr = NULL, .relpathlenp = NULL,
2695 		                         .REALpathptr = NULL, .REALpathlenp = NULL};
2696 	ssize_t buf_size;
2697 	size_t copy_size;
2698 	ssize_t varsize;
2699 	const char *vname = NULL;
2700 	const char *cnp;
2701 	ssize_t cnl;
2702 	char *fullpathptr;
2703 	ssize_t fullpathlen;
2704 	char *relpathptr;
2705 	ssize_t relpathlen;
2706 	char *REALpathptr;
2707 	ssize_t REALpathlen;
2708 	int error;
2709 	int proc_is64;
2710 	int return_valid;
2711 	int pack_invalid;
2712 	int is_realdev;
2713 	int alloc_local_buf;
2714 	const int use_fork = options & FSOPT_ATTR_CMN_EXTENDED;
2715 
2716 	proc_is64 = proc_is64bit(vfs_context_proc(ctx));
2717 	ab.base = NULL;
2718 	cnp = "unknown";
2719 	cnl = 0;
2720 	fullpathptr = NULL;
2721 	fullpathlen = 0;
2722 	relpathptr = NULL;
2723 	relpathlen = 0;
2724 	REALpathptr = NULL;
2725 	REALpathlen = 0;
2726 	error = 0;
2727 	alloc_local_buf = 0;
2728 
2729 	buf_size = (ssize_t)uio_resid(auio);
2730 	if ((buf_size <= 0) || (uio_iovcnt(auio) > 1)) {
2731 		return EINVAL;
2732 	}
2733 
2734 	copy_size = 0;
2735 	/* Check for special packing semantics */
2736 	return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
2737 	pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
2738 	is_realdev = options & FSOPT_RETURN_REALDEV ? 1 : 0;
2739 
2740 	if (pack_invalid) {
2741 		/* Generate a valid mask for post processing */
2742 		bcopy(&(alp->commonattr), &ab.valid, sizeof(attribute_set_t));
2743 	}
2744 
2745 	/* did we ask for something the filesystem doesn't support? */
2746 	if (vap->va_active &&
2747 	    (!VATTR_ALL_SUPPORTED(vap)
2748 #if CONFIG_FIRMLINKS
2749 	    /* For firmlink targets we have to overide what the FS returned for parentid */
2750 	    ||
2751 	    (!is_realdev && vp && (vp->v_flag & VFMLINKTARGET) && vp->v_fmlink &&
2752 	    (alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)))
2753 #endif
2754 	    )) {
2755 		// this disables the selectors that were not supported by the filesystem
2756 		vattr_get_alt_data(vp, alp, vap, return_valid, is_bulk, is_realdev,
2757 		    ctx);
2758 
2759 		/* check again */
2760 		if (!VATTR_ALL_SUPPORTED(vap)) {
2761 			if (return_valid && pack_invalid) {
2762 				/* Fix up valid mask for post processing */
2763 				getattrlist_fixupattrs(&ab.valid, vap, use_fork);
2764 
2765 				/* Force packing of everything asked for */
2766 				vap->va_supported = vap->va_active;
2767 			} else if (return_valid) {
2768 				/* Adjust the requested attributes */
2769 				getattrlist_fixupattrs(
2770 					(attribute_set_t *)&(alp->commonattr), vap, use_fork);
2771 			} else {
2772 				error = EINVAL;
2773 			}
2774 		}
2775 
2776 		if (error) {
2777 			goto out;
2778 		}
2779 	}
2780 
2781 	//if a path is requested, allocate a temporary buffer to build it
2782 	if (vp && (alp->commonattr & (ATTR_CMN_FULLPATH))) {
2783 		fullpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
2784 		apaths.fullpathptr = fullpathptr;
2785 		apaths.fullpathlenp = &fullpathlen;
2786 	}
2787 
2788 	// only interpret fork attributes if they're used as new common attributes
2789 	if (vp && use_fork) {
2790 		if (alp->forkattr & (ATTR_CMNEXT_RELPATH)) {
2791 			relpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
2792 			apaths.relpathptr = relpathptr;
2793 			apaths.relpathlenp = &relpathlen;
2794 		}
2795 
2796 		if (alp->forkattr & (ATTR_CMNEXT_NOFIRMLINKPATH)) {
2797 			REALpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
2798 			apaths.REALpathptr = REALpathptr;
2799 			apaths.REALpathlenp = &REALpathlen;
2800 		}
2801 	}
2802 
2803 	/*
2804 	 * Compute variable-space requirements.
2805 	 */
2806 	error = calc_varsize(vp, alp, vap, &varsize, &apaths, &vname, &cnp, &cnl);
2807 	if (error) {
2808 		goto out;
2809 	}
2810 
2811 	/*
2812 	 * Allocate a target buffer for attribute results.
2813 	 *
2814 	 * Note that we won't ever copy out more than the caller requested, even though
2815 	 * we might have to allocate more than they offer so that the diagnostic checks
2816 	 * don't result in a panic if the caller's buffer is too small.
2817 	 */
2818 	ab.allocated = fixedsize + varsize;
2819 	/* Cast 'allocated' to an unsigned to verify allocation size */
2820 	if (((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
2821 		error = ENOMEM;
2822 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
2823 		goto out;
2824 	}
2825 
2826 	/*
2827 	 * Special handling for bulk calls, align to 8 (and only if enough
2828 	 * space left.
2829 	 */
2830 	if (is_bulk) {
2831 		if (buf_size < ab.allocated) {
2832 			goto out;
2833 		} else {
2834 			ssize_t newlen;
2835 
2836 			newlen = (ab.allocated + 7) & ~0x07;
2837 			/* Align only if enough space for alignment */
2838 			if (newlen <= buf_size) {
2839 				ab.allocated = newlen;
2840 			}
2841 		}
2842 	}
2843 
2844 	/*
2845 	 * See if we can reuse buffer passed in i.e. it is a kernel buffer
2846 	 * and big enough.
2847 	 */
2848 	if (uio_isuserspace(auio) || (buf_size < ab.allocated)) {
2849 		ab.base = kalloc_data(ab.allocated, Z_ZERO | Z_WAITOK);
2850 		alloc_local_buf = 1;
2851 	} else {
2852 		/*
2853 		 * In case this is a kernel buffer and sufficiently
2854 		 * big, this function will try to use that buffer
2855 		 * instead of allocating another buffer and bcopy'ing
2856 		 * into it.
2857 		 *
2858 		 * The calculation below figures out where to start
2859 		 * writing in the buffer and once all the data has been
2860 		 * filled in, uio_resid is updated to reflect the usage
2861 		 * of the buffer.
2862 		 *
2863 		 * uio_offset cannot be used here to determine the
2864 		 * starting location as uio_offset could be set to a
2865 		 * value which has nothing to do the location
2866 		 * in the buffer.
2867 		 */
2868 		ab.base = (char *)uio_curriovbase(auio) +
2869 		    ((ssize_t)uio_curriovlen(auio) - buf_size);
2870 		bzero(ab.base, ab.allocated);
2871 	}
2872 
2873 	if (ab.base == NULL) {
2874 		error = ENOMEM;
2875 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
2876 		goto out;
2877 	}
2878 
2879 
2880 	/* set the S_IFMT bits for the mode */
2881 	if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
2882 		if (vp) {
2883 			switch (vp->v_type) {
2884 			case VREG:
2885 				vap->va_mode |= S_IFREG;
2886 				break;
2887 			case VDIR:
2888 				vap->va_mode |= S_IFDIR;
2889 				break;
2890 			case VBLK:
2891 				vap->va_mode |= S_IFBLK;
2892 				break;
2893 			case VCHR:
2894 				vap->va_mode |= S_IFCHR;
2895 				break;
2896 			case VLNK:
2897 				vap->va_mode |= S_IFLNK;
2898 				break;
2899 			case VSOCK:
2900 				vap->va_mode |= S_IFSOCK;
2901 				break;
2902 			case VFIFO:
2903 				vap->va_mode |= S_IFIFO;
2904 				break;
2905 			default:
2906 				error = EBADF;
2907 				goto out;
2908 			}
2909 		}
2910 	}
2911 
2912 	/*
2913 	 * Pack results into the destination buffer.
2914 	 */
2915 	ab.fixedcursor = ab.base + sizeof(uint32_t);
2916 	if (return_valid) {
2917 		ab.fixedcursor += sizeof(attribute_set_t);
2918 		bzero(&ab.actual, sizeof(ab.actual));
2919 	}
2920 	ab.varcursor = ab.base + fixedsize;
2921 	ab.needed = ab.allocated;
2922 
2923 	/* common attributes ************************************************/
2924 	error = attr_pack_common(ctx, (options & FSOPT_RETURN_REALDEV ? mp : NULL),
2925 	    vp, alp, &ab, vap, proc_is64, cnp, cnl, fullpathptr, fullpathlen,
2926 	    return_valid, pack_invalid, vtype, is_bulk);
2927 
2928 	/* directory attributes *********************************************/
2929 	if (!error && alp->dirattr && (vtype == VDIR)) {
2930 		error = attr_pack_dir(vp, alp, &ab, vap, return_valid, pack_invalid);
2931 	}
2932 
2933 	/* file attributes **************************************************/
2934 	if (!error && alp->fileattr && (vtype != VDIR)) {
2935 		error = attr_pack_file(ctx, vp, alp, &ab, vap, return_valid,
2936 		    pack_invalid, is_bulk);
2937 	}
2938 
2939 	/* common extended attributes *****************************************/
2940 	if (!error && use_fork) {
2941 		error = attr_pack_common_extended(mp, vp, alp, &ab, relpathptr, relpathlen,
2942 		    REALpathptr, REALpathlen, vap, return_valid, pack_invalid);
2943 	}
2944 
2945 	if (error) {
2946 		goto out;
2947 	}
2948 
2949 	/* diagnostic */
2950 	if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize) {
2951 		panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
2952 		    fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
2953 	}
2954 	if (!return_valid && ab.varcursor != (ab.base + ab.needed)) {
2955 		panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
2956 	}
2957 
2958 	/*
2959 	 * In the compatible case, we report the smaller of the required and returned sizes.
2960 	 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
2961 	 * of the result buffer, even if we copied less out.  The caller knows how big a buffer
2962 	 * they gave us, so they can always check for truncation themselves.
2963 	 */
2964 	*(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? (uint32_t)ab.needed : (uint32_t)lmin(ab.allocated, ab.needed);
2965 
2966 	/* Return attribute set output if requested. */
2967 	if (return_valid) {
2968 		ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
2969 		if (pack_invalid) {
2970 			/* Only report the attributes that are valid */
2971 			ab.actual.commonattr &= ab.valid.commonattr;
2972 			ab.actual.dirattr &= ab.valid.dirattr;
2973 			ab.actual.fileattr &= ab.valid.fileattr;
2974 		}
2975 		bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof(ab.actual));
2976 	}
2977 
2978 	copy_size = lmin(buf_size, ab.allocated);
2979 
2980 	/* Only actually copyout as much out as the user buffer can hold */
2981 	if (alloc_local_buf) {
2982 		error = uiomove(ab.base, (int)copy_size, auio);
2983 	} else {
2984 		off_t orig_offset = uio_offset(auio);
2985 
2986 		/*
2987 		 * The buffer in the uio struct was used directly
2988 		 * (i.e. it was a kernel buffer and big enough
2989 		 * to hold the data required) in order to avoid
2990 		 * un-needed allocation and copies.
2991 		 *
2992 		 * At this point, update the resid value to what it
2993 		 * would be if this was the result of a uiomove. The
2994 		 * offset is also incremented, though it may not
2995 		 * mean anything to the caller but that is what
2996 		 * uiomove does as well.
2997 		 */
2998 		uio_setresid(auio, buf_size - copy_size);
2999 		uio_setoffset(auio, orig_offset + (off_t)copy_size);
3000 	}
3001 
3002 out:
3003 	if (vname) {
3004 		vnode_putname(vname);
3005 	}
3006 	if (fullpathptr) {
3007 		zfree(ZV_NAMEI, fullpathptr);
3008 	}
3009 	if (relpathptr) {
3010 		zfree(ZV_NAMEI, relpathptr);
3011 	}
3012 	if (REALpathptr) {
3013 		zfree(ZV_NAMEI, REALpathptr);
3014 	}
3015 	if (alloc_local_buf) {
3016 		kfree_data(ab.base, ab.allocated);
3017 	}
3018 	return error;
3019 }
3020 
3021 errno_t
vfs_attr_pack_ext(mount_t mp,vnode_t vp,uio_t uio,struct attrlist * alp,uint64_t options,struct vnode_attr * vap,__unused void * fndesc,vfs_context_t ctx)3022 vfs_attr_pack_ext(mount_t mp, vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
3023     struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx)
3024 {
3025 	int error;
3026 	ssize_t fixedsize;
3027 	uint64_t orig_active;
3028 	struct attrlist orig_al;
3029 	enum vtype v_type;
3030 
3031 	if (vp) {
3032 		v_type = vnode_vtype(vp);
3033 	} else {
3034 		v_type = vap->va_objtype;
3035 	}
3036 
3037 	orig_al = *alp;
3038 	orig_active = vap->va_active;
3039 	vap->va_active = 0;
3040 
3041 	error = getattrlist_setupvattr_all(alp, vap, v_type, &fixedsize,
3042 	    proc_is64bit(vfs_context_proc(ctx)), options & FSOPT_ATTR_CMN_EXTENDED);
3043 
3044 	if (error) {
3045 		VFS_DEBUG(ctx, vp,
3046 		    "ATTRLIST - ERROR: setup for request failed");
3047 		goto out;
3048 	}
3049 
3050 	error = vfs_attr_pack_internal(mp, vp, uio, alp,
3051 	    options | FSOPT_REPORT_FULLSIZE, vap, NULL, ctx, 1, v_type,
3052 	    fixedsize);
3053 
3054 	VATTR_CLEAR_SUPPORTED_ALL(vap);
3055 	vap->va_active = orig_active;
3056 	*alp = orig_al;
3057 out:
3058 	return error;
3059 }
3060 
3061 errno_t
vfs_attr_pack(vnode_t vp,uio_t uio,struct attrlist * alp,uint64_t options,struct vnode_attr * vap,__unused void * fndesc,vfs_context_t ctx)3062 vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
3063     struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx)
3064 {
3065 	return vfs_attr_pack_ext(NULL, vp, uio, alp, options, vap, fndesc, ctx);
3066 }
3067 
3068 /*
3069  * Obtain attribute information about a filesystem object.
3070  *
3071  * Note: The alt_name parameter can be used by the caller to pass in the vnode
3072  * name obtained from some authoritative source (eg. readdir vnop); where
3073  * filesystems' getattr vnops do not support ATTR_CMN_NAME, the alt_name will be
3074  * used as the ATTR_CMN_NAME attribute returned in vnode_attr.va_name.
3075  *
3076  */
3077 static int
getattrlist_internal(vfs_context_t ctx,vnode_t vp,struct attrlist * alp,user_addr_t attributeBuffer,size_t bufferSize,uint64_t options,enum uio_seg segflg,char * authoritative_name,struct ucred * file_cred)3078 getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
3079     user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
3080     enum uio_seg segflg, char* authoritative_name, struct ucred *file_cred)
3081 {
3082 	struct vnode_attr *va;
3083 	kauth_action_t  action;
3084 	ssize_t         fixedsize;
3085 	char            *va_name;
3086 	int             proc_is64;
3087 	int             error;
3088 	int             return_valid;
3089 	int             pack_invalid;
3090 	int             vtype = 0;
3091 	uio_t           auio;
3092 	uio_stackbuf_t uio_buf[UIO_SIZEOF(1)];
3093 	// must be true for fork attributes to be used as new common attributes
3094 	const int use_fork = (options & FSOPT_ATTR_CMN_EXTENDED) != 0;
3095 
3096 	if (bufferSize < sizeof(uint32_t)) {
3097 		return ERANGE;
3098 	}
3099 
3100 	proc_is64 = proc_is64bit(vfs_context_proc(ctx));
3101 
3102 	if (segflg == UIO_USERSPACE) {
3103 		if (proc_is64) {
3104 			segflg = UIO_USERSPACE64;
3105 		} else {
3106 			segflg = UIO_USERSPACE32;
3107 		}
3108 	}
3109 	auio = uio_createwithbuffer(1, 0, segflg, UIO_READ,
3110 	    &uio_buf[0], sizeof(uio_buf));
3111 	uio_addiov(auio, attributeBuffer, bufferSize);
3112 
3113 	va = kalloc_type(struct vnode_attr, Z_WAITOK);
3114 	VATTR_INIT(va);
3115 	va_name = NULL;
3116 
3117 	if (alp->bitmapcount != ATTR_BIT_MAP_COUNT) {
3118 		error = EINVAL;
3119 		goto out;
3120 	}
3121 
3122 	VFS_DEBUG(ctx, vp, "%p  ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
3123 	    vp, vfs_context_proc(ctx)->p_comm, alp->commonattr, alp->volattr, alp->fileattr, alp->dirattr, alp->forkattr,
3124 	    (options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
3125 
3126 #if CONFIG_MACF
3127 	error = mac_vnode_check_getattrlist(ctx, vp, alp, options);
3128 	if (error) {
3129 		goto out;
3130 	}
3131 #endif /* MAC */
3132 
3133 	/*
3134 	 * It is legal to request volume or file attributes, but not both.
3135 	 *
3136 	 * 26903449 fork attributes can also be requested, but only if they're
3137 	 * interpreted as new, common attributes
3138 	 */
3139 	if (alp->volattr) {
3140 		if (alp->fileattr || alp->dirattr || (alp->forkattr && !use_fork)) {
3141 			error = EINVAL;
3142 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory attributes");
3143 			goto out;
3144 		}
3145 		/* handle volume attribute request */
3146 		error = getvolattrlist(ctx, vp, alp, attributeBuffer,
3147 		    bufferSize, options, segflg, proc_is64);
3148 		goto out;
3149 	}
3150 
3151 	/*
3152 	 * ATTR_CMN_GEN_COUNT and ATTR_CMN_DOCUMENT_ID reuse the bits
3153 	 * originally allocated to ATTR_CMN_NAMEDATTRCOUNT and
3154 	 * ATTR_CMN_NAMEDATTRLIST.
3155 	 */
3156 	if ((alp->commonattr & (ATTR_CMN_GEN_COUNT | ATTR_CMN_DOCUMENT_ID)) &&
3157 	    !(options & FSOPT_ATTR_CMN_EXTENDED)) {
3158 		error = EINVAL;
3159 		goto out;
3160 	}
3161 
3162 	/* common extended attributes require FSOPT_ATTR_CMN_EXTENDED option */
3163 	if (!(use_fork) && (alp->forkattr & ATTR_CMNEXT_VALIDMASK)) {
3164 		error = EINVAL;
3165 		goto out;
3166 	}
3167 
3168 	/* FSOPT_ATTR_CMN_EXTENDED requires forkattrs are not referenced */
3169 	if ((options & FSOPT_ATTR_CMN_EXTENDED) && (alp->forkattr & (ATTR_FORK_VALIDMASK))) {
3170 		error = EINVAL;
3171 		goto out;
3172 	}
3173 
3174 	/* Check for special packing semantics */
3175 	return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
3176 	pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
3177 	if (pack_invalid) {
3178 		/* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
3179 		if (!return_valid || (alp->forkattr && !use_fork)) {
3180 			error = EINVAL;
3181 			goto out;
3182 		}
3183 		/* Keep invalid attrs from being uninitialized */
3184 		bzero(va, sizeof(*va));
3185 	}
3186 
3187 	/* Pick up the vnode type.  If the FS is bad and changes vnode types on us, we
3188 	 * will have a valid snapshot that we can work from here.
3189 	 */
3190 	vtype = vp->v_type;
3191 
3192 	/*
3193 	 * Set up the vnode_attr structure and authorise.
3194 	 */
3195 	if ((error = getattrlist_setupvattr(alp, va, &fixedsize, &action, proc_is64, (vtype == VDIR), use_fork)) != 0) {
3196 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
3197 		goto out;
3198 	}
3199 	if ((error = vnode_authorize(vp, NULL, action, ctx)) != 0) {
3200 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorisation failed/denied");
3201 		goto out;
3202 	}
3203 
3204 
3205 	if (va->va_active != 0) {
3206 		uint64_t va_active = va->va_active;
3207 
3208 		/*
3209 		 * If we're going to ask for va_name, allocate a buffer to point it at
3210 		 */
3211 		if (VATTR_IS_ACTIVE(va, va_name)) {
3212 			va_name = zalloc(ZV_NAMEI);
3213 			/*
3214 			 * If we have an authoritative_name, prefer that name.
3215 			 *
3216 			 * N.B. Since authoritative_name implies this is coming from getattrlistbulk,
3217 			 * we know the name is authoritative. For /dev/fd, we want to use the file
3218 			 * descriptor as the name not the underlying name of the associate vnode in a
3219 			 * particular file system.
3220 			 */
3221 			if (authoritative_name) {
3222 				/* Don't ask the file system */
3223 				VATTR_CLEAR_ACTIVE(va, va_name);
3224 				strlcpy(va_name, authoritative_name, MAXPATHLEN);
3225 			}
3226 		}
3227 
3228 		va->va_name = authoritative_name ? NULL : va_name;
3229 
3230 		if (options & FSOPT_RETURN_REALDEV) {
3231 			va->va_vaflags |= VA_REALFSID;
3232 		}
3233 
3234 		/*
3235 		 * Call the filesystem.
3236 		 */
3237 		if ((error = vnode_getattr(vp, va, ctx)) != 0) {
3238 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
3239 			goto out;
3240 		}
3241 #if CONFIG_MACF
3242 		/*
3243 		 * Give MAC polices a chance to reject or filter the
3244 		 * attributes returned by the filesystem.  Note that MAC
3245 		 * policies are consulted *after* calling the filesystem
3246 		 * because filesystems can return more attributes than
3247 		 * were requested so policies wouldn't be authoritative
3248 		 * is consulted beforehand.  This also gives policies an
3249 		 * opportunity to change the values of attributes
3250 		 * retrieved.
3251 		 */
3252 		error = mac_vnode_check_getattr(ctx, file_cred, vp, va);
3253 		if (error) {
3254 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d", error);
3255 			goto out;
3256 		}
3257 #else
3258 		(void)file_cred;
3259 #endif
3260 
3261 		/*
3262 		 * It we ask for the name, i.e., vname is non null and
3263 		 * we have an authoritative name, then reset va_name is
3264 		 * active and if needed set va_name is supported.
3265 		 *
3266 		 * A (buggy) filesystem may change fields which belong
3267 		 * to us. We try to deal with that here as well.
3268 		 */
3269 		va->va_active = va_active;
3270 		if (authoritative_name && va_name) {
3271 			VATTR_SET_ACTIVE(va, va_name);
3272 			if (!(VATTR_IS_SUPPORTED(va, va_name))) {
3273 				VATTR_SET_SUPPORTED(va, va_name);
3274 			}
3275 		}
3276 		va->va_name = va_name;
3277 	}
3278 
3279 	error = vfs_attr_pack_internal(vp->v_mount, vp, auio, alp, options, va, NULL, ctx,
3280 	    0, vtype, fixedsize);
3281 
3282 out:
3283 	if (va_name) {
3284 		zfree(ZV_NAMEI, va_name);
3285 	}
3286 	if (VATTR_IS_SUPPORTED(va, va_acl) && (va->va_acl != NULL)) {
3287 		kauth_acl_free(va->va_acl);
3288 	}
3289 	kfree_type(struct vnode_attr, va);
3290 
3291 	VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
3292 	return error;
3293 }
3294 
3295 int
fgetattrlist(proc_t p,struct fgetattrlist_args * uap,__unused int32_t * retval)3296 fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
3297 {
3298 	vfs_context_t ctx;
3299 	vnode_t vp;
3300 	int error;
3301 	struct attrlist al;
3302 	struct fileproc *fp;
3303 
3304 	ctx = vfs_context_current();
3305 	vp = NULL;
3306 	fp = NULL;
3307 	error = 0;
3308 
3309 	if ((error = fp_get_ftype(p, uap->fd, DTYPE_VNODE, EINVAL, &fp)) != 0) {
3310 		return error;
3311 	}
3312 	vp = (struct vnode *)fp_get_data(fp);
3313 
3314 	if ((error = vnode_getwithref(vp)) != 0) {
3315 		goto out;
3316 	}
3317 
3318 	/*
3319 	 * Fetch the attribute request.
3320 	 */
3321 	error = copyin(uap->alist, &al, sizeof(al));
3322 	if (error) {
3323 		goto out_vnode_put;
3324 	}
3325 
3326 	/* Default to using the vnode's name. */
3327 	error = getattrlist_internal(ctx, vp, &al, uap->attributeBuffer,
3328 	    uap->bufferSize, uap->options,
3329 	    (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : \
3330 	    UIO_USERSPACE32), NULL,
3331 	    fp->fp_glob->fg_cred);
3332 
3333 out_vnode_put:
3334 	vnode_put(vp);
3335 out:
3336 	fp_drop(p, uap->fd, fp, 0);
3337 
3338 	return error;
3339 }
3340 
3341 static int
getattrlistat_internal(vfs_context_t ctx,user_addr_t path,struct attrlist * alp,user_addr_t attributeBuffer,size_t bufferSize,uint64_t options,enum uio_seg segflg,enum uio_seg pathsegflg,int fd)3342 getattrlistat_internal(vfs_context_t ctx, user_addr_t path,
3343     struct attrlist *alp, user_addr_t attributeBuffer, size_t bufferSize,
3344     uint64_t options, enum uio_seg segflg, enum uio_seg pathsegflg, int fd)
3345 {
3346 	struct nameidata nd;
3347 	vnode_t vp;
3348 	int32_t nameiflags;
3349 	int error;
3350 
3351 	nameiflags = 0;
3352 	/*
3353 	 * Look up the file.
3354 	 */
3355 	if (!(options & FSOPT_NOFOLLOW)) {
3356 		nameiflags |= FOLLOW;
3357 	}
3358 
3359 	nameiflags |= AUDITVNPATH1;
3360 	NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, pathsegflg,
3361 	    path, ctx);
3362 
3363 	error = nameiat(&nd, fd);
3364 
3365 	if (error) {
3366 		return error;
3367 	}
3368 
3369 	vp = nd.ni_vp;
3370 
3371 	error = getattrlist_internal(ctx, vp, alp, attributeBuffer,
3372 	    bufferSize, options, segflg, NULL, NOCRED);
3373 
3374 	/* Retain the namei reference until the getattrlist completes. */
3375 	nameidone(&nd);
3376 	vnode_put(vp);
3377 	return error;
3378 }
3379 
3380 int
getattrlist(proc_t p,struct getattrlist_args * uap,__unused int32_t * retval)3381 getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval)
3382 {
3383 	enum uio_seg segflg;
3384 	struct attrlist al;
3385 	int error;
3386 
3387 	segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
3388 
3389 	/*
3390 	 * Fetch the attribute request.
3391 	 */
3392 	error = copyin(uap->alist, &al, sizeof(al));
3393 	if (error) {
3394 		return error;
3395 	}
3396 
3397 	return getattrlistat_internal(vfs_context_current(),
3398 	           CAST_USER_ADDR_T(uap->path), &al,
3399 	           CAST_USER_ADDR_T(uap->attributeBuffer), uap->bufferSize,
3400 	           (uint64_t)uap->options, segflg, segflg, AT_FDCWD);
3401 }
3402 
3403 int
getattrlistat(proc_t p,struct getattrlistat_args * uap,__unused int32_t * retval)3404 getattrlistat(proc_t p, struct getattrlistat_args *uap, __unused int32_t *retval)
3405 {
3406 	enum uio_seg segflg;
3407 	struct attrlist al;
3408 	int error;
3409 
3410 	segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
3411 
3412 	/*
3413 	 * Fetch the attribute request.
3414 	 */
3415 	error = copyin(uap->alist, &al, sizeof(al));
3416 	if (error) {
3417 		return error;
3418 	}
3419 
3420 	return getattrlistat_internal(vfs_context_current(),
3421 	           CAST_USER_ADDR_T(uap->path), &al,
3422 	           CAST_USER_ADDR_T(uap->attributeBuffer), uap->bufferSize,
3423 	           (uint64_t)uap->options, segflg, segflg, uap->fd);
3424 }
3425 
3426 /*
3427  * This refills the per-fd direntries cache by issuing a VNOP_READDIR.
3428  * It attempts to try and find a size the filesystem responds to, so
3429  * it first tries 1 direntry sized buffer and going from 1 to 2 to 4
3430  * direntry sized buffers to readdir. If the filesystem does not respond
3431  * to 4 * direntry it returns the error by the filesystem (if any) and sets
3432  * EOF.
3433  *
3434  * This function also tries again if the last "refill" returned an EOF
3435  * to try and get any additional entries if they were added after the last
3436  * refill.
3437  */
3438 static int
refill_fd_direntries(vfs_context_t ctx,vnode_t dvp,struct fd_vn_data * fvd,int * eofflagp)3439 refill_fd_direntries(vfs_context_t ctx, vnode_t dvp, struct fd_vn_data *fvd,
3440     int *eofflagp)
3441 {
3442 	uio_t rdir_uio;
3443 	uio_stackbuf_t uio_buf[UIO_SIZEOF(1)];
3444 	size_t rdirbufsiz;
3445 	size_t rdirbufused;
3446 	int eofflag;
3447 	int nentries;
3448 	int error;
3449 
3450 	/*
3451 	 * If the last readdir returned EOF, don't try again.
3452 	 */
3453 	if (fvd->fv_eofflag) {
3454 		*eofflagp = 1;
3455 		if (fvd->fv_buf) {
3456 			kfree_data(fvd->fv_buf, fvd->fv_bufallocsiz);
3457 			fvd->fv_buf = NULL;
3458 		}
3459 		return 0;
3460 	}
3461 
3462 	error = 0;
3463 
3464 	/*
3465 	 * If there is a cached allocation size of the dirbuf that should be
3466 	 * allocated, use that. Otherwise start with a allocation size of
3467 	 * FV_DIRBUF_START_SIZ. This start size may need to be increased if the
3468 	 * filesystem doesn't respond to the initial size.
3469 	 */
3470 
3471 	if (fvd->fv_offset && fvd->fv_bufallocsiz) {
3472 		rdirbufsiz = fvd->fv_bufallocsiz;
3473 	} else {
3474 		rdirbufsiz = FV_DIRBUF_START_SIZ;
3475 	}
3476 
3477 	*eofflagp = 0;
3478 
3479 	rdir_uio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
3480 	    &uio_buf[0], sizeof(uio_buf));
3481 
3482 retry_alloc:
3483 	/*
3484 	 * Don't explicitly zero out this buffer since this is
3485 	 * not copied out to user space.
3486 	 */
3487 	if (!fvd->fv_buf) {
3488 		fvd->fv_buf = kalloc_data(rdirbufsiz, Z_WAITOK);
3489 		fvd->fv_bufallocsiz = rdirbufsiz;
3490 		fvd->fv_bufdone = 0;
3491 	}
3492 
3493 	uio_reset(rdir_uio, fvd->fv_eoff, UIO_SYSSPACE, UIO_READ);
3494 	uio_addiov(rdir_uio, CAST_USER_ADDR_T(fvd->fv_buf), rdirbufsiz);
3495 
3496 	/*
3497 	 * Some filesystems do not set nentries or eofflag...
3498 	 */
3499 	eofflag = 0;
3500 	nentries = 0;
3501 	error = vnode_readdir64(dvp, rdir_uio, VNODE_READDIR_EXTENDED,
3502 	    &eofflag, &nentries, ctx);
3503 
3504 	rdirbufused = rdirbufsiz - (size_t)uio_resid(rdir_uio);
3505 
3506 	if (!error && (rdirbufused > 0) && (rdirbufused <= rdirbufsiz)) {
3507 		/* Save offsets */
3508 		fvd->fv_soff = fvd->fv_eoff;
3509 		fvd->fv_eoff = uio_offset(rdir_uio);
3510 		/* Save eofflag state but don't return EOF for this time.*/
3511 		fvd->fv_eofflag = eofflag;
3512 		eofflag = 0;
3513 		/* Reset buffer parameters */
3514 		fvd->fv_bufsiz = rdirbufused;
3515 		fvd->fv_bufdone = 0;
3516 		bzero(fvd->fv_buf + rdirbufused, rdirbufsiz - rdirbufused);
3517 	} else if (!eofflag && (rdirbufsiz < FV_DIRBUF_MAX_SIZ)) {
3518 		/*
3519 		 * Some Filesystems have higher requirements for the
3520 		 * smallest buffer size they will respond to for a
3521 		 * directory listing. Start (relatively) small but increase
3522 		 * it upto FV_DIRBUF_MAX_SIZ. Most should be good with
3523 		 * 1*direntry. Cache the size found so that this does not need
3524 		 * need to be done every time. This also means that an error
3525 		 * from VNOP_READDIR is ignored until at least FV_DIRBUF_MAX_SIZ
3526 		 * has been attempted.
3527 		 */
3528 		kfree_data(fvd->fv_buf, fvd->fv_bufallocsiz);
3529 		rdirbufsiz = 2 * rdirbufsiz;
3530 		fvd->fv_bufallocsiz = 0;
3531 		goto retry_alloc;
3532 	} else if (!error) {
3533 		/*
3534 		 * The Filesystem did not set eofflag but also did not
3535 		 * return any entries (or an error). It is presumed that
3536 		 * EOF has been reached.
3537 		 */
3538 		fvd->fv_eofflag = eofflag = 1;
3539 	}
3540 
3541 	/*
3542 	 * If the filesystem returned an error and it had previously returned
3543 	 * EOF, ignore the error and set EOF.
3544 	 */
3545 	if (error && fvd->fv_eofflag) {
3546 		eofflag = 1;
3547 		error = 0;
3548 	}
3549 
3550 	/*
3551 	 * If either the directory has either hit EOF or an error, now is a good
3552 	 * time to free up directory entry buffer.
3553 	 */
3554 	if ((error || eofflag) && fvd->fv_buf) {
3555 		kfree_data(fvd->fv_buf, fvd->fv_bufallocsiz);
3556 		if (error) {
3557 			fvd->fv_bufallocsiz = 0;
3558 		}
3559 	}
3560 
3561 	*eofflagp = eofflag;
3562 
3563 	return error;
3564 }
3565 
3566 /*
3567  * gets the current direntry. To advance to the next direntry this has to be
3568  * paired with a direntry_done.
3569  *
3570  * Since directories have restrictions on where directory enumeration
3571  * can restart from, entries are first read into* a per fd diectory entry
3572  * "cache" and entries provided from that cache.
3573  */
3574 static int
get_direntry(vfs_context_t ctx,vnode_t dvp,struct fd_vn_data * fvd,int * eofflagp,struct direntry ** dpp)3575 get_direntry(vfs_context_t ctx, vnode_t dvp, struct fd_vn_data *fvd,
3576     int *eofflagp, struct direntry **dpp)
3577 {
3578 	int eofflag;
3579 	int error;
3580 
3581 	*eofflagp = 0;
3582 	*dpp = NULL;
3583 	error = 0;
3584 	if (!fvd->fv_bufsiz) {
3585 		error = refill_fd_direntries(ctx, dvp, fvd, &eofflag);
3586 		if (error) {
3587 			return error;
3588 		}
3589 		if (eofflag) {
3590 			*eofflagp = eofflag;
3591 			return error;
3592 		}
3593 	}
3594 
3595 	*dpp = (struct direntry *)(fvd->fv_buf + fvd->fv_bufdone);
3596 	return error;
3597 }
3598 
3599 /*
3600  * Advances to the next direntry.
3601  */
3602 static void
direntry_done(struct fd_vn_data * fvd)3603 direntry_done(struct fd_vn_data *fvd)
3604 {
3605 	struct direntry *dp;
3606 
3607 	dp = (struct direntry *)(fvd->fv_buf + fvd->fv_bufdone);
3608 	if (dp->d_reclen) {
3609 		fvd->fv_bufdone += dp->d_reclen;
3610 		if (fvd->fv_bufdone > fvd->fv_bufsiz) {
3611 			fvd->fv_bufdone = fvd->fv_bufsiz;
3612 		}
3613 	} else {
3614 		fvd->fv_bufdone = fvd->fv_bufsiz;
3615 	}
3616 
3617 	/*
3618 	 * If we're at the end the fd direntries cache, reset the
3619 	 * cache trackers.
3620 	 */
3621 	if (fvd->fv_bufdone == fvd->fv_bufsiz) {
3622 		fvd->fv_bufdone = 0;
3623 		fvd->fv_bufsiz = 0;
3624 	}
3625 }
3626 
3627 /*
3628  *  A stripped down version of getattrlist_internal to fill in only select
3629  *  attributes in case of an error from getattrlist_internal.
3630  *
3631  *  It always returns at least ATTR_BULK_REQUIRED i.e. the name (but may also
3632  *  return some other attributes which can be obtained from the vnode).
3633  *
3634  *  It does not change the value of the passed in attrlist.
3635  *
3636  *  The objective of this function is to fill in an "error entry", i.e.
3637  *  an entry with ATTR_CMN_RETURNED_ATTRS & ATTR_CMN_NAME. If the caller
3638  *  has also asked for ATTR_CMN_ERROR, it is filled in as well.
3639  *
3640  *  Input
3641  *       vp - vnode pointer
3642  *       alp - pointer to attrlist struct.
3643  *       options - options passed to getattrlistbulk(2)
3644  *       kern_attr_buf - Kernel buffer to fill data (assumes offset 0 in
3645  *           buffer)
3646  *       kern_attr_buf_siz - Size of buffer.
3647  *       needs_error_attr - Whether the caller asked for ATTR_CMN_ERROR
3648  *       error_attr - This value is used to fill ATTR_CMN_ERROR (if the user
3649  *                  has requested it in the attribute list.
3650  *       namebuf - This is used to fill in the name.
3651  *       ctx - vfs context of caller.
3652  */
3653 static void
get_error_attributes(vnode_t vp,struct attrlist * alp,uint64_t options,user_addr_t kern_attr_buf,size_t kern_attr_buf_siz,int error_attr,caddr_t namebuf,vfs_context_t ctx)3654 get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options,
3655     user_addr_t kern_attr_buf, size_t kern_attr_buf_siz, int error_attr,
3656     caddr_t namebuf, vfs_context_t ctx)
3657 {
3658 	size_t fsiz, vsiz;
3659 	struct _attrlist_buf ab;
3660 	size_t namelen;
3661 	kauth_action_t action;
3662 	struct attrlist al;
3663 	int needs_error_attr = (alp->commonattr & ATTR_CMN_ERROR);
3664 
3665 	/*
3666 	 * To calculate fixed size required, in the FSOPT_PACK_INVAL_ATTRS case,
3667 	 * the fixedsize should include space for all the attributes asked by
3668 	 * the user. Only ATTR_BULK_REQUIRED (and ATTR_CMN_ERROR) will be filled
3669 	 * and will be valid. All other attributes are zeroed out later.
3670 	 *
3671 	 * ATTR_CMN_RETURNED_ATTRS, ATTR_CMN_ERROR and ATTR_CMN_NAME
3672 	 * (the only valid ones being returned from here) happen to be
3673 	 * the first three attributes by order as well.
3674 	 */
3675 	al = *alp;
3676 	if (!(options & FSOPT_PACK_INVAL_ATTRS)) {
3677 		/*
3678 		 * In this case the fixedsize only needs to be only for the
3679 		 * attributes being actually returned.
3680 		 */
3681 		al.commonattr = ATTR_BULK_REQUIRED;
3682 		if (needs_error_attr) {
3683 			al.commonattr |= ATTR_CMN_ERROR;
3684 		}
3685 		al.fileattr = 0;
3686 		al.dirattr = 0;
3687 	}
3688 
3689 	/*
3690 	 * Passing NULL for the vnode_attr pointer is valid for
3691 	 * getattrlist_setupvattr. All that is required is the size.
3692 	 */
3693 	fsiz = 0;
3694 	(void)getattrlist_setupvattr(&al, NULL, (ssize_t *)&fsiz,
3695 	    &action, proc_is64bit(vfs_context_proc(ctx)),
3696 	    (vnode_vtype(vp) == VDIR), (options & FSOPT_ATTR_CMN_EXTENDED));
3697 
3698 	namelen = strlen(namebuf);
3699 	vsiz = namelen + 1;
3700 	vsiz = ((vsiz + 3) & ~0x03);
3701 
3702 	bzero(&ab, sizeof(ab));
3703 	ab.base = (char *)kern_attr_buf;
3704 	ab.needed = fsiz + vsiz;
3705 
3706 	/* Fill in the size needed */
3707 	*((uint32_t *)ab.base) = (uint32_t)ab.needed;
3708 	if (ab.needed > (ssize_t)kern_attr_buf_siz) {
3709 		goto out;
3710 	}
3711 
3712 	/*
3713 	 * Setup to pack results into the destination buffer.
3714 	 */
3715 	ab.fixedcursor = ab.base + sizeof(uint32_t);
3716 	/*
3717 	 * Zero out buffer, ab.fixedbuffer starts after the first uint32_t
3718 	 * which gives the length. This ensures everything that we don't
3719 	 * fill in explicitly later is zeroed out correctly.
3720 	 */
3721 	bzero(ab.fixedcursor, fsiz);
3722 	/*
3723 	 * variable size data should start after all the fixed
3724 	 * size data.
3725 	 */
3726 	ab.varcursor = ab.base + fsiz;
3727 	/*
3728 	 * Initialise the value for ATTR_CMN_RETURNED_ATTRS and leave space
3729 	 * Leave space for filling in its value here at the end.
3730 	 */
3731 	bzero(&ab.actual, sizeof(ab.actual));
3732 	ab.fixedcursor += sizeof(attribute_set_t);
3733 
3734 	ab.allocated = ab.needed;
3735 
3736 	/* Fill ATTR_CMN_ERROR (if asked for) */
3737 	if (needs_error_attr) {
3738 		ATTR_PACK4(ab, error_attr);
3739 		ab.actual.commonattr |= ATTR_CMN_ERROR;
3740 	}
3741 
3742 	/*
3743 	 * Fill ATTR_CMN_NAME, The attrrefrence is packed at this location
3744 	 * but the actual string itself is packed after fixedsize which set
3745 	 * to different lengths based on whether FSOPT_PACK_INVAL_ATTRS
3746 	 * was passed.
3747 	 */
3748 	attrlist_pack_string(&ab, namebuf, namelen);
3749 
3750 	/*
3751 	 * Now Fill in ATTR_CMN_RETURNED_ATTR. This copies to a
3752 	 * location after the count i.e. before ATTR_CMN_ERROR and
3753 	 * ATTR_CMN_NAME.
3754 	 */
3755 	ab.actual.commonattr |= ATTR_CMN_NAME | ATTR_CMN_RETURNED_ATTRS;
3756 	bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof(ab.actual));
3757 out:
3758 	return;
3759 }
3760 
3761 /*
3762  * This is the buffer size required to return at least 1 entry. We need space
3763  * for the length, for ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_NAME. Assuming the
3764  * smallest filename of a single byte we get
3765  */
3766 
3767 #define MIN_BUF_SIZE_REQUIRED  (sizeof(uint32_t) + sizeof(attribute_set_t) +\
3768     sizeof(attrreference_t))
3769 
3770 /*
3771  * Read directory entries and get attributes filled in for each directory
3772  */
3773 static int
readdirattr(vnode_t dvp,struct fd_vn_data * fvd,uio_t auio,struct attrlist * alp,uint64_t options,int * count,int * eofflagp,vfs_context_t ctx)3774 readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
3775     struct attrlist *alp, uint64_t options, int *count, int *eofflagp,
3776     vfs_context_t ctx)
3777 {
3778 	caddr_t kern_attr_buf;
3779 	size_t kern_attr_buf_siz;
3780 	caddr_t max_path_name_buf = NULL;
3781 	int error = 0;
3782 
3783 	*count = 0;
3784 	*eofflagp = 0;
3785 
3786 	if (uio_iovcnt(auio) > 1) {
3787 		return EINVAL;
3788 	}
3789 
3790 	/*
3791 	 * We fill in a kernel buffer for the attributes and uiomove each
3792 	 * entry's attributes (as returned by getattrlist_internal)
3793 	 */
3794 	kern_attr_buf_siz = uio_resid(auio);
3795 	if (kern_attr_buf_siz > ATTR_MAX_BUFFER) {
3796 		kern_attr_buf_siz = ATTR_MAX_BUFFER;
3797 	} else if (kern_attr_buf_siz == 0) {
3798 		/* Nothing to do */
3799 		return error;
3800 	}
3801 
3802 	kern_attr_buf = kalloc_data(kern_attr_buf_siz, Z_WAITOK);
3803 
3804 	while (uio_resid(auio) > (user_ssize_t)MIN_BUF_SIZE_REQUIRED) {
3805 		struct direntry *dp;
3806 		user_addr_t name_buffer;
3807 		struct nameidata nd;
3808 		vnode_t vp;
3809 		struct attrlist al;
3810 		size_t entlen;
3811 		size_t bytes_left;
3812 		size_t pad_bytes;
3813 		ssize_t new_resid;
3814 
3815 		/*
3816 		 * get_direntry returns the current direntry and does not
3817 		 * advance. A move to the next direntry only happens if
3818 		 * direntry_done is called.
3819 		 */
3820 		error = get_direntry(ctx, dvp, fvd, eofflagp, &dp);
3821 		if (error || (*eofflagp) || !dp) {
3822 			break;
3823 		}
3824 
3825 		/*
3826 		 * skip "." and ".." (and a bunch of other invalid conditions.)
3827 		 */
3828 		if (!dp->d_reclen || dp->d_ino == 0 || dp->d_namlen == 0 ||
3829 		    (dp->d_namlen == 1 && dp->d_name[0] == '.') ||
3830 		    (dp->d_namlen == 2 && dp->d_name[0] == '.' &&
3831 		    dp->d_name[1] == '.')) {
3832 			direntry_done(fvd);
3833 			continue;
3834 		}
3835 
3836 		/*
3837 		 * try to deal with not-null terminated filenames.
3838 		 */
3839 		if (dp->d_name[dp->d_namlen] != '\0') {
3840 			if (!max_path_name_buf) {
3841 				max_path_name_buf = zalloc_flags(ZV_NAMEI, Z_WAITOK);
3842 			}
3843 			bcopy(dp->d_name, max_path_name_buf, dp->d_namlen);
3844 			max_path_name_buf[dp->d_namlen] = '\0';
3845 			name_buffer = CAST_USER_ADDR_T(max_path_name_buf);
3846 		} else {
3847 			name_buffer = CAST_USER_ADDR_T(&(dp->d_name));
3848 		}
3849 
3850 		/*
3851 		 * We have an iocount on the directory already.
3852 		 *
3853 		 * Note that we supply NOCROSSMOUNT to the namei call as we attempt to acquire
3854 		 * a vnode for this particular entry.  This is because the native call will
3855 		 * (likely) attempt to emit attributes based on its own metadata in order to avoid
3856 		 * creating vnodes where posssible.  If the native call is not going to  walk
3857 		 * up the vnode mounted-on chain in order to find the top-most mount point, then we
3858 		 * should not either in this emulated readdir+getattrlist() approach.  We
3859 		 * will be responsible for setting DIR_MNTSTATUS_MNTPOINT on that directory that
3860 		 * contains a mount point.
3861 		 */
3862 		NDINIT(&nd, LOOKUP, OP_GETATTR, (AUDITVNPATH1 | USEDVP | NOCROSSMOUNT),
3863 		    UIO_SYSSPACE, CAST_USER_ADDR_T(name_buffer), ctx);
3864 
3865 		nd.ni_dvp = dvp;
3866 		error = namei(&nd);
3867 
3868 		if (error) {
3869 			direntry_done(fvd);
3870 			error = 0;
3871 			continue;
3872 		}
3873 
3874 		vp = nd.ni_vp;
3875 
3876 		/*
3877 		 * getattrlist_internal can change the values of the
3878 		 * the required attribute list. Copy the current values
3879 		 * and use that one instead.
3880 		 */
3881 		al = *alp;
3882 
3883 		error = getattrlist_internal(ctx, vp, &al,
3884 		    CAST_USER_ADDR_T(kern_attr_buf), kern_attr_buf_siz,
3885 		    options | FSOPT_REPORT_FULLSIZE, UIO_SYSSPACE,
3886 		    CAST_DOWN_EXPLICIT(char *, name_buffer),
3887 		    NOCRED);
3888 
3889 		nameidone(&nd);
3890 
3891 		if (error) {
3892 			get_error_attributes(vp, alp, options,
3893 			    CAST_USER_ADDR_T(kern_attr_buf),
3894 			    kern_attr_buf_siz, error, (caddr_t)name_buffer,
3895 			    ctx);
3896 			error = 0;
3897 		}
3898 
3899 		/* Done with vnode now */
3900 		vnode_put(vp);
3901 
3902 		/*
3903 		 * Because FSOPT_REPORT_FULLSIZE was set, the first 4 bytes
3904 		 * of the buffer returned by getattrlist contains the size
3905 		 * (even if the provided buffer isn't sufficiently big). Use
3906 		 * that to check if we've run out of buffer space.
3907 		 *
3908 		 * resid is a signed type, and the size of the buffer etc
3909 		 * are unsigned types. It is theoretically possible for
3910 		 * resid to be < 0 and in which case we would be assigning
3911 		 * an out of bounds value to bytes_left (which is unsigned)
3912 		 * uiomove takes care to not ever set resid to < 0, so it
3913 		 * is safe to do this here.
3914 		 */
3915 		bytes_left = (size_t)((user_size_t)uio_resid(auio));
3916 		entlen = (size_t)(*((uint32_t *)(kern_attr_buf)));
3917 		if (!entlen || (entlen > bytes_left)) {
3918 			break;
3919 		}
3920 
3921 		/*
3922 		 * Will the pad bytes fit as well  ? If they can't be, still use
3923 		 * this entry but this will be the last entry returned.
3924 		 */
3925 		pad_bytes = ((entlen + 7) & ~0x07) - entlen;
3926 		new_resid = 0;
3927 		if (pad_bytes && (entlen + pad_bytes <= bytes_left)) {
3928 			/*
3929 			 * While entlen can never be > ATTR_MAX_BUFFER,
3930 			 * (entlen + pad_bytes) can be, handle that and
3931 			 * zero out the pad bytes. N.B. - Only zero
3932 			 * out information in the kernel buffer that is
3933 			 * going to be uiomove'ed out.
3934 			 */
3935 			if (entlen + pad_bytes <= kern_attr_buf_siz) {
3936 				/* This is the normal case. */
3937 				bzero(kern_attr_buf + entlen, pad_bytes);
3938 			} else {
3939 				bzero(kern_attr_buf + entlen,
3940 				    kern_attr_buf_siz - entlen);
3941 				/*
3942 				 * Pad bytes left over, change the resid value
3943 				 * manually. We only got in here because
3944 				 * bytes_left >= entlen + pad_bytes so
3945 				 * new_resid (which is a signed type) is
3946 				 * always positive.
3947 				 */
3948 				new_resid = (ssize_t)(bytes_left -
3949 				    (entlen + pad_bytes));
3950 			}
3951 			entlen += pad_bytes;
3952 		}
3953 		*((uint32_t *)kern_attr_buf) = (uint32_t)entlen;
3954 		error = uiomove(kern_attr_buf, min((int)entlen, (int)kern_attr_buf_siz),
3955 		    auio);
3956 
3957 		if (error) {
3958 			break;
3959 		}
3960 
3961 		if (new_resid) {
3962 			uio_setresid(auio, (user_ssize_t)new_resid);
3963 		}
3964 
3965 		/*
3966 		 * At this point, the directory entry has been consumed, proceed
3967 		 * to the next one.
3968 		 */
3969 		(*count)++;
3970 		direntry_done(fvd);
3971 	}
3972 
3973 	if (max_path_name_buf) {
3974 		zfree(ZV_NAMEI, max_path_name_buf);
3975 	}
3976 
3977 	/*
3978 	 * At this point, kern_attr_buf is always allocated
3979 	 */
3980 	kfree_data(kern_attr_buf, kern_attr_buf_siz);
3981 
3982 	/*
3983 	 * Always set the offset to the last succesful offset
3984 	 * returned by VNOP_READDIR.
3985 	 */
3986 	uio_setoffset(auio, fvd->fv_eoff);
3987 
3988 	return error;
3989 }
3990 
3991 /* common attributes that only require KAUTH_VNODE_LIST_DIRECTORY */
3992 #define LIST_DIR_ATTRS    (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |  \
3993 	                   ATTR_CMN_FILEID | ATTR_CMN_RETURNED_ATTRS |  \
3994 	                   ATTR_CMN_ERROR)
3995 
3996 /*
3997  * int getattrlistbulk(int dirfd, struct attrlist *alist, void *attributeBuffer,
3998  *    size_t bufferSize, uint64_t options)
3999  *
4000  * Gets directory entries alongwith their attributes in the same way
4001  * getattrlist does for a single file system object.
4002  *
4003  * On non error returns, retval will hold the count of entries returned.
4004  */
4005 int
getattrlistbulk(proc_t p,struct getattrlistbulk_args * uap,int32_t * retval)4006 getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
4007 {
4008 	struct attrlist al;
4009 	vnode_t dvp = NULLVP;
4010 	struct fileproc *fp;
4011 	struct fd_vn_data *fvdata;
4012 	vfs_context_t ctx;
4013 	uthread_t ut;
4014 	enum uio_seg segflg;
4015 	int count;
4016 	uio_t auio = NULL;
4017 	uio_stackbuf_t uio_buf[UIO_SIZEOF(1)];
4018 	kauth_action_t action;
4019 	int eofflag;
4020 	uint64_t options;
4021 	int error;
4022 
4023 	*retval = 0;
4024 
4025 	error = fp_getfvp(p, uap->dirfd, &fp, &dvp);
4026 	if (error) {
4027 		return error;
4028 	}
4029 
4030 	count = 0;
4031 	fvdata = NULL;
4032 	eofflag = 0;
4033 	ctx = vfs_context_current();
4034 	ut = current_uthread();
4035 	segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
4036 
4037 	if ((fp->fp_glob->fg_flag & FREAD) == 0) {
4038 		/*
4039 		 *  AUDIT_ARG(vnpath_withref, dvp, ARG_VNODE1);
4040 		 */
4041 		error = EBADF;
4042 		dvp = NULLVP;
4043 		goto out;
4044 	}
4045 
4046 	if ((error = vnode_getwithref(dvp))) {
4047 		dvp = NULLVP;
4048 		goto out;
4049 	}
4050 
4051 	if (uap->options & FSOPT_LIST_SNAPSHOT) {
4052 		vnode_t snapdvp;
4053 
4054 		if (!vnode_isvroot(dvp)) {
4055 			error = EINVAL;
4056 			goto out;
4057 		}
4058 
4059 		/* switch directory to snapshot directory */
4060 		error = vnode_get_snapdir(dvp, &snapdvp, ctx);
4061 		if (error) {
4062 			goto out;
4063 		}
4064 		vnode_put(dvp);
4065 		dvp = snapdvp;
4066 	}
4067 
4068 	if (dvp->v_type != VDIR) {
4069 		error = ENOTDIR;
4070 		goto out;
4071 	}
4072 
4073 #if CONFIG_MACF
4074 	error = mac_file_check_change_offset(vfs_context_ucred(ctx),
4075 	    fp->fp_glob);
4076 	if (error) {
4077 		goto out;
4078 	}
4079 #endif
4080 	/*
4081 	 * XXX : Audit Support
4082 	 * AUDIT_ARG(vnpath, dvp, ARG_VNODE1);
4083 	 */
4084 
4085 	options = uap->options | FSOPT_ATTR_CMN_EXTENDED;
4086 
4087 	if ((error = copyin(CAST_USER_ADDR_T(uap->alist), &al,
4088 	    sizeof(struct attrlist)))) {
4089 		goto out;
4090 	}
4091 
4092 	if (al.volattr ||
4093 	    ((al.commonattr & ATTR_BULK_REQUIRED) != ATTR_BULK_REQUIRED)) {
4094 		error = EINVAL;
4095 		goto out;
4096 	}
4097 
4098 #if CONFIG_MACF
4099 	error = mac_vnode_check_readdir(ctx, dvp);
4100 	if (error != 0) {
4101 		goto out;
4102 	}
4103 #endif /* MAC */
4104 
4105 	/*
4106 	 * Requested attributes that are available in the direntry struct, with the addition
4107 	 * of ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_ERROR, can be let past with just LIST_DIRECTORY.
4108 	 * Any other requested attributes require SEARCH as well.
4109 	 */
4110 	action = KAUTH_VNODE_LIST_DIRECTORY;
4111 	if ((al.commonattr & ~LIST_DIR_ATTRS) || al.fileattr || al.dirattr) {
4112 		action |= KAUTH_VNODE_SEARCH;
4113 	}
4114 
4115 	error = vnode_authorize(dvp, NULL, action, ctx);
4116 	if (error) {
4117 		goto out;
4118 	}
4119 
4120 	fvdata = (struct fd_vn_data *)fp->fp_glob->fg_vn_data;
4121 	if (!fvdata) {
4122 		panic("Directory expected to have fg_vn_data");
4123 	}
4124 
4125 	FV_LOCK(fvdata);
4126 
4127 	/*
4128 	 * getattrlistbulk(2) maintains its offset in fv_offset. However
4129 	 * if the offset in the file glob is set (or reset) to 0, the directory
4130 	 * traversal needs to be restarted (Any existing state in the
4131 	 * directory buffer is removed as well).
4132 	 */
4133 	if (!fp->fp_glob->fg_offset) {
4134 		fvdata->fv_offset = 0;
4135 		kfree_data(fvdata->fv_buf, fvdata->fv_bufallocsiz);
4136 		fvdata->fv_bufsiz = 0;
4137 		fvdata->fv_bufdone = 0;
4138 		fvdata->fv_soff = 0;
4139 		fvdata->fv_eoff = 0;
4140 		fvdata->fv_eofflag = 0;
4141 	}
4142 
4143 	auio = uio_createwithbuffer(1, fvdata->fv_offset, segflg, UIO_READ,
4144 	    &uio_buf[0], sizeof(uio_buf));
4145 	uio_addiov(auio, uap->attributeBuffer, (user_size_t)uap->bufferSize);
4146 
4147 	/*
4148 	 * For "expensive" operations in which the native VNOP implementations
4149 	 * end up having to do just as much (if not more) work than the default
4150 	 * implementation, fall back to the default implementation.
4151 	 * The VNOP helper functions depend on the filesystem providing the
4152 	 * object type, if the caller has not requested ATTR_CMN_OBJTYPE, fall
4153 	 * back to the default implementation.
4154 	 */
4155 	if ((al.commonattr &
4156 	    (ATTR_CMN_UUID | ATTR_CMN_GRPUUID | ATTR_CMN_EXTENDED_SECURITY)) ||
4157 	    !(al.commonattr & ATTR_CMN_OBJTYPE)) {
4158 		error = ENOTSUP;
4159 	} else {
4160 		struct vnode_attr *va;
4161 		char *va_name;
4162 
4163 		if (fvdata->fv_eofflag && !fvdata->fv_buf) {
4164 			/*
4165 			 * If the last successful VNOP_GETATTRLISTBULK or
4166 			 * VNOP_READDIR returned EOF, don't try again.
4167 			 */
4168 			eofflag = 1;
4169 			count = 0;
4170 			error = 0;
4171 		} else {
4172 			eofflag = 0;
4173 			count = 0;
4174 
4175 			va = kalloc_type(struct vnode_attr, Z_WAITOK);
4176 
4177 			VATTR_INIT(va);
4178 			va_name = zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
4179 			va->va_name = va_name;
4180 
4181 			(void)getattrlist_setupvattr_all(&al, va, VNON, NULL,
4182 			    IS_64BIT_PROCESS(p), (uap->options & FSOPT_ATTR_CMN_EXTENDED));
4183 
4184 			/*
4185 			 * Set UT_KERN_RAGE_VNODES to cause all vnodes created by the
4186 			 * filesystem to be rapidly aged.
4187 			 */
4188 			ut->uu_flag |= UT_KERN_RAGE_VNODES;
4189 			error = VNOP_GETATTRLISTBULK(dvp, &al, va, auio, NULL,
4190 			    options, &eofflag, &count, ctx);
4191 			ut->uu_flag &= ~UT_KERN_RAGE_VNODES;
4192 
4193 			zfree(ZV_NAMEI, va_name);
4194 			kfree_type(struct vnode_attr, va);
4195 
4196 			/*
4197 			 * cache state of eofflag.
4198 			 */
4199 			if (!error) {
4200 				fvdata->fv_eofflag = eofflag;
4201 			}
4202 		}
4203 	}
4204 
4205 	/*
4206 	 * If the Filessytem does not natively support getattrlistbulk,
4207 	 * do the default implementation.
4208 	 */
4209 	if (error == ENOTSUP) {
4210 		eofflag = 0;
4211 		count = 0;
4212 
4213 		ut->uu_flag |= UT_KERN_RAGE_VNODES;
4214 		error = readdirattr(dvp, fvdata, auio, &al, options,
4215 		    &count, &eofflag, ctx);
4216 		ut->uu_flag &= ~UT_KERN_RAGE_VNODES;
4217 	}
4218 
4219 	if (count) {
4220 		fvdata->fv_offset = uio_offset(auio);
4221 		fp->fp_glob->fg_offset = fvdata->fv_offset;
4222 		*retval = count;
4223 		error = 0;
4224 	} else if (!error && !eofflag) {
4225 		/*
4226 		 * This just means the buffer was too small to fit even a
4227 		 * single entry.
4228 		 */
4229 		error = ERANGE;
4230 	}
4231 
4232 	FV_UNLOCK(fvdata);
4233 out:
4234 	if (dvp) {
4235 		vnode_put(dvp);
4236 	}
4237 
4238 	file_drop(uap->dirfd);
4239 
4240 	return error;
4241 }
4242 
4243 static int
attrlist_unpack_fixed(char ** cursor,char * end,void * buf,ssize_t size)4244 attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
4245 {
4246 	/* make sure we have enough source data */
4247 	if ((*cursor) + size > end) {
4248 		return EINVAL;
4249 	}
4250 
4251 	bcopy(*cursor, buf, size);
4252 	*cursor += size;
4253 	return 0;
4254 }
4255 
4256 #define ATTR_UNPACK(v)          do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
4257 #define ATTR_UNPACK_CAST(t, v)  do { t _f; ATTR_UNPACK(_f); v = (typeof(v))_f;} while(0)
4258 #define ATTR_UNPACK_TIME(v, is64)                               \
4259 	do {                                                    \
4260 	        if (is64) {                                     \
4261 	                struct user64_timespec us;              \
4262 	                ATTR_UNPACK(us);                        \
4263 	                v.tv_sec = (unsigned long)us.tv_sec;                   \
4264 	                v.tv_nsec = (long)us.tv_nsec;                 \
4265 	        } else {                                        \
4266 	                struct user32_timespec us;              \
4267 	                ATTR_UNPACK(us);                        \
4268 	                v.tv_sec = us.tv_sec;                   \
4269 	                v.tv_nsec = us.tv_nsec;                 \
4270 	        }                                               \
4271 	} while(0)
4272 
4273 
4274 /*
4275  * Write attributes.
4276  */
4277 static int
setattrlist_internal(vnode_t vp,struct setattrlist_args * uap,proc_t p,vfs_context_t ctx)4278 setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_context_t ctx)
4279 {
4280 	struct attrlist al;
4281 	struct vnode_attr va;
4282 	struct attrreference ar;
4283 	kauth_action_t  action;
4284 	char            *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
4285 	int             proc_is64, error;
4286 	uint32_t        nace;
4287 	kauth_filesec_t rfsec;
4288 
4289 	user_buf = NULL;
4290 	fndrinfo = NULL;
4291 	volname = NULL;
4292 	error = 0;
4293 	proc_is64 = proc_is64bit(p);
4294 	VATTR_INIT(&va);
4295 
4296 	if (uap->options & FSOPT_UTIMES_NULL) {
4297 		va.va_vaflags |= VA_UTIMES_NULL;
4298 	}
4299 
4300 	/*
4301 	 * Fetch the attribute set and validate.
4302 	 */
4303 	if ((error = copyin(uap->alist, (caddr_t) &al, sizeof(al)))) {
4304 		goto out;
4305 	}
4306 	if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
4307 		error = EINVAL;
4308 		goto out;
4309 	}
4310 
4311 #if DEVELOPMENT || DEBUG
4312 	/*
4313 	 * XXX VSWAP: Check for entitlements or special flag here
4314 	 * so we can restrict access appropriately.
4315 	 */
4316 #else /* DEVELOPMENT || DEBUG */
4317 
4318 	if (vnode_isswap(vp) && (ctx != vfs_context_kernel())) {
4319 		error = EPERM;
4320 		goto out;
4321 	}
4322 #endif /* DEVELOPMENT || DEBUG */
4323 
4324 	VFS_DEBUG(ctx, vp, "%p  ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
4325 	    vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
4326 	    (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
4327 
4328 	if (al.volattr) {
4329 		if ((al.volattr & ~ATTR_VOL_SETMASK) ||
4330 		    (al.commonattr & ~ATTR_CMN_VOLSETMASK) ||
4331 		    al.fileattr ||
4332 		    al.forkattr) {
4333 			error = EINVAL;
4334 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
4335 			goto out;
4336 		}
4337 	} else {
4338 		if ((al.commonattr & ~ATTR_CMN_SETMASK) ||
4339 		    (al.fileattr & ~ATTR_FILE_SETMASK) ||
4340 		    (al.dirattr & ~ATTR_DIR_SETMASK) ||
4341 		    (al.forkattr & ~ATTR_FORK_SETMASK)) {
4342 			error = EINVAL;
4343 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
4344 			goto out;
4345 		}
4346 	}
4347 
4348 	/*
4349 	 * If the caller's bitmaps indicate that there are no attributes to set,
4350 	 * then exit early.
4351 	 */
4352 	if (al.commonattr == 0 &&
4353 	    (al.volattr & ~ATTR_VOL_INFO) == 0 &&
4354 	    al.dirattr == 0 &&
4355 	    al.fileattr == 0 &&
4356 	    al.forkattr == 0) {
4357 		error = 0;
4358 		goto out;
4359 	}
4360 
4361 	/*
4362 	 * Make the naive assumption that the caller has supplied a reasonable buffer
4363 	 * size.  We could be more careful by pulling in the fixed-size region, checking
4364 	 * the attrref structures, then pulling in the variable section.
4365 	 * We need to reconsider this for handling large ACLs, as they should probably be
4366 	 * brought directly into a buffer.  Multiple copyins will make this slower though.
4367 	 *
4368 	 * We could also map the user buffer if it is larger than some sensible mimimum.
4369 	 */
4370 	if (uap->bufferSize > ATTR_MAX_BUFFER) {
4371 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large", uap->bufferSize);
4372 		error = ENOMEM;
4373 		goto out;
4374 	}
4375 	user_buf = kalloc_data(uap->bufferSize, Z_WAITOK);
4376 	if (user_buf == NULL) {
4377 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
4378 		error = ENOMEM;
4379 		goto out;
4380 	}
4381 	if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) {
4382 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed");
4383 		goto out;
4384 	}
4385 	VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
4386 
4387 #if CONFIG_MACF
4388 	error = mac_vnode_check_setattrlist(ctx, vp, &al);
4389 	if (error) {
4390 		goto out;
4391 	}
4392 #endif /* MAC */
4393 
4394 	/*
4395 	 * Unpack the argument buffer.
4396 	 */
4397 	cursor = user_buf;
4398 	bufend = cursor + uap->bufferSize;
4399 
4400 	/* common */
4401 	if (al.commonattr & ATTR_CMN_SCRIPT) {
4402 		ATTR_UNPACK(va.va_encoding);
4403 		VATTR_SET_ACTIVE(&va, va_encoding);
4404 	}
4405 	if (al.commonattr & ATTR_CMN_CRTIME) {
4406 		ATTR_UNPACK_TIME(va.va_create_time, proc_is64);
4407 		VATTR_SET_ACTIVE(&va, va_create_time);
4408 	}
4409 	if (al.commonattr & ATTR_CMN_MODTIME) {
4410 		ATTR_UNPACK_TIME(va.va_modify_time, proc_is64);
4411 		VATTR_SET_ACTIVE(&va, va_modify_time);
4412 	}
4413 	if (al.commonattr & ATTR_CMN_CHGTIME) {
4414 		ATTR_UNPACK_TIME(va.va_change_time, proc_is64);
4415 		al.commonattr &= ~ATTR_CMN_CHGTIME;
4416 		/*quietly ignore change time; advisory in man page*/
4417 	}
4418 	if (al.commonattr & ATTR_CMN_ACCTIME) {
4419 		ATTR_UNPACK_TIME(va.va_access_time, proc_is64);
4420 		VATTR_SET_ACTIVE(&va, va_access_time);
4421 	}
4422 	if (al.commonattr & ATTR_CMN_BKUPTIME) {
4423 		ATTR_UNPACK_TIME(va.va_backup_time, proc_is64);
4424 		VATTR_SET_ACTIVE(&va, va_backup_time);
4425 	}
4426 	if (al.commonattr & ATTR_CMN_FNDRINFO) {
4427 		if ((cursor + 32) > bufend) {
4428 			error = EINVAL;
4429 			VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO");
4430 			goto out;
4431 		}
4432 		fndrinfo = cursor;
4433 		cursor += 32;
4434 	}
4435 	if (al.commonattr & ATTR_CMN_OWNERID) {
4436 		ATTR_UNPACK(va.va_uid);
4437 		VATTR_SET_ACTIVE(&va, va_uid);
4438 	}
4439 	if (al.commonattr & ATTR_CMN_GRPID) {
4440 		ATTR_UNPACK(va.va_gid);
4441 		VATTR_SET_ACTIVE(&va, va_gid);
4442 	}
4443 	if (al.commonattr & ATTR_CMN_ACCESSMASK) {
4444 		ATTR_UNPACK_CAST(uint32_t, va.va_mode);
4445 		VATTR_SET_ACTIVE(&va, va_mode);
4446 	}
4447 	if (al.commonattr & ATTR_CMN_FLAGS) {
4448 		ATTR_UNPACK(va.va_flags);
4449 		VATTR_SET_ACTIVE(&va, va_flags);
4450 #if CONFIG_MACF
4451 		if ((error = mac_vnode_check_setflags(ctx, vp, va.va_flags)) != 0) {
4452 			goto out;
4453 		}
4454 #endif
4455 	}
4456 	if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
4457 		/*
4458 		 * We are (for now) passed a kauth_filesec_t, but all we want from
4459 		 * it is the ACL.
4460 		 */
4461 		cp = cursor;
4462 		ATTR_UNPACK(ar);
4463 		if (ar.attr_dataoffset < 0) {
4464 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied", ar.attr_dataoffset);
4465 			error = EINVAL;
4466 			goto out;
4467 		}
4468 
4469 		cp += ar.attr_dataoffset;
4470 		rfsec = (kauth_filesec_t)cp;
4471 		if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) ||                     /* no space for acl */
4472 		    (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) ||       /* bad magic */
4473 		    (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
4474 		    ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) {  /* ACEs overrun buffer */
4475 			error = EINVAL;
4476 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
4477 			goto out;
4478 		}
4479 		nace = rfsec->fsec_entrycount;
4480 		if (nace == KAUTH_FILESEC_NOACL) {
4481 			nace = 0;
4482 		}
4483 		if (nace > KAUTH_ACL_MAX_ENTRIES) {                     /* ACL size invalid */
4484 			error = EINVAL;
4485 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
4486 			goto out;
4487 		}
4488 		nace = rfsec->fsec_acl.acl_entrycount;
4489 		if (nace == KAUTH_FILESEC_NOACL) {
4490 			/* deleting ACL */
4491 			VATTR_SET(&va, va_acl, NULL);
4492 		} else {
4493 			if (nace > KAUTH_ACL_MAX_ENTRIES) {                     /* ACL size invalid */
4494 				error = EINVAL;
4495 				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
4496 				goto out;
4497 			}
4498 			VATTR_SET(&va, va_acl, &rfsec->fsec_acl);
4499 		}
4500 	}
4501 	if (al.commonattr & ATTR_CMN_UUID) {
4502 		ATTR_UNPACK(va.va_uuuid);
4503 		VATTR_SET_ACTIVE(&va, va_uuuid);
4504 	}
4505 	if (al.commonattr & ATTR_CMN_GRPUUID) {
4506 		ATTR_UNPACK(va.va_guuid);
4507 		VATTR_SET_ACTIVE(&va, va_guuid);
4508 	}
4509 	if (al.commonattr & ATTR_CMN_ADDEDTIME) {
4510 		ATTR_UNPACK_TIME(va.va_addedtime, proc_is64);
4511 		VATTR_SET_ACTIVE(&va, va_addedtime);
4512 	}
4513 	/* Support setattrlist of data protection class */
4514 	if (al.commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) {
4515 		ATTR_UNPACK(va.va_dataprotect_class);
4516 		VATTR_SET_ACTIVE(&va, va_dataprotect_class);
4517 	}
4518 
4519 	/* volume */
4520 	if (al.volattr & ATTR_VOL_INFO) {
4521 		if (al.volattr & ATTR_VOL_NAME) {
4522 			volname = cursor;
4523 			ATTR_UNPACK(ar);
4524 			/* attr_length cannot be 0! */
4525 			if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0) ||
4526 			    (ar.attr_length > uap->bufferSize) ||
4527 			    (uap->bufferSize - ar.attr_length < (unsigned)ar.attr_dataoffset)) {
4528 				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) ", ar.attr_dataoffset);
4529 				error = EINVAL;
4530 				goto out;
4531 			}
4532 
4533 			if (volname >= bufend - ar.attr_dataoffset - ar.attr_length) {
4534 				error = EINVAL;
4535 				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
4536 				goto out;
4537 			}
4538 			volname += ar.attr_dataoffset;
4539 			/* guarantee NUL termination */
4540 			volname[ar.attr_length - 1] = 0;
4541 		}
4542 	}
4543 
4544 	/* file */
4545 	if (al.fileattr & ATTR_FILE_DEVTYPE) {
4546 		/* XXX does it actually make any sense to change this? */
4547 		error = EINVAL;
4548 		VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented");
4549 		goto out;
4550 	}
4551 
4552 	/*
4553 	 * Validate and authorize.
4554 	 */
4555 	action = 0;
4556 	if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, ctx)) != 0)) {
4557 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
4558 		goto out;
4559 	}
4560 	/*
4561 	 * We can auth file Finder Info here.  HFS volume FinderInfo is really boot data,
4562 	 * and will be auth'ed by the FS.
4563 	 */
4564 	if (fndrinfo != NULL) {
4565 		if (al.volattr & ATTR_VOL_INFO) {
4566 			if (vp->v_tag != VT_HFS) {
4567 				error = EINVAL;
4568 				goto out;
4569 			}
4570 		} else {
4571 			action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
4572 		}
4573 	}
4574 
4575 	if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) {
4576 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
4577 		goto out;
4578 	}
4579 
4580 	/*
4581 	 * When we're setting both the access mask and the finder info, then
4582 	 * check if were about to remove write access for the owner.  Since
4583 	 * vnode_setattr and vn_setxattr invoke two separate vnops, we need
4584 	 * to consider their ordering.
4585 	 *
4586 	 * If were about to remove write access for the owner we'll set the
4587 	 * Finder Info here before vnode_setattr.  Otherwise we'll set it
4588 	 * after vnode_setattr since it may be adding owner write access.
4589 	 */
4590 	if ((fndrinfo != NULL) && !(al.volattr & ATTR_VOL_INFO) &&
4591 	    (al.commonattr & ATTR_CMN_ACCESSMASK) && !(va.va_mode & S_IWUSR)) {
4592 		if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
4593 			goto out;
4594 		}
4595 		fndrinfo = NULL;  /* it was set here so skip setting below */
4596 	}
4597 
4598 	/*
4599 	 * Write the attributes if we have any.
4600 	 */
4601 	if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, ctx)) != 0)) {
4602 		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
4603 		goto out;
4604 	}
4605 
4606 #if CONFIG_MACF
4607 	mac_vnode_notify_setattrlist(ctx, vp, &al);
4608 	if (VATTR_IS_ACTIVE(&va, va_flags)) {
4609 		mac_vnode_notify_setflags(ctx, vp, va.va_flags);
4610 	}
4611 #endif
4612 
4613 	/*
4614 	 * Write the Finder Info if we have any.
4615 	 */
4616 	if (fndrinfo != NULL) {
4617 		if (al.volattr & ATTR_VOL_INFO) {
4618 			if (vp->v_tag == VT_HFS) {
4619 #define HFS_SET_BOOT_INFO   (FCNTL_FS_SPECIFIC_BASE + 0x00005)
4620 				error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx);
4621 				if (error != 0) {
4622 					goto out;
4623 				}
4624 			} else {
4625 				/* XXX should never get here */
4626 			}
4627 		} else if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
4628 			goto out;
4629 		}
4630 	}
4631 
4632 	/*
4633 	 * Set the volume name, if we have one
4634 	 */
4635 	if (volname != NULL) {
4636 		struct vfs_attr vs = {};
4637 
4638 		VFSATTR_INIT(&vs);
4639 
4640 		vs.f_vol_name = volname;        /* References the setattrlist buffer directly */
4641 		VFSATTR_WANTED(&vs, f_vol_name);
4642 
4643 #if CONFIG_MACF
4644 		error = mac_mount_check_setattr(ctx, vp->v_mount, &vs);
4645 		if (error != 0) {
4646 			goto out;
4647 		}
4648 #endif
4649 
4650 		if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
4651 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
4652 			goto out;
4653 		}
4654 
4655 		if (!VFSATTR_ALL_SUPPORTED(&vs)) {
4656 			error = EINVAL;
4657 			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
4658 			goto out;
4659 		}
4660 	}
4661 
4662 	/* all done and successful */
4663 
4664 out:
4665 	kfree_data(user_buf, uap->bufferSize);
4666 	VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
4667 	return error;
4668 }
4669 
4670 int
setattrlist(proc_t p,struct setattrlist_args * uap,__unused int32_t * retval)4671 setattrlist(proc_t p, struct setattrlist_args *uap, __unused int32_t *retval)
4672 {
4673 	struct vfs_context *ctx;
4674 	struct nameidata nd;
4675 	vnode_t         vp = NULL;
4676 	uint32_t          nameiflags;
4677 	int error = 0;
4678 
4679 	ctx = vfs_context_current();
4680 
4681 	/*
4682 	 * Look up the file.
4683 	 */
4684 	nameiflags = AUDITVNPATH1;
4685 	if ((uap->options & FSOPT_NOFOLLOW) == 0) {
4686 		nameiflags |= FOLLOW;
4687 	}
4688 	NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
4689 	if ((error = namei(&nd)) != 0) {
4690 		goto out;
4691 	}
4692 	vp = nd.ni_vp;
4693 	nameidone(&nd);
4694 
4695 	error = setattrlist_internal(vp, uap, p, ctx);
4696 out:
4697 	if (vp != NULL) {
4698 		vnode_put(vp);
4699 	}
4700 	return error;
4701 }
4702 
4703 int
setattrlistat(proc_t p,struct setattrlistat_args * uap,__unused int32_t * retval)4704 setattrlistat(proc_t p, struct setattrlistat_args *uap, __unused int32_t *retval)
4705 {
4706 	struct setattrlist_args ap;
4707 	struct vfs_context *ctx;
4708 	struct nameidata nd;
4709 	vnode_t vp = NULLVP;
4710 	uint32_t nameiflags;
4711 	int error;
4712 
4713 	ctx = vfs_context_current();
4714 
4715 	AUDIT_ARG(fd, uap->fd);
4716 	/*
4717 	 * Look up the file.
4718 	 */
4719 	nameiflags = AUDITVNPATH1;
4720 	if (!(uap->options & FSOPT_NOFOLLOW)) {
4721 		nameiflags |= FOLLOW;
4722 	}
4723 	NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
4724 	if ((error = nameiat(&nd, uap->fd)) != 0) {
4725 		goto out;
4726 	}
4727 	vp = nd.ni_vp;
4728 	nameidone(&nd);
4729 
4730 	ap.path = 0;
4731 	ap.alist = uap->alist;
4732 	ap.attributeBuffer = uap->attributeBuffer;
4733 	ap.bufferSize = uap->bufferSize;
4734 	ap.options = uap->options;
4735 
4736 	error = setattrlist_internal(vp, &ap, p, ctx);
4737 out:
4738 	if (vp) {
4739 		vnode_put(vp);
4740 	}
4741 	return error;
4742 }
4743 
4744 int
fsetattrlist(proc_t p,struct fsetattrlist_args * uap,__unused int32_t * retval)4745 fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
4746 {
4747 	struct vfs_context *ctx;
4748 	vnode_t         vp = NULL;
4749 	int             error;
4750 	struct setattrlist_args ap;
4751 
4752 	ctx = vfs_context_current();
4753 
4754 	if ((error = file_vnode(uap->fd, &vp)) != 0) {
4755 		return error;
4756 	}
4757 
4758 	if ((error = vnode_getwithref(vp)) != 0) {
4759 		file_drop(uap->fd);
4760 		return error;
4761 	}
4762 
4763 	ap.path = 0;
4764 	ap.alist = uap->alist;
4765 	ap.attributeBuffer = uap->attributeBuffer;
4766 	ap.bufferSize = uap->bufferSize;
4767 	ap.options = uap->options;
4768 
4769 	error = setattrlist_internal(vp, &ap, p, ctx);
4770 	file_drop(uap->fd);
4771 	if (vp != NULL) {
4772 		vnode_put(vp);
4773 	}
4774 
4775 	return error;
4776 }
4777