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