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