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