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