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