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/vnode_internal.h>
41#include <sys/mount_internal.h>
42#include <sys/proc_internal.h>
43#include <sys/kauth.h>
44#include <sys/uio_internal.h>
45#include <sys/malloc.h>
46#include <sys/attr.h>
47#include <sys/sysproto.h>
48#include <sys/xattr.h>
49#include <sys/fsevents.h>
50#include <kern/kalloc.h>
51#include <miscfs/specfs/specdev.h>
52#include <hfs/hfs.h>
53
54#if CONFIG_MACF
55#include <security/mac_framework.h>
56#endif
57
58#define ATTR_TIME_SIZE	-1
59
60/*
61 * SPI.
62 */
63#define FSOPT_ATTRLIST_EXTENDED	0x00000020
64
65/* Valid only if FSOPT_ATTRLIST_EXTENDED is set */
66#define ATTR_CMN_GEN_COUNT	0x00080000 /* same as ATTR_CMN_NAMEDATTRCOUNT */
67#define ATTR_CMN_DOCUMENT_ID	0x00100000 /* same as ATTR_CMN_NAMEDATTRLIST */
68
69#define ATTR_CMN_ERROR		0x20000000
70
71/*
72 * Structure describing the state of an in-progress attrlist operation.
73 */
74struct _attrlist_buf {
75	char	*base;
76	char	*fixedcursor;
77	char	*varcursor;
78	ssize_t	allocated;
79	ssize_t needed;
80	attribute_set_t	actual;
81	attribute_set_t valid;
82};
83
84
85/*
86 * Attempt to pack a fixed width attribute of size (count) bytes from
87 * source to our attrlist buffer.
88 */
89static void
90attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
91{
92	/*
93	 * Use ssize_t for pointer math purposes,
94	 * since a ssize_t is a signed long
95	 */
96	ssize_t	fit;
97
98	/*
99	 * Compute the amount of remaining space in the attrlist buffer
100	 * based on how much we've used for fixed width fields vs. the
101	 * start of the attributes.
102	 *
103	 * If we've still got room, then 'fit' will contain the amount of
104	 * remaining space.
105	 *
106	 * Note that this math is safe because, in the event that the
107	 * fixed-width cursor has moved beyond the end of the buffer,
108	 * then, the second input into lmin() below will be negative, and
109	 * we will fail the (fit > 0) check below.
110	 */
111	fit = lmin(count, ab->allocated - (ab->fixedcursor - ab->base));
112	if (fit > 0) {
113		/* Copy in as much as we can */
114		bcopy(source, ab->fixedcursor, fit);
115	}
116
117	/* always move in increments of 4, even if we didn't pack an attribute. */
118	ab->fixedcursor += roundup(count, 4);
119}
120
121/*
122 * Attempt to pack one (or two) variable width attributes into the attrlist
123 * buffer.  If we are trying to pack two variable width attributes, they are treated
124 * as a single variable-width attribute from the POV of the system call caller.
125 *
126 * Recall that a variable-width attribute has two components: the fixed-width
127 * attribute that tells the caller where to look, and the actual variable width data.
128 */
129static void
130attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count,
131			const void *ext, ssize_t extcount)
132{
133
134	/* Use ssize_t's for pointer math ease */
135	struct attrreference ar;
136	ssize_t fit;
137
138	/*
139	 * Pack the fixed-width component to the variable object.
140	 * Note that we may be able to pack the fixed width attref, but not
141	 * the variable (if there's no room).
142	 */
143	ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
144	ar.attr_length = count + extcount;
145	attrlist_pack_fixed(ab, &ar, sizeof(ar));
146
147	/*
148	 * Use an lmin() to do a signed comparison. We use a signed comparison
149	 * to detect the 'out of memory' conditions as described above in the
150	 * fixed width check above.
151	 *
152	 * Then pack the first variable attribute as space allows.  Note that we advance
153	 * the variable cursor only if we we had some available space.
154	 */
155	fit = lmin(count, ab->allocated - (ab->varcursor - ab->base));
156	if (fit > 0) {
157		if (source != NULL) {
158			bcopy(source, ab->varcursor, fit);
159		}
160		ab->varcursor += fit;
161	}
162
163	/* Compute the available space for the second attribute */
164	fit = lmin(extcount, ab->allocated - (ab->varcursor - ab->base));
165	if (fit > 0) {
166		/* Copy in data for the second attribute (if needed) if there is room */
167		if (ext != NULL) {
168			bcopy(ext, ab->varcursor, fit);
169		}
170		ab->varcursor += fit;
171	}
172	/* always move in increments of 4 */
173	ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4);
174}
175
176/*
177 * Packing a single variable-width attribute is the same as calling the two, but with
178 * an invalid 2nd attribute.
179 */
180static void
181attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
182{
183	attrlist_pack_variable2(ab, source, count, NULL, 0);
184}
185
186/*
187 * Attempt to pack a string. This is a special case of a variable width attribute.
188 *
189 * If "source" is NULL, then an empty string ("") will be packed.  If "source" is
190 * not NULL, but "count" is zero, then "source" is assumed to be a NUL-terminated
191 * C-string.  If "source" is not NULL and "count" is not zero, then only the first
192 * "count" bytes of "source" will be copied, and a NUL terminator will be added.
193 *
194 * If the attrlist buffer doesn't have enough room to hold the entire string (including
195 * NUL terminator), then copy as much as will fit.  The attrlist buffer's "varcursor"
196 * will always be updated based on the entire length of the string (including NUL
197 * terminator); this means "varcursor" may end up pointing beyond the end of the
198 * allocated buffer space.
199 */
200static void
201attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count)
202{
203	struct attrreference ar;
204	ssize_t fit, space;
205
206	/*
207	 * Supplied count is character count of string text, excluding trailing nul
208	 * which we always supply here.
209	 */
210	if (source == NULL) {
211		count = 0;
212	} else if (count == 0) {
213		count = strlen(source);
214	}
215
216	/*
217	 * Construct the fixed-width attribute that refers to this string.
218	 */
219	ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
220	ar.attr_length = count + 1;
221	attrlist_pack_fixed(ab, &ar, sizeof(ar));
222
223	/*
224	 * Now compute how much available memory we have to copy the string text.
225	 *
226	 * space = the number of bytes available in the attribute buffer to hold the
227	 *         string's value.
228	 *
229	 * fit = the number of bytes to copy from the start of the string into the
230	 *       attribute buffer, NOT including the NUL terminator.  If the attribute
231	 *       buffer is large enough, this will be the string's length; otherwise, it
232	 *       will be equal to "space".
233	 */
234	space = ab->allocated - (ab->varcursor - ab->base);
235	fit = lmin(count, space);
236	if (space > 0) {
237		/*
238		 * If there is space remaining, copy data in, and
239		 * accommodate the trailing NUL terminator.
240		 *
241		 * NOTE: if "space" is too small to hold the string and its NUL
242		 * terminator (space < fit + 1), then the string value in the attribute
243		 * buffer will NOT be NUL terminated!
244		 *
245		 * NOTE 2: bcopy() will do nothing if the length ("fit") is zero.
246		 * Therefore, we don't bother checking for that here.
247		 */
248		bcopy(source, ab->varcursor, fit);
249		/* is there room for our trailing nul? */
250		if (space > fit) {
251			ab->varcursor[fit++] = '\0';
252			/* 'fit' now the number of bytes AFTER adding in the NUL */
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_USERACCESS,	0,				sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
494	{ATTR_CMN_EXTENDED_SECURITY, VATTR_BIT(va_acl),		sizeof(struct attrreference),	KAUTH_VNODE_READ_SECURITY},
495	{ATTR_CMN_UUID,		VATTR_BIT(va_uuuid),		sizeof(guid_t),			KAUTH_VNODE_READ_ATTRIBUTES},
496	{ATTR_CMN_GRPUUID,	VATTR_BIT(va_guuid),		sizeof(guid_t),			KAUTH_VNODE_READ_ATTRIBUTES},
497	{ATTR_CMN_FILEID,	VATTR_BIT(va_fileid), 		sizeof(uint64_t),		KAUTH_VNODE_READ_ATTRIBUTES},
498	{ATTR_CMN_PARENTID,	VATTR_BIT(va_parentid),		sizeof(uint64_t),		KAUTH_VNODE_READ_ATTRIBUTES},
499	{ATTR_CMN_FULLPATH, 	0, 				sizeof(struct attrreference),	KAUTH_VNODE_READ_ATTRIBUTES},
500	{ATTR_CMN_ADDEDTIME, 	VATTR_BIT(va_addedtime), 	ATTR_TIME_SIZE,			KAUTH_VNODE_READ_ATTRIBUTES},
501	{ATTR_CMN_RETURNED_ATTRS, 0,				sizeof(attribute_set_t),	0},
502	{ATTR_CMN_ERROR, 	0,				sizeof(uint32_t),		0},
503	{0, 0, 0, 0}
504};
505
506static struct getattrlist_attrtab getattrlist_common_tab_extended[] = {
507	{ATTR_CMN_NAME,		VATTR_BIT(va_name),		sizeof(struct attrreference),	KAUTH_VNODE_READ_ATTRIBUTES},
508	{ATTR_CMN_DEVID,	0,				sizeof(dev_t),			KAUTH_VNODE_READ_ATTRIBUTES},
509	{ATTR_CMN_FSID,		VATTR_BIT(va_fsid),		sizeof(fsid_t),			KAUTH_VNODE_READ_ATTRIBUTES},
510	{ATTR_CMN_OBJTYPE,	0,				sizeof(fsobj_type_t),		KAUTH_VNODE_READ_ATTRIBUTES},
511	{ATTR_CMN_OBJTAG,	0,				sizeof(fsobj_tag_t),		KAUTH_VNODE_READ_ATTRIBUTES},
512	{ATTR_CMN_OBJID,	VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
513	{ATTR_CMN_OBJPERMANENTID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
514	{ATTR_CMN_PAROBJID,	VATTR_BIT(va_parentid),		sizeof(fsobj_id_t),		KAUTH_VNODE_READ_ATTRIBUTES},
515	{ATTR_CMN_SCRIPT,	VATTR_BIT(va_encoding),		sizeof(text_encoding_t),	KAUTH_VNODE_READ_ATTRIBUTES},
516	{ATTR_CMN_CRTIME,	VATTR_BIT(va_create_time),	ATTR_TIME_SIZE,			KAUTH_VNODE_READ_ATTRIBUTES},
517	{ATTR_CMN_MODTIME,	VATTR_BIT(va_modify_time),	ATTR_TIME_SIZE,			KAUTH_VNODE_READ_ATTRIBUTES},
518	{ATTR_CMN_CHGTIME,	VATTR_BIT(va_change_time),	ATTR_TIME_SIZE,			KAUTH_VNODE_READ_ATTRIBUTES},
519	{ATTR_CMN_ACCTIME,	VATTR_BIT(va_access_time),	ATTR_TIME_SIZE,			KAUTH_VNODE_READ_ATTRIBUTES},
520	{ATTR_CMN_BKUPTIME,	VATTR_BIT(va_backup_time),	ATTR_TIME_SIZE,			KAUTH_VNODE_READ_ATTRIBUTES},
521	{ATTR_CMN_FNDRINFO,	0,				32,				KAUTH_VNODE_READ_ATTRIBUTES},
522	{ATTR_CMN_OWNERID,	VATTR_BIT(va_uid),		sizeof(uid_t),			KAUTH_VNODE_READ_ATTRIBUTES},
523	{ATTR_CMN_GRPID,	VATTR_BIT(va_gid),		sizeof(gid_t),			KAUTH_VNODE_READ_ATTRIBUTES},
524	{ATTR_CMN_ACCESSMASK,	VATTR_BIT(va_mode),		sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
525	{ATTR_CMN_FLAGS,	VATTR_BIT(va_flags),		sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
526	{ATTR_CMN_GEN_COUNT,	VATTR_BIT(va_gen),		sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
527	{ATTR_CMN_DOCUMENT_ID,	VATTR_BIT(va_document_id),	sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
528	{ATTR_CMN_USERACCESS,	0,				sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
529	{ATTR_CMN_EXTENDED_SECURITY, VATTR_BIT(va_acl),		sizeof(struct attrreference),	KAUTH_VNODE_READ_SECURITY},
530	{ATTR_CMN_UUID,		VATTR_BIT(va_uuuid),		sizeof(guid_t),			KAUTH_VNODE_READ_ATTRIBUTES},
531	{ATTR_CMN_GRPUUID,	VATTR_BIT(va_guuid),		sizeof(guid_t),			KAUTH_VNODE_READ_ATTRIBUTES},
532	{ATTR_CMN_FILEID,	VATTR_BIT(va_fileid), 		sizeof(uint64_t),		KAUTH_VNODE_READ_ATTRIBUTES},
533	{ATTR_CMN_PARENTID,	VATTR_BIT(va_parentid),		sizeof(uint64_t),		KAUTH_VNODE_READ_ATTRIBUTES},
534	{ATTR_CMN_FULLPATH, 	0, 				sizeof(struct attrreference),	KAUTH_VNODE_READ_ATTRIBUTES},
535	{ATTR_CMN_ADDEDTIME, 	VATTR_BIT(va_addedtime), 	ATTR_TIME_SIZE,			KAUTH_VNODE_READ_ATTRIBUTES},
536	{ATTR_CMN_RETURNED_ATTRS, 0,				sizeof(attribute_set_t),	0},
537	{ATTR_CMN_ERROR, 	0,				sizeof(uint32_t),		0},
538	{0, 0, 0, 0}
539};
540
541static struct getattrlist_attrtab getattrlist_dir_tab[] = {
542	{ATTR_DIR_LINKCOUNT,	VATTR_BIT(va_dirlinkcount),	sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
543	{ATTR_DIR_ENTRYCOUNT,	VATTR_BIT(va_nchildren),	sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
544	{ATTR_DIR_MOUNTSTATUS,	0,				sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
545	{0, 0, 0, 0}
546};
547static struct getattrlist_attrtab getattrlist_file_tab[] = {
548	{ATTR_FILE_LINKCOUNT,	VATTR_BIT(va_nlink),		sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
549	{ATTR_FILE_TOTALSIZE,	VATTR_BIT(va_total_size),	sizeof(off_t),			KAUTH_VNODE_READ_ATTRIBUTES},
550	{ATTR_FILE_ALLOCSIZE,	VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
551	{ATTR_FILE_IOBLOCKSIZE,	VATTR_BIT(va_iosize),		sizeof(uint32_t),		KAUTH_VNODE_READ_ATTRIBUTES},
552	{ATTR_FILE_DEVTYPE,	VATTR_BIT(va_rdev),		sizeof(dev_t),			KAUTH_VNODE_READ_ATTRIBUTES},
553	{ATTR_FILE_DATALENGTH,	VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
554	{ATTR_FILE_DATAALLOCSIZE, VATTR_BIT(va_total_alloc)| VATTR_BIT(va_data_alloc), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
555	{ATTR_FILE_RSRCLENGTH,	0,				sizeof(off_t),			KAUTH_VNODE_READ_ATTRIBUTES},
556	{ATTR_FILE_RSRCALLOCSIZE, 0,				sizeof(off_t),			KAUTH_VNODE_READ_ATTRIBUTES},
557	{0, 0, 0, 0}
558};
559
560/*
561 * The following are attributes that VFS can derive.
562 *
563 * A majority of them are the same attributes that are required for stat(2) and statfs(2).
564 */
565#define VFS_DFLT_ATTR_VOL	(ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE |  \
566				 ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE |  \
567				 ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION |  \
568				 ATTR_VOL_ALLOCATIONCLUMP |  ATTR_VOL_IOBLOCKSIZE |  \
569				 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |  \
570				 ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |  \
571				 ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
572
573#define VFS_DFLT_ATTR_CMN	(ATTR_CMN_NAME | ATTR_CMN_DEVID |  \
574				 ATTR_CMN_FSID | ATTR_CMN_OBJTYPE |  \
575				 ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |  \
576				 ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT |  \
577				 ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME |  \
578				 ATTR_CMN_FNDRINFO |  \
579				 ATTR_CMN_OWNERID  | ATTR_CMN_GRPID |  \
580				 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS |  \
581				 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
582				 ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS | \
583				 ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT)
584
585#define VFS_DFLT_ATTR_CMN_EXT	(ATTR_CMN_EXT_GEN_COUNT | ATTR_CMN_EXT_DOCUMENT_ID)
586
587#define VFS_DFLT_ATTR_DIR	(ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
588
589#define VFS_DFLT_ATTR_FILE	(ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |  \
590				 ATTR_FILE_ALLOCSIZE  | ATTR_FILE_IOBLOCKSIZE |  \
591				 ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH |  \
592				 ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH |  \
593				 ATTR_FILE_RSRCALLOCSIZE)
594
595static int
596getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs, struct vnode_attr *vap,
597    ssize_t *sizep, kauth_action_t *actionp, int is_64bit)
598{
599	attrgroup_t	recognised;
600
601	recognised = 0;
602	do {
603		/* is this attribute set? */
604		if (tab->attr & attrs) {
605			recognised |= tab->attr;
606			vap->va_active |= tab->bits;
607			if (tab->size == ATTR_TIME_SIZE) {
608				if (is_64bit) {
609					*sizep += sizeof(struct user64_timespec);
610				} else {
611					*sizep += sizeof(struct user32_timespec);
612				}
613			} else {
614				*sizep += tab->size;
615			}
616			*actionp |= tab->action;
617			if (attrs == recognised)
618				break;  /* all done, get out */
619		}
620	} while ((++tab)->attr != 0);
621
622	/* check to make sure that we recognised all of the passed-in attributes */
623	if (attrs & ~recognised)
624		return(EINVAL);
625	return(0);
626}
627
628/*
629 * Given the attributes listed in alp, configure vap to request
630 * the data from a filesystem.
631 */
632static int
633getattrlist_setupvattr(struct attrlist *alp, int attr_cmn_extended, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir)
634{
635	int	error;
636	struct getattrlist_attrtab *cmn_tab;
637
638
639	if (attr_cmn_extended)
640		cmn_tab = getattrlist_common_tab_extended;
641	else
642		cmn_tab = getattrlist_common_tab;
643	/*
644	 * Parse the above tables.
645	 */
646	*sizep = sizeof(uint32_t);	/* length count */
647	*actionp = 0;
648	if (alp->commonattr &&
649	    (error = getattrlist_parsetab(cmn_tab, alp->commonattr, vap, sizep, actionp, is_64bit)) != 0)
650		return(error);
651	if (isdir && alp->dirattr &&
652	    (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit)) != 0)
653		return(error);
654	if (!isdir && alp->fileattr &&
655	    (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit)) != 0)
656		return(error);
657
658	return(0);
659}
660
661/*
662 * Given the attributes listed in asp and those supported
663 * in the vap, fixup the asp attributes to reflect any
664 * missing attributes from the file system
665 */
666static void
667getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap)
668{
669	struct getattrlist_attrtab *tab;
670
671	if (asp->commonattr) {
672		tab = getattrlist_common_tab;
673		do {
674            /*
675			 * This if() statement is slightly confusing. We're trying to
676			 * iterate through all of the bits listed in the array
677			 * getattr_common_tab, and see if the filesystem was expected
678			 * to support it, and whether or not we need to do anything about this.
679			 *
680			 * This array is full of structs that have 4 fields (attr, bits, size, action).
681			 * The first is used to store the ATTR_CMN_* bit that was being requested
682			 * from userland.  The second stores the VATTR_BIT corresponding to the field
683			 * filled in vnode_attr struct.  If it is 0, then we don't typically expect
684			 * the filesystem to fill in this field.  The third is the size of the field,
685			 * and the fourth is the type of kauth actions needed.
686			 *
687			 * So, for all of the ATTR_CMN bits listed in this array, we iterate through
688			 * them, and check to see if it was both passed down to the filesystem via the
689			 * va_active bitfield, and whether or not we expect it to be emitted from
690			 * the filesystem.  If it wasn't supported, then we un-twiddle the bit and move
691			 * on.  This is done so that we can uncheck those bits and re-request
692			 * a vnode_getattr from the filesystem again.
693			 */
694			if ((tab->attr & asp->commonattr) &&
695			    (tab->bits & vap->va_active) &&
696			    (tab->bits & vap->va_supported) == 0) {
697				asp->commonattr &= ~tab->attr;
698			}
699		} while ((++tab)->attr != 0);
700	}
701	if (asp->dirattr) {
702		tab = getattrlist_dir_tab;
703		do {
704			if ((tab->attr & asp->dirattr) &&
705			    (tab->bits & vap->va_active) &&
706			    (vap->va_supported & tab->bits) == 0) {
707				asp->dirattr &= ~tab->attr;
708			}
709		} while ((++tab)->attr != 0);
710	}
711	if (asp->fileattr) {
712		tab = getattrlist_file_tab;
713		do {
714			if ((tab->attr & asp->fileattr) &&
715			    (tab->bits & vap->va_active) &&
716			    (vap->va_supported & tab->bits) == 0) {
717				asp->fileattr &= ~tab->attr;
718			}
719		} while ((++tab)->attr != 0);
720	}
721}
722
723static int
724setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
725{
726	uio_t	auio;
727	char	uio_buf[UIO_SIZEOF(1)];
728	int	error;
729
730	if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
731		error = ENOMEM;
732	} else {
733		uio_addiov(auio, CAST_USER_ADDR_T(fndrinfo), 32);
734		error = vn_setxattr(vp, XATTR_FINDERINFO_NAME, auio, XATTR_NOSECURITY, ctx);
735		uio_free(auio);
736	}
737
738#if CONFIG_FSE
739	if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) {
740	    add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
741	}
742#endif
743	return (error);
744}
745
746
747/*
748 * Find something resembling a terminal component name in the mountedonname for vp
749 *
750 */
751static void
752getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
753{
754	int		counting;
755	const char	*cp;
756
757	/*
758	 * We're looking for the last sequence of non / characters, but
759	 * not including any trailing / characters.
760	 */
761	*np = NULL;
762	*nl = 0;
763	counting = 0;
764	for (cp = mn; *cp != 0; cp++) {
765		if (!counting) {
766			/* start of run of chars */
767			if (*cp != '/') {
768				*np = cp;
769				counting = 1;
770			}
771		} else {
772			/* end of run of chars */
773			if (*cp == '/') {
774				*nl = cp - *np;
775				counting = 0;
776			}
777		}
778	}
779	/* need to close run? */
780	if (counting)
781		*nl = cp - *np;
782}
783
784
785static int
786getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
787               vfs_context_t ctx, int is_64bit)
788{
789	struct vfs_attr vs;
790	struct vnode_attr va;
791	struct _attrlist_buf ab;
792	int		error;
793	ssize_t		fixedsize, varsize;
794	const char	*cnp = NULL;	/* protected by ATTR_CMN_NAME */
795	ssize_t		cnl = 0;	/* protected by ATTR_CMN_NAME */
796	int		release_str = 0;
797	mount_t		mnt;
798	int		return_valid;
799	int		pack_invalid;
800
801	ab.base = NULL;
802	VATTR_INIT(&va);
803	VFSATTR_INIT(&vs);
804	vs.f_vol_name = NULL;
805	mnt = vp->v_mount;
806
807	/* Check for special packing semantics */
808	return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS);
809	pack_invalid = (uap->options & FSOPT_PACK_INVAL_ATTRS);
810	if (pack_invalid) {
811		/* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
812		if (!return_valid) {
813			error = EINVAL;
814			goto out;
815		}
816		/* Keep invalid attrs from being uninitialized */
817		bzero(&vs, sizeof (vs));
818		/* Generate a valid mask for post processing */
819		bcopy(&alp->commonattr, &ab.valid, sizeof (attribute_set_t));
820	}
821
822	/*
823	 * For now, the vnode must be the root of its filesystem.
824	 * To relax this, we need to be able to find the root vnode of a filesystem
825	 * from any vnode in the filesystem.
826	 */
827	if (!vnode_isvroot(vp)) {
828		error = EINVAL;
829		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
830		goto out;
831	}
832
833	/*
834	 * Set up the vfs_attr structure and call the filesystem.
835	 */
836	if ((error = getvolattrlist_setupvfsattr(alp, &vs, &fixedsize, is_64bit)) != 0) {
837		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
838		goto out;
839	}
840	if (vs.f_active != 0) {
841		/* If we're going to ask for f_vol_name, allocate a buffer to point it at */
842		if (VFSATTR_IS_ACTIVE(&vs, f_vol_name)) {
843			vs.f_vol_name = (char *) kalloc(MAXPATHLEN);
844			if (vs.f_vol_name == NULL) {
845				error = ENOMEM;
846				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
847				goto out;
848			}
849		}
850
851#if CONFIG_MACF
852		error = mac_mount_check_getattr(ctx, mnt, &vs);
853		if (error != 0)
854			goto out;
855#endif
856		VFS_DEBUG(ctx, vp, "ATTRLIST -       calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported);
857		if ((error = vfs_getattr(mnt, &vs, ctx)) != 0) {
858			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
859			goto out;
860		}
861
862		/*
863		 * Did we ask for something the filesystem doesn't support?
864		 */
865		if (!VFSATTR_ALL_SUPPORTED(&vs)) {
866			/* default value for volume subtype */
867			if (VFSATTR_IS_ACTIVE(&vs, f_fssubtype)
868			    && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype))
869				VFSATTR_RETURN(&vs, f_fssubtype, 0);
870
871			/*
872			 * If the file system didn't supply f_signature, then
873			 * default it to 'BD', which is the generic signature
874			 * that most Carbon file systems should return.
875			 */
876			if (VFSATTR_IS_ACTIVE(&vs, f_signature)
877			    && !VFSATTR_IS_SUPPORTED(&vs, f_signature))
878				VFSATTR_RETURN(&vs, f_signature, 0x4244);
879
880			/* default for block size */
881			if (VFSATTR_IS_ACTIVE(&vs, f_bsize)
882			    && !VFSATTR_IS_SUPPORTED(&vs, f_bsize))
883				VFSATTR_RETURN(&vs, f_bsize, mnt->mnt_devblocksize);
884
885			/* default value for volume f_attributes */
886			if (VFSATTR_IS_ACTIVE(&vs, f_attributes)
887			    && !VFSATTR_IS_SUPPORTED(&vs, f_attributes)) {
888				vol_attributes_attr_t *attrp = &vs.f_attributes;
889
890				attrp->validattr.commonattr = VFS_DFLT_ATTR_CMN;
891				attrp->validattr.volattr = VFS_DFLT_ATTR_VOL;
892				attrp->validattr.dirattr = VFS_DFLT_ATTR_DIR;
893				attrp->validattr.fileattr = VFS_DFLT_ATTR_FILE;
894				attrp->validattr.forkattr = 0;
895
896				attrp->nativeattr.commonattr =  0;
897				attrp->nativeattr.volattr = 0;
898				attrp->nativeattr.dirattr = 0;
899				attrp->nativeattr.fileattr = 0;
900				attrp->nativeattr.forkattr = 0;
901				VFSATTR_SET_SUPPORTED(&vs, f_attributes);
902			}
903
904			/* default value for volume f_capabilities */
905			if (VFSATTR_IS_ACTIVE(&vs, f_capabilities)) {
906				/* getattrlist is always supported now. */
907				if (!VFSATTR_IS_SUPPORTED(&vs, f_capabilities)) {
908					vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = 0;
909					vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
910					vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
911					vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
912
913					vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0;
914					vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
915					vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
916					vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
917					VFSATTR_SET_SUPPORTED(&vs, f_capabilities);
918				}
919				else {
920					/* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
921					vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
922					vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
923				}
924			}
925
926			/* check to see if our fixups were enough */
927			if (!VFSATTR_ALL_SUPPORTED(&vs)) {
928				if (return_valid) {
929					if (pack_invalid) {
930						/* Fix up valid mask for post processing */
931						getvolattrlist_fixupattrs(&ab.valid, &vs);
932
933						/* Force packing of everything asked for */
934						vs.f_supported = vs.f_active;
935					} else {
936						/* Adjust the requested attributes */
937						getvolattrlist_fixupattrs((attribute_set_t *)&alp->commonattr, &vs);
938					}
939				} else {
940					error = EINVAL;
941					goto out;
942				}
943			}
944		}
945	}
946
947	/*
948	 * Some fields require data from the root vp
949	 */
950	if (alp->commonattr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | ATTR_CMN_SCRIPT)) {
951		VATTR_WANTED(&va, va_uid);
952		VATTR_WANTED(&va, va_gid);
953		VATTR_WANTED(&va, va_mode);
954		VATTR_WANTED(&va, va_flags);
955		VATTR_WANTED(&va, va_encoding);
956
957		if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
958			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp);
959			goto out;
960		}
961
962		if (VATTR_IS_ACTIVE(&va, va_encoding) &&
963		    !VATTR_IS_SUPPORTED(&va, va_encoding)) {
964			if (!return_valid || pack_invalid)
965				/* use kTextEncodingMacUnicode */
966				VATTR_RETURN(&va, va_encoding, 0x7e);
967			else
968				/* don't use a default */
969				alp->commonattr &= ~ATTR_CMN_SCRIPT;
970		}
971	}
972
973	/*
974	 * Compute variable-size buffer requirements.
975	 */
976	varsize = 0;
977	if (alp->commonattr & ATTR_CMN_NAME) {
978		if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
979			vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
980			/* special case for boot volume.  Use root name when it's
981			 * available (which is the volume name) or just the mount on
982			 * name of "/".  we must do this for binary compatibility with
983			 * pre Tiger code.  returning nothing for the boot volume name
984			 * breaks installers - 3961058
985			 */
986			cnp = vnode_getname(vp);
987			if (cnp == NULL) {
988				/* just use "/" as name */
989				cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
990			}
991			else {
992				release_str = 1;
993			}
994			cnl = strlen(cnp);
995		}
996		else {
997			getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
998		}
999		if (alp->commonattr & ATTR_CMN_NAME)
1000			varsize += roundup(cnl + 1, 4);
1001	}
1002	if (alp->volattr & ATTR_VOL_MOUNTPOINT)
1003		varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntonname) + 1, 4);
1004	if (alp->volattr & ATTR_VOL_NAME) {
1005		vs.f_vol_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
1006		varsize += roundup(strlen(vs.f_vol_name) + 1, 4);
1007	}
1008	if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
1009		varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4);
1010
1011	/*
1012	 * Allocate a target buffer for attribute results.
1013	 * Note that since we won't ever copy out more than the caller requested,
1014	 * we never need to allocate more than they offer.
1015	 */
1016	ab.allocated = ulmin(uap->bufferSize, fixedsize + varsize);
1017	if (ab.allocated > ATTR_MAX_BUFFER) {
1018		error = ENOMEM;
1019		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
1020		goto out;
1021	}
1022	MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
1023	if (ab.base == NULL) {
1024		error = ENOMEM;
1025		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
1026		goto out;
1027	}
1028
1029	/*
1030	 * Pack results into the destination buffer.
1031	 */
1032	ab.fixedcursor = ab.base + sizeof(uint32_t);
1033	if (return_valid) {
1034		ab.fixedcursor += sizeof (attribute_set_t);
1035		bzero(&ab.actual, sizeof (ab.actual));
1036	}
1037	ab.varcursor = ab.base + fixedsize;
1038	ab.needed = fixedsize + varsize;
1039
1040	/* common attributes **************************************************/
1041	if (alp->commonattr & ATTR_CMN_NAME) {
1042		attrlist_pack_string(&ab, cnp, cnl);
1043		ab.actual.commonattr |= ATTR_CMN_NAME;
1044	}
1045	if ((alp->commonattr & ATTR_CMN_ERROR) &&
1046	    (!return_valid || pack_invalid)) {
1047		ATTR_PACK4(ab, 0);
1048		ab.actual.commonattr |= ATTR_CMN_ERROR;
1049	}
1050	if (alp->commonattr & ATTR_CMN_DEVID) {
1051		ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]);
1052		ab.actual.commonattr |= ATTR_CMN_DEVID;
1053	}
1054	if (alp->commonattr & ATTR_CMN_FSID) {
1055		ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid);
1056		ab.actual.commonattr |= ATTR_CMN_FSID;
1057	}
1058	if (alp->commonattr & ATTR_CMN_OBJTYPE) {
1059		if (!return_valid || pack_invalid)
1060			ATTR_PACK4(ab, 0);
1061	}
1062	if (alp->commonattr & ATTR_CMN_OBJTAG) {
1063		ATTR_PACK4(ab, vp->v_tag);
1064		ab.actual.commonattr |= ATTR_CMN_OBJTAG;
1065	}
1066	if (alp->commonattr & ATTR_CMN_OBJID) {
1067		if (!return_valid || pack_invalid) {
1068			fsobj_id_t f = {0, 0};
1069			ATTR_PACK8(ab, f);
1070		}
1071	}
1072	if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
1073		if (!return_valid || pack_invalid) {
1074			fsobj_id_t f = {0, 0};
1075			ATTR_PACK8(ab, f);
1076		}
1077	}
1078	if (alp->commonattr & ATTR_CMN_PAROBJID) {
1079		if (!return_valid || pack_invalid) {
1080			fsobj_id_t f = {0, 0};
1081			ATTR_PACK8(ab, f);
1082		}
1083	}
1084	/* note that this returns the encoding for the volume name, not the node name */
1085	if (alp->commonattr & ATTR_CMN_SCRIPT) {
1086		ATTR_PACK4(ab, va.va_encoding);
1087		ab.actual.commonattr |= ATTR_CMN_SCRIPT;
1088	}
1089	if (alp->commonattr & ATTR_CMN_CRTIME) {
1090		ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit);
1091		ab.actual.commonattr |= ATTR_CMN_CRTIME;
1092	}
1093	if (alp->commonattr & ATTR_CMN_MODTIME) {
1094		ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
1095		ab.actual.commonattr |= ATTR_CMN_MODTIME;
1096	}
1097	if (alp->commonattr & ATTR_CMN_CHGTIME) {
1098		if (!return_valid || pack_invalid)
1099			ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
1100	}
1101	if (alp->commonattr & ATTR_CMN_ACCTIME) {
1102		ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
1103		ab.actual.commonattr |= ATTR_CMN_ACCTIME;
1104	}
1105	if (alp->commonattr & ATTR_CMN_BKUPTIME) {
1106		ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit);
1107		ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
1108	}
1109	if (alp->commonattr & ATTR_CMN_FNDRINFO) {
1110		char f[32];
1111		/*
1112		 * This attribute isn't really Finder Info, at least for HFS.
1113		 */
1114		if (vp->v_tag == VT_HFS) {
1115			error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx);
1116			if (error == 0) {
1117				attrlist_pack_fixed(&ab, f, sizeof(f));
1118				ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
1119			} else if (!return_valid) {
1120				goto out;
1121			}
1122		} else if (!return_valid || pack_invalid) {
1123			/* XXX we could at least pass out the volume UUID here */
1124			bzero(&f, sizeof(f));
1125			attrlist_pack_fixed(&ab, f, sizeof(f));
1126		}
1127	}
1128	if (alp->commonattr & ATTR_CMN_OWNERID) {
1129		ATTR_PACK4(ab, va.va_uid);
1130		ab.actual.commonattr |= ATTR_CMN_OWNERID;
1131	}
1132	if (alp->commonattr & ATTR_CMN_GRPID) {
1133		ATTR_PACK4(ab, va.va_gid);
1134		ab.actual.commonattr |= ATTR_CMN_GRPID;
1135	}
1136	if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
1137		ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
1138		ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
1139	}
1140	if (alp->commonattr & ATTR_CMN_FLAGS) {
1141		ATTR_PACK4(ab, va.va_flags);
1142		ab.actual.commonattr |= ATTR_CMN_FLAGS;
1143	}
1144	if (alp->commonattr & ATTR_CMN_USERACCESS) {	/* XXX this is expensive and also duplicate work */
1145		uint32_t	perms = 0;
1146		if (vnode_isdir(vp)) {
1147			if (vnode_authorize(vp, NULL,
1148				KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
1149				perms |= W_OK;
1150			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
1151				perms |= R_OK;
1152			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
1153				perms |= X_OK;
1154		} else {
1155			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
1156				perms |= W_OK;
1157			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
1158				perms |= R_OK;
1159			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
1160				perms |= X_OK;
1161		}
1162#if CONFIG_MACF
1163		/*
1164		 * Rather than MAC preceding DAC, in this case we want
1165		 * the smallest set of permissions granted by both MAC & DAC
1166		 * checks.  We won't add back any permissions.
1167		 */
1168		if (perms & W_OK)
1169			if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
1170				perms &= ~W_OK;
1171		if (perms & R_OK)
1172			if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
1173				perms &= ~R_OK;
1174		if (perms & X_OK)
1175			if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
1176				perms &= ~X_OK;
1177#endif /* MAC */
1178		KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
1179		ATTR_PACK4(ab, perms);
1180		ab.actual.commonattr |= ATTR_CMN_USERACCESS;
1181	}
1182	/*
1183	 * The following common volume attributes are only
1184	 * packed when the pack_invalid mode is enabled.
1185	 */
1186	if (pack_invalid) {
1187		uint64_t fid = 0;
1188
1189		if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY)
1190			attrlist_pack_variable(&ab, NULL, 0);
1191		if (alp->commonattr & ATTR_CMN_UUID)
1192			ATTR_PACK(&ab, kauth_null_guid);
1193		if (alp->commonattr & ATTR_CMN_GRPUUID)
1194			ATTR_PACK(&ab, kauth_null_guid);
1195		if (alp->commonattr & ATTR_CMN_FILEID)
1196			ATTR_PACK8(ab, fid);
1197		if (alp->commonattr & ATTR_CMN_PARENTID)
1198			ATTR_PACK8(ab, fid);
1199	}
1200
1201	/* volume attributes **************************************************/
1202
1203	if (alp->volattr & ATTR_VOL_FSTYPE) {
1204		ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
1205		ab.actual.volattr |= ATTR_VOL_FSTYPE;
1206	}
1207 	if (alp->volattr & ATTR_VOL_SIGNATURE) {
1208 		ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
1209		ab.actual.volattr |= ATTR_VOL_SIGNATURE;
1210	}
1211	if (alp->volattr & ATTR_VOL_SIZE) {
1212		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
1213		ab.actual.volattr |= ATTR_VOL_SIZE;
1214	}
1215	if (alp->volattr & ATTR_VOL_SPACEFREE) {
1216		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
1217		ab.actual.volattr |= ATTR_VOL_SPACEFREE;
1218	}
1219	if (alp->volattr & ATTR_VOL_SPACEAVAIL) {
1220		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
1221		ab.actual.volattr |= ATTR_VOL_SPACEAVAIL;
1222	}
1223	if (alp->volattr & ATTR_VOL_MINALLOCATION) {
1224		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
1225		ab.actual.volattr |= ATTR_VOL_MINALLOCATION;
1226	}
1227	if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP) {
1228		ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);			/* not strictly true */
1229		ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP;
1230	}
1231	if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) {
1232		ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
1233		ab.actual.volattr |= ATTR_VOL_IOBLOCKSIZE;
1234	}
1235	if (alp->volattr & ATTR_VOL_OBJCOUNT) {
1236		ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
1237		ab.actual.volattr |= ATTR_VOL_OBJCOUNT;
1238	}
1239	if (alp->volattr & ATTR_VOL_FILECOUNT) {
1240		ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
1241		ab.actual.volattr |= ATTR_VOL_FILECOUNT;
1242	}
1243	if (alp->volattr & ATTR_VOL_DIRCOUNT) {
1244		ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
1245		ab.actual.volattr |= ATTR_VOL_DIRCOUNT;
1246	}
1247	if (alp->volattr & ATTR_VOL_MAXOBJCOUNT) {
1248		ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
1249		ab.actual.volattr |= ATTR_VOL_MAXOBJCOUNT;
1250	}
1251	if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
1252		attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
1253		ab.actual.volattr |= ATTR_VOL_MOUNTPOINT;
1254	}
1255	if (alp->volattr & ATTR_VOL_NAME) {
1256		attrlist_pack_string(&ab, vs.f_vol_name, 0);
1257		ab.actual.volattr |= ATTR_VOL_NAME;
1258	}
1259	if (alp->volattr & ATTR_VOL_MOUNTFLAGS) {
1260		ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
1261		ab.actual.volattr |= ATTR_VOL_MOUNTFLAGS;
1262	}
1263	if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
1264		attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
1265		ab.actual.volattr |= ATTR_VOL_MOUNTEDDEVICE;
1266	}
1267	if (alp->volattr & ATTR_VOL_ENCODINGSUSED) {
1268		if (!return_valid || pack_invalid)
1269			ATTR_PACK_CAST(&ab, uint64_t, ~0LL);  /* return all encodings */
1270	}
1271	if (alp->volattr & ATTR_VOL_CAPABILITIES) {
1272		/* fix up volume capabilities */
1273		if (vfs_extendedsecurity(mnt)) {
1274			vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
1275		} else {
1276			vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
1277		}
1278		vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
1279		ATTR_PACK(&ab, vs.f_capabilities);
1280		ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
1281	}
1282	if (alp->volattr & ATTR_VOL_UUID) {
1283		ATTR_PACK(&ab, vs.f_uuid);
1284		ab.actual.volattr |= ATTR_VOL_UUID;
1285	}
1286	if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
1287		/* fix up volume attribute information */
1288
1289		vs.f_attributes.validattr.commonattr |= VFS_DFLT_ATTR_CMN;
1290		vs.f_attributes.validattr.volattr |= VFS_DFLT_ATTR_VOL;
1291		vs.f_attributes.validattr.dirattr |= VFS_DFLT_ATTR_DIR;
1292		vs.f_attributes.validattr.fileattr |= VFS_DFLT_ATTR_FILE;
1293
1294		if (vfs_extendedsecurity(mnt)) {
1295			vs.f_attributes.validattr.commonattr |= (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1296		} else {
1297			vs.f_attributes.validattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1298			vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1299		}
1300		ATTR_PACK(&ab, vs.f_attributes);
1301		ab.actual.volattr |= ATTR_VOL_ATTRIBUTES;
1302	}
1303
1304	/* diagnostic */
1305	if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
1306		panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
1307		    fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
1308	if (!return_valid && ab.varcursor != (ab.base + ab.needed))
1309		panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
1310
1311	/*
1312	 * In the compatible case, we report the smaller of the required and returned sizes.
1313	 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1314	 * of the result buffer, even if we copied less out.  The caller knows how big a buffer
1315	 * they gave us, so they can always check for truncation themselves.
1316	 */
1317	*(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
1318
1319	/* Return attribute set output if requested. */
1320	if (return_valid) {
1321		ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
1322		if (pack_invalid) {
1323			/* Only report the attributes that are valid */
1324			ab.actual.commonattr &= ab.valid.commonattr;
1325			ab.actual.volattr &= ab.valid.volattr;
1326		}
1327		bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
1328	}
1329	error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
1330
1331out:
1332	if (vs.f_vol_name != NULL)
1333		kfree(vs.f_vol_name, MAXPATHLEN);
1334	if (release_str) {
1335		vnode_putname(cnp);
1336	}
1337	if (ab.base != NULL)
1338		FREE(ab.base, M_TEMP);
1339	VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
1340	return(error);
1341}
1342
1343/*
1344 * Obtain attribute information about a filesystem object.
1345 */
1346
1347static int
1348getattrlist_internal(vnode_t vp, struct getattrlist_args *uap,
1349		__unused struct componentname *getattr_name, proc_t p, vfs_context_t ctx)
1350{
1351	struct attrlist	al;
1352	struct vnode_attr va;
1353	struct _attrlist_buf ab;
1354	kauth_action_t	action;
1355	ssize_t		fixedsize, varsize;
1356	const char	*cnp;
1357	const char	*vname = NULL;
1358	char 		*fullpathptr;
1359	ssize_t		fullpathlen;
1360	ssize_t		cnl;
1361	int		proc_is64;
1362	int		error;
1363	int		return_valid;
1364	int		pack_invalid;
1365	int		attr_extended;
1366	int		vtype = 0;
1367	uint32_t	perms = 0;
1368
1369	proc_is64 = proc_is64bit(p);
1370	VATTR_INIT(&va);
1371	va.va_name = NULL;
1372	ab.base = NULL;
1373	cnp = "unknown";
1374	cnl = 0;
1375	fullpathptr = NULL;
1376	fullpathlen = 0;
1377
1378	/*
1379	 * Fetch the attribute request.
1380	 */
1381	if ((error = copyin(uap->alist, &al, sizeof(al))) != 0)
1382		goto out;
1383	if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
1384		error = EINVAL;
1385		goto out;
1386	}
1387
1388	VFS_DEBUG(ctx, vp, "%p  ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1389	    vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
1390	    (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
1391
1392#if CONFIG_MACF
1393	error = mac_vnode_check_getattrlist(ctx, vp, &al);
1394	if (error)
1395		goto out;
1396#endif /* MAC */
1397
1398	/*
1399	 * It is legal to request volume or file attributes,
1400	 * but not both.
1401	 */
1402	if (al.volattr) {
1403		if (al.fileattr || al.dirattr || al.forkattr) {
1404			error = EINVAL;
1405			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
1406			goto out;
1407		}
1408		/* handle volume attribute request */
1409		error = getvolattrlist(vp, uap, &al, ctx, proc_is64);
1410		goto out;
1411	}
1412
1413	/* Check for special packing semantics */
1414	return_valid = (al.commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
1415	pack_invalid = (uap->options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
1416	attr_extended = (uap->options & FSOPT_ATTRLIST_EXTENDED) ? 1 : 0;
1417	if (pack_invalid) {
1418		/* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
1419		if (!return_valid || al.forkattr) {
1420			error = EINVAL;
1421			goto out;
1422		}
1423		/* Keep invalid attrs from being uninitialized */
1424		bzero(&va, sizeof (va));
1425		/* Generate a valid mask for post processing */
1426		bcopy(&al.commonattr, &ab.valid, sizeof (attribute_set_t));
1427	}
1428
1429	/* Pick up the vnode type.  If the FS is bad and changes vnode types on us, we
1430	 * will have a valid snapshot that we can work from here.
1431	 */
1432	vtype = vp->v_type;
1433
1434
1435	/*
1436	 * Set up the vnode_attr structure and authorise.
1437	 */
1438	if ((error = getattrlist_setupvattr(&al, attr_extended, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) {
1439		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
1440		goto out;
1441	}
1442	if ((error = vnode_authorize(vp, NULL, action, ctx)) != 0) {
1443		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorisation failed/denied");
1444		goto out;
1445	}
1446
1447	/*
1448	 * If we're asking for the full path, allocate a buffer for that.
1449	 */
1450	if (al.commonattr & (ATTR_CMN_FULLPATH)) {
1451		fullpathptr = (char*) kalloc(MAXPATHLEN);
1452		if (fullpathptr == NULL) {
1453			error = ENOMEM;
1454			VFS_DEBUG(ctx,vp, "ATTRLIST - ERROR: cannot allocate fullpath buffer");
1455			goto out;
1456		}
1457	}
1458
1459
1460	if (va.va_active != 0) {
1461		/*
1462		 * If we're going to ask for va_name, allocate a buffer to point it at
1463		 */
1464		if (VATTR_IS_ACTIVE(&va, va_name)) {
1465			va.va_name = (char *) kalloc(MAXPATHLEN);
1466			if (va.va_name == NULL) {
1467				error = ENOMEM;
1468				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
1469				goto out;
1470			}
1471		}
1472
1473		/*
1474		 * Call the filesystem.
1475		 */
1476		if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
1477			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
1478			goto out;
1479		}
1480
1481		/* did we ask for something the filesystem doesn't support? */
1482		if (!VATTR_ALL_SUPPORTED(&va)) {
1483
1484			/*
1485			 * There are a couple of special cases.  If we are after object IDs,
1486			 * we can make do with va_fileid.
1487			 */
1488			if ((al.commonattr & (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) && !VATTR_IS_SUPPORTED(&va, va_linkid))
1489				VATTR_CLEAR_ACTIVE(&va, va_linkid);	/* forget we wanted this */
1490
1491			/*
1492			 * Many filesystems don't know their parent object id.
1493			 * If necessary, attempt to derive it from the vnode.
1494			 */
1495			if ((al.commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) &&
1496			    !VATTR_IS_SUPPORTED(&va, va_parentid)) {
1497				vnode_t	dvp;
1498
1499				if ((dvp = vnode_getparent(vp)) != NULLVP) {
1500					struct vnode_attr lva;
1501
1502					VATTR_INIT(&lva);
1503					VATTR_WANTED(&lva, va_fileid);
1504					if (vnode_getattr(dvp, &lva, ctx) == 0 &&
1505					    VATTR_IS_SUPPORTED(&va, va_fileid)) {
1506						va.va_parentid = lva.va_fileid;
1507						VATTR_SET_SUPPORTED(&va, va_parentid);
1508					}
1509					vnode_put(dvp);
1510				}
1511			}
1512			/*
1513			 * And we can report datasize/alloc from total.
1514			 */
1515			if ((al.fileattr & ATTR_FILE_DATALENGTH) && !VATTR_IS_SUPPORTED(&va, va_data_size))
1516				VATTR_CLEAR_ACTIVE(&va, va_data_size);
1517			if ((al.fileattr & ATTR_FILE_DATAALLOCSIZE) && !VATTR_IS_SUPPORTED(&va, va_data_alloc))
1518				VATTR_CLEAR_ACTIVE(&va, va_data_alloc);
1519
1520			/*
1521			 * If we don't have an encoding, go with UTF-8
1522			 */
1523			if ((al.commonattr & ATTR_CMN_SCRIPT) &&
1524			    !VATTR_IS_SUPPORTED(&va, va_encoding) && !return_valid)
1525				VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
1526
1527			/*
1528			 * If we don't have a name, we'll get one from the vnode or mount point.
1529			 */
1530			if ((al.commonattr & ATTR_CMN_NAME) && !VATTR_IS_SUPPORTED(&va, va_name)) {
1531				VATTR_CLEAR_ACTIVE(&va, va_name);
1532			}
1533
1534			/* If va_dirlinkcount isn't supported use a default of 1. */
1535			if ((al.dirattr & ATTR_DIR_LINKCOUNT) && !VATTR_IS_SUPPORTED(&va, va_dirlinkcount)) {
1536				VATTR_RETURN(&va, va_dirlinkcount, 1);
1537			}
1538
1539			/* check again */
1540			if (!VATTR_ALL_SUPPORTED(&va)) {
1541				if (return_valid) {
1542					if (pack_invalid) {
1543						/* Fix up valid mask for post processing */
1544						getattrlist_fixupattrs(&ab.valid, &va);
1545
1546						/* Force packing of everything asked for */
1547						va.va_supported = va.va_active;
1548					} else {
1549						/* Adjust the requested attributes */
1550						getattrlist_fixupattrs((attribute_set_t *)&al.commonattr, &va);
1551					}
1552				} else {
1553					error = EINVAL;
1554					goto out;
1555				}
1556			}
1557		}
1558	}
1559
1560	/*
1561	 * Compute variable-space requirements.
1562	 */
1563	varsize = 0; /* length count */
1564
1565	/* We may need to fix up the name attribute if requested */
1566	if (al.commonattr & ATTR_CMN_NAME) {
1567		if (VATTR_IS_SUPPORTED(&va, va_name)) {
1568			va.va_name[MAXPATHLEN-1] = '\0';	/* Ensure nul-termination */
1569			cnp = va.va_name;
1570			cnl = strlen(cnp);
1571		}
1572		else {
1573			/* Filesystem did not support getting the name */
1574			if (vnode_isvroot(vp)) {
1575				if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
1576						vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
1577					/* special case for boot volume.  Use root name when it's
1578					 * available (which is the volume name) or just the mount on
1579					 * name of "/".  we must do this for binary compatibility with
1580					 * pre Tiger code.  returning nothing for the boot volume name
1581					 * breaks installers - 3961058
1582					 */
1583					cnp = vname = vnode_getname(vp);
1584					if (cnp == NULL) {
1585						/* just use "/" as name */
1586						cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
1587					}
1588					cnl = strlen(cnp);
1589				}
1590				else {
1591					getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
1592				}
1593			}
1594			else {
1595				cnp = vname = vnode_getname(vp);
1596				cnl = 0;
1597				if (cnp != NULL) {
1598					cnl = strlen(cnp);
1599				}
1600			}
1601		}
1602		varsize += roundup(cnl + 1, 4);
1603	}
1604
1605	/*
1606	 * Compute the full path to this vnode, if necessary. This attribute is almost certainly
1607	 * not supported by any filesystem, so build the path to this vnode at this time.
1608	 */
1609	if (al.commonattr & ATTR_CMN_FULLPATH) {
1610		int len = MAXPATHLEN;
1611		int err;
1612		/* call build_path making sure NOT to use the cache-only behavior */
1613		err = build_path(vp, fullpathptr, len, &len, 0, vfs_context_current());
1614		if (err) {
1615			error = err;
1616			goto out;
1617		}
1618		fullpathlen = 0;
1619		if (fullpathptr){
1620			fullpathlen = strlen(fullpathptr);
1621		}
1622		varsize += roundup(fullpathlen+1, 4);
1623	}
1624
1625	/*
1626	 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
1627	 *
1628	 * XXX This needs to change at some point; since the blob is opaque in
1629	 * user-space this is OK.
1630	 */
1631	if ((al.commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
1632			VATTR_IS_SUPPORTED(&va, va_acl) &&
1633			(va.va_acl != NULL)) {
1634
1635		/*
1636		 * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
1637		 * KAUTH_FILESEC_NOACL ourselves
1638		 */
1639		if (va.va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) {
1640			varsize += roundup((KAUTH_FILESEC_SIZE(0)), 4);
1641		}
1642		else {
1643			varsize += roundup ((KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount)), 4);
1644		}
1645	}
1646
1647	/*
1648	 * Allocate a target buffer for attribute results.
1649	 *
1650	 * Note that we won't ever copy out more than the caller requested, even though
1651	 * we might have to allocate more than they offer so that the diagnostic checks
1652	 * don't result in a panic if the caller's buffer is too small..
1653	 */
1654	ab.allocated = fixedsize + varsize;
1655	/* Cast 'allocated' to an unsigned to verify allocation size */
1656	if ( ((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
1657		error = ENOMEM;
1658		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
1659		goto out;
1660	}
1661	MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
1662	if (ab.base == NULL) {
1663		error = ENOMEM;
1664		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
1665		goto out;
1666	}
1667
1668	/* set the S_IFMT bits for the mode */
1669	if (al.commonattr & ATTR_CMN_ACCESSMASK) {
1670		switch (vp->v_type) {
1671		case VREG:
1672			va.va_mode |= S_IFREG;
1673			break;
1674		case VDIR:
1675			va.va_mode |= S_IFDIR;
1676			break;
1677		case VBLK:
1678			va.va_mode |= S_IFBLK;
1679			break;
1680		case VCHR:
1681			va.va_mode |= S_IFCHR;
1682			break;
1683		case VLNK:
1684			va.va_mode |= S_IFLNK;
1685			break;
1686		case VSOCK:
1687			va.va_mode |= S_IFSOCK;
1688			break;
1689		case VFIFO:
1690			va.va_mode |= S_IFIFO;
1691			break;
1692		default:
1693			error = EBADF;
1694			goto out;
1695		}
1696	}
1697
1698	/*
1699	 * Pack results into the destination buffer.
1700	 */
1701	ab.fixedcursor = ab.base + sizeof(uint32_t);
1702	if (return_valid) {
1703		ab.fixedcursor += sizeof (attribute_set_t);
1704		bzero(&ab.actual, sizeof (ab.actual));
1705	}
1706	ab.varcursor = ab.base + fixedsize;
1707	ab.needed = ab.allocated;
1708
1709	/* common attributes **************************************************/
1710	if (al.commonattr & ATTR_CMN_NAME) {
1711		attrlist_pack_string(&ab, cnp, cnl);
1712		ab.actual.commonattr |= ATTR_CMN_NAME;
1713	}
1714	if ((al.commonattr & ATTR_CMN_ERROR) &&
1715	    (!return_valid || pack_invalid)) {
1716		ATTR_PACK4(ab, 0);
1717		ab.actual.commonattr |= ATTR_CMN_ERROR;
1718	}
1719	if (al.commonattr & ATTR_CMN_DEVID) {
1720		ATTR_PACK4(ab, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
1721		ab.actual.commonattr |= ATTR_CMN_DEVID;
1722	}
1723	if (al.commonattr & ATTR_CMN_FSID) {
1724		ATTR_PACK8(ab, vp->v_mount->mnt_vfsstat.f_fsid);
1725		ab.actual.commonattr |= ATTR_CMN_FSID;
1726	}
1727	if (al.commonattr & ATTR_CMN_OBJTYPE) {
1728		ATTR_PACK4(ab, vtype);
1729		ab.actual.commonattr |= ATTR_CMN_OBJTYPE;
1730	}
1731	if (al.commonattr & ATTR_CMN_OBJTAG) {
1732		ATTR_PACK4(ab, vp->v_tag);
1733		ab.actual.commonattr |= ATTR_CMN_OBJTAG;
1734	}
1735	if (al.commonattr & ATTR_CMN_OBJID) {
1736		fsobj_id_t f;
1737		/*
1738		 * Carbon can't deal with us reporting the target ID
1739		 * for links.  So we ask the filesystem to give us the
1740		 * source ID as well, and if it gives us one, we use
1741		 * it instead.
1742		 */
1743		if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1744			f.fid_objno = va.va_linkid;
1745		} else {
1746			f.fid_objno = va.va_fileid;
1747		}
1748		f.fid_generation = 0;
1749		ATTR_PACK8(ab, f);
1750		ab.actual.commonattr |= ATTR_CMN_OBJID;
1751	}
1752	if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
1753		fsobj_id_t f;
1754		/*
1755		 * Carbon can't deal with us reporting the target ID
1756		 * for links.  So we ask the filesystem to give us the
1757		 * source ID as well, and if it gives us one, we use
1758		 * it instead.
1759		 */
1760		if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1761			f.fid_objno = va.va_linkid;
1762		} else {
1763			f.fid_objno = va.va_fileid;
1764		}
1765		f.fid_generation = 0;
1766		ATTR_PACK8(ab, f);
1767		ab.actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
1768	}
1769	if (al.commonattr & ATTR_CMN_PAROBJID) {
1770		fsobj_id_t f;
1771
1772		f.fid_objno = va.va_parentid;  /* could be lossy here! */
1773		f.fid_generation = 0;
1774		ATTR_PACK8(ab, f);
1775		ab.actual.commonattr |= ATTR_CMN_PAROBJID;
1776	}
1777	if (al.commonattr & ATTR_CMN_SCRIPT) {
1778 		if (VATTR_IS_SUPPORTED(&va, va_encoding)) {
1779			ATTR_PACK4(ab, va.va_encoding);
1780			ab.actual.commonattr |= ATTR_CMN_SCRIPT;
1781		} else if (!return_valid || pack_invalid) {
1782			ATTR_PACK4(ab, 0x7e);
1783		}
1784	}
1785	if (al.commonattr & ATTR_CMN_CRTIME) {
1786		ATTR_PACK_TIME(ab, va.va_create_time, proc_is64);
1787		ab.actual.commonattr |= ATTR_CMN_CRTIME;
1788	}
1789	if (al.commonattr & ATTR_CMN_MODTIME) {
1790		ATTR_PACK_TIME(ab, va.va_modify_time, proc_is64);
1791		ab.actual.commonattr |= ATTR_CMN_MODTIME;
1792	}
1793	if (al.commonattr & ATTR_CMN_CHGTIME) {
1794		ATTR_PACK_TIME(ab, va.va_change_time, proc_is64);
1795		ab.actual.commonattr |= ATTR_CMN_CHGTIME;
1796	}
1797	if (al.commonattr & ATTR_CMN_ACCTIME) {
1798		ATTR_PACK_TIME(ab, va.va_access_time, proc_is64);
1799		ab.actual.commonattr |= ATTR_CMN_ACCTIME;
1800	}
1801	if (al.commonattr & ATTR_CMN_BKUPTIME) {
1802		ATTR_PACK_TIME(ab, va.va_backup_time, proc_is64);
1803		ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
1804	}
1805	/*
1806	 * They are requesting user access, we should obtain this before getting
1807	 * the finder info. For some network file systems this is a performance
1808	 * improvement.
1809	 */
1810	if (al.commonattr & ATTR_CMN_USERACCESS) {	/* this is expensive */
1811		if (vtype == VDIR) {
1812			if (vnode_authorize(vp, NULL,
1813								KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
1814				perms |= W_OK;
1815			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
1816				perms |= R_OK;
1817			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
1818				perms |= X_OK;
1819		} else {
1820			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
1821				perms |= W_OK;
1822			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
1823				perms |= R_OK;
1824			if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
1825				perms |= X_OK;
1826		}
1827	}
1828
1829	if (al.commonattr & ATTR_CMN_FNDRINFO) {
1830		uio_t	auio;
1831		size_t	fisize = 32;
1832		char	uio_buf[UIO_SIZEOF(1)];
1833
1834		if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
1835		     uio_buf, sizeof(uio_buf))) == NULL) {
1836			error = ENOMEM;
1837			goto out;
1838		}
1839		uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
1840		error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio,
1841				    &fisize, XATTR_NOSECURITY, ctx);
1842		uio_free(auio);
1843		/*
1844		 * Default to zeros if its not available,
1845		 * unless ATTR_CMN_RETURNED_ATTRS was requested.
1846		 */
1847		if (error &&
1848		    (!return_valid || pack_invalid) &&
1849		    ((error == ENOATTR) || (error == ENOENT) ||
1850		     (error == ENOTSUP) || (error == EPERM))) {
1851			VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1852			bzero(ab.fixedcursor, 32);
1853			error = 0;
1854		}
1855		if (error == 0) {
1856			ab.fixedcursor += 32;
1857			ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
1858		} else if (!return_valid) {
1859			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1860			goto out;
1861		}
1862	}
1863	if (al.commonattr & ATTR_CMN_OWNERID) {
1864		ATTR_PACK4(ab, va.va_uid);
1865		ab.actual.commonattr |= ATTR_CMN_OWNERID;
1866	}
1867	if (al.commonattr & ATTR_CMN_GRPID) {
1868		ATTR_PACK4(ab, va.va_gid);
1869		ab.actual.commonattr |= ATTR_CMN_GRPID;
1870	}
1871	if (al.commonattr & ATTR_CMN_ACCESSMASK) {
1872		ATTR_PACK4(ab, va.va_mode);
1873		ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
1874	}
1875	if (al.commonattr & ATTR_CMN_FLAGS) {
1876		ATTR_PACK4(ab, va.va_flags);
1877		ab.actual.commonattr |= ATTR_CMN_FLAGS;
1878	}
1879	if (attr_extended) {
1880		if (al.commonattr & ATTR_CMN_GEN_COUNT) {
1881			if (VATTR_IS_SUPPORTED(&va, va_gen)) {
1882				ATTR_PACK4(ab, va.va_gen);
1883				ab.actual.commonattr |= ATTR_CMN_GEN_COUNT;
1884			} else if (!return_valid || pack_invalid) {
1885				ATTR_PACK4(ab, 0);
1886			}
1887		}
1888
1889		if (al.commonattr & ATTR_CMN_DOCUMENT_ID) {
1890			if (VATTR_IS_SUPPORTED(&va, va_document_id)) {
1891				ATTR_PACK4(ab, va.va_document_id);
1892				ab.actual.commonattr |= ATTR_CMN_DOCUMENT_ID;
1893			} else if (!return_valid || pack_invalid) {
1894				ATTR_PACK4(ab, 0);
1895			}
1896		}
1897	}
1898	/* We already obtain the user access, so just fill in the buffer here */
1899	if (al.commonattr & ATTR_CMN_USERACCESS) {
1900#if CONFIG_MACF
1901		/*
1902		 * Rather than MAC preceding DAC, in this case we want
1903		 * the smallest set of permissions granted by both MAC & DAC
1904		 * checks.  We won't add back any permissions.
1905		 */
1906		if (perms & W_OK)
1907			if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
1908				perms &= ~W_OK;
1909		if (perms & R_OK)
1910			if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
1911				perms &= ~R_OK;
1912		if (perms & X_OK)
1913			if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
1914				perms &= ~X_OK;
1915#endif /* MAC */
1916		VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
1917		ATTR_PACK4(ab, perms);
1918		ab.actual.commonattr |= ATTR_CMN_USERACCESS;
1919	}
1920	if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1921		if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) {
1922			struct kauth_filesec fsec;
1923			/*
1924			 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1925			 */
1926			fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
1927			fsec.fsec_owner = kauth_null_guid;
1928			fsec.fsec_group = kauth_null_guid;
1929			attrlist_pack_variable2(&ab, &fsec, __offsetof(struct kauth_filesec, fsec_acl), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
1930			ab.actual.commonattr |= ATTR_CMN_EXTENDED_SECURITY;
1931		} else if (!return_valid || pack_invalid) {
1932			attrlist_pack_variable(&ab, NULL, 0);
1933		}
1934	}
1935  	if (al.commonattr & ATTR_CMN_UUID) {
1936 		if (VATTR_IS_SUPPORTED(&va, va_uuuid)) {
1937			ATTR_PACK(&ab, va.va_uuuid);
1938			ab.actual.commonattr |= ATTR_CMN_UUID;
1939		} else if (!return_valid || pack_invalid) {
1940			ATTR_PACK(&ab, kauth_null_guid);
1941		}
1942	}
1943	if (al.commonattr & ATTR_CMN_GRPUUID) {
1944		if (VATTR_IS_SUPPORTED(&va, va_guuid)) {
1945			ATTR_PACK(&ab, va.va_guuid);
1946			ab.actual.commonattr |= ATTR_CMN_GRPUUID;
1947		} else if (!return_valid || pack_invalid) {
1948			ATTR_PACK(&ab, kauth_null_guid);
1949		}
1950	}
1951	if (al.commonattr & ATTR_CMN_FILEID) {
1952		ATTR_PACK8(ab, va.va_fileid);
1953		ab.actual.commonattr |= ATTR_CMN_FILEID;
1954	}
1955	if (al.commonattr & ATTR_CMN_PARENTID) {
1956		ATTR_PACK8(ab, va.va_parentid);
1957		ab.actual.commonattr |= ATTR_CMN_PARENTID;
1958	}
1959
1960	if (al.commonattr & ATTR_CMN_FULLPATH) {
1961		attrlist_pack_string (&ab, fullpathptr, fullpathlen);
1962		ab.actual.commonattr |= ATTR_CMN_FULLPATH;
1963	}
1964
1965    if (al.commonattr & ATTR_CMN_ADDEDTIME) {
1966		ATTR_PACK_TIME(ab, va.va_addedtime, proc_is64);
1967		ab.actual.commonattr |= ATTR_CMN_ADDEDTIME;
1968	}
1969
1970	/* directory attributes *********************************************/
1971	if (al.dirattr && (vtype == VDIR)) {
1972		if (al.dirattr & ATTR_DIR_LINKCOUNT) {  /* full count of entries */
1973			ATTR_PACK4(ab, (uint32_t)va.va_dirlinkcount);
1974			ab.actual.dirattr |= ATTR_DIR_LINKCOUNT;
1975		}
1976		if (al.dirattr & ATTR_DIR_ENTRYCOUNT) {
1977			ATTR_PACK4(ab, (uint32_t)va.va_nchildren);
1978			ab.actual.dirattr |= ATTR_DIR_ENTRYCOUNT;
1979		}
1980		if (al.dirattr & ATTR_DIR_MOUNTSTATUS) {
1981			uint32_t mntstat;
1982
1983			mntstat = (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0;
1984#if CONFIG_TRIGGERS
1985			/*
1986			 * Report back on active vnode triggers
1987			 * that can directly trigger a mount
1988			 */
1989			if (vp->v_resolve &&
1990			    !(vp->v_resolve->vr_flags & VNT_NO_DIRECT_MOUNT)) {
1991				mntstat |= DIR_MNTSTATUS_TRIGGER;
1992			}
1993#endif
1994			ATTR_PACK4(ab, mntstat);
1995			ab.actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
1996		}
1997	}
1998
1999	/* file attributes **************************************************/
2000	if (al.fileattr && (vtype != VDIR)) {
2001
2002		size_t	rsize = 0;
2003		uint64_t rlength = 0;
2004		uint64_t ralloc = 0;
2005		/*
2006		 * Pre-fetch the rsrc attributes now so we only get them once.
2007		 * Fetch the resource fork size/allocation via xattr interface
2008		 */
2009		if (al.fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE | ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
2010			if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, ctx)) != 0) {
2011				if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)|| (error == EACCES)) {
2012					rsize = 0;
2013					error = 0;
2014				} else {
2015					goto out;
2016				}
2017			}
2018			rlength = rsize;
2019
2020			if (al.fileattr & (ATTR_FILE_RSRCALLOCSIZE | ATTR_FILE_ALLOCSIZE)) {
2021				uint32_t  blksize = vp->v_mount->mnt_vfsstat.f_bsize;
2022				if (blksize == 0) {
2023					blksize = 512;
2024				}
2025				ralloc = roundup(rsize, blksize);
2026			}
2027		}
2028
2029		if (al.fileattr & ATTR_FILE_LINKCOUNT) {
2030			ATTR_PACK4(ab, (uint32_t)va.va_nlink);
2031			ab.actual.fileattr |= ATTR_FILE_LINKCOUNT;
2032		}
2033		/*
2034		 * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes:
2035		 * We infer that if the filesystem does not support va_data_size or va_data_alloc
2036		 * it must not know about alternate forks.  So when we need to gather
2037		 * the total size or total alloc, it's OK to substitute the total size for
2038		 * the data size below.  This is because it is likely a flat filesystem and we must
2039		 * be using AD files to store the rsrc fork and EAs.
2040		 *
2041		 * Additionally, note that getattrlist is barred from being called on
2042		 * resource fork paths. (Search for CN_ALLOWRSRCFORK).  So if the filesystem does
2043		 * support va_data_size, it is guaranteed to represent the data fork's size.  This
2044		 * is an important distinction to make because when we call vnode_getattr on
2045		 * an HFS resource fork vnode, to get the size, it will vend out the resource
2046		 * fork's size (it only gets the size of the passed-in vnode).
2047		 */
2048		if (al.fileattr & ATTR_FILE_TOTALSIZE) {
2049			uint64_t totalsize = rlength;
2050
2051			if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
2052				totalsize += va.va_data_size;
2053			} else {
2054				totalsize += va.va_total_size;
2055			}
2056
2057			ATTR_PACK8(ab, totalsize);
2058			ab.actual.fileattr |= ATTR_FILE_TOTALSIZE;
2059		}
2060		if (al.fileattr & ATTR_FILE_ALLOCSIZE) {
2061			uint64_t totalalloc = ralloc;
2062
2063			/*
2064			 * If data_alloc is supported, then it must represent the
2065			 * data fork size.
2066			 */
2067			if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
2068				totalalloc += va.va_data_alloc;
2069			}
2070			else {
2071				totalalloc += va.va_total_alloc;
2072			}
2073
2074			ATTR_PACK8(ab, totalalloc);
2075			ab.actual.fileattr |= ATTR_FILE_ALLOCSIZE;
2076		}
2077		if (al.fileattr & ATTR_FILE_IOBLOCKSIZE) {
2078			ATTR_PACK4(ab, va.va_iosize);
2079			ab.actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
2080		}
2081		if (al.fileattr & ATTR_FILE_CLUMPSIZE) {
2082			if (!return_valid || pack_invalid) {
2083				ATTR_PACK4(ab, 0);     /* this value is deprecated */
2084				ab.actual.fileattr |= ATTR_FILE_CLUMPSIZE;
2085			}
2086		}
2087		if (al.fileattr & ATTR_FILE_DEVTYPE) {
2088			uint32_t dev;
2089
2090			if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
2091				if (vp->v_specinfo != NULL)
2092					dev = vp->v_specinfo->si_rdev;
2093				else
2094					dev = va.va_rdev;
2095			} else {
2096				dev = 0;
2097			}
2098			ATTR_PACK4(ab, dev);
2099			ab.actual.fileattr |= ATTR_FILE_DEVTYPE;
2100		}
2101
2102		/*
2103		 * If the filesystem does not support datalength
2104		 * or dataallocsize, then we infer that totalsize and
2105		 * totalalloc are substitutes.
2106		 */
2107		if (al.fileattr & ATTR_FILE_DATALENGTH) {
2108			if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
2109				ATTR_PACK8(ab, va.va_data_size);
2110			} else {
2111				ATTR_PACK8(ab, va.va_total_size);
2112			}
2113			ab.actual.fileattr |= ATTR_FILE_DATALENGTH;
2114		}
2115		if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
2116			if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
2117				ATTR_PACK8(ab, va.va_data_alloc);
2118			} else {
2119				ATTR_PACK8(ab, va.va_total_alloc);
2120			}
2121			ab.actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
2122		}
2123		/* already got the resource fork size/allocation above */
2124		if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
2125			ATTR_PACK8(ab, rlength);
2126			ab.actual.fileattr |= ATTR_FILE_RSRCLENGTH;
2127		}
2128		if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
2129			ATTR_PACK8(ab, ralloc);
2130			ab.actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
2131		}
2132	}
2133
2134	/* diagnostic */
2135	if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
2136		panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
2137		    fixedsize, (long) (ab.fixedcursor - ab.base), al.commonattr, al.volattr);
2138	if (!return_valid && ab.varcursor != (ab.base + ab.needed))
2139		panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
2140
2141	/*
2142	 * In the compatible case, we report the smaller of the required and returned sizes.
2143	 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
2144	 * of the result buffer, even if we copied less out.  The caller knows how big a buffer
2145	 * they gave us, so they can always check for truncation themselves.
2146	 */
2147	*(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
2148
2149	/* Return attribute set output if requested. */
2150	if (return_valid) {
2151		ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
2152		if (pack_invalid) {
2153			/* Only report the attributes that are valid */
2154			ab.actual.commonattr &= ab.valid.commonattr;
2155			ab.actual.dirattr &= ab.valid.dirattr;
2156			ab.actual.fileattr &= ab.valid.fileattr;
2157		}
2158		bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
2159	}
2160
2161	/* Only actually copyout as much out as the user buffer can hold */
2162	error = copyout(ab.base, uap->attributeBuffer, imin(uap->bufferSize, ab.allocated));
2163
2164out:
2165	if (va.va_name)
2166		kfree(va.va_name, MAXPATHLEN);
2167	if (fullpathptr)
2168		kfree(fullpathptr, MAXPATHLEN);
2169	if (vname)
2170		vnode_putname(vname);
2171	if (ab.base != NULL)
2172		FREE(ab.base, M_TEMP);
2173	if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
2174		kauth_acl_free(va.va_acl);
2175
2176	VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
2177	return(error);
2178}
2179
2180int
2181fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
2182{
2183	struct vfs_context *ctx;
2184	vnode_t		vp = NULL;
2185	int		error;
2186	struct getattrlist_args	ap;
2187
2188	ctx = vfs_context_current();
2189	error = 0;
2190
2191	if ((error = file_vnode(uap->fd, &vp)) != 0)
2192		return (error);
2193
2194	if ((error = vnode_getwithref(vp)) != 0) {
2195		file_drop(uap->fd);
2196		return(error);
2197	}
2198
2199	ap.path = 0;
2200	ap.alist = uap->alist;
2201	ap.attributeBuffer = uap->attributeBuffer;
2202	ap.bufferSize = uap->bufferSize;
2203	ap.options = uap->options;
2204
2205	/* Default to using the vnode's name. */
2206	error = getattrlist_internal(vp, &ap, NULL, p, ctx);
2207
2208	file_drop(uap->fd);
2209	if (vp)
2210		vnode_put(vp);
2211
2212	return error;
2213}
2214
2215int
2216getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval)
2217{
2218	struct vfs_context *ctx;
2219	struct nameidata nd;
2220	vnode_t		vp = NULL;
2221	u_long		nameiflags;
2222	int		error;
2223
2224	ctx = vfs_context_current();
2225	error = 0;
2226
2227	/*
2228	 * Look up the file.
2229	 */
2230	nameiflags = NOTRIGGER | AUDITVNPATH1;
2231	if (!(uap->options & FSOPT_NOFOLLOW))
2232		nameiflags |= FOLLOW;
2233	NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
2234
2235	if ((error = namei(&nd)) != 0) {
2236		/* vp is still uninitialized */
2237		return error;
2238	}
2239
2240	vp = nd.ni_vp;
2241	/* Pass along our componentname to getattrlist_internal */
2242	error = getattrlist_internal(vp, uap, &(nd.ni_cnd), p, ctx);
2243
2244	/* Retain the namei reference until the getattrlist completes. */
2245	nameidone(&nd);
2246	if (vp)
2247		vnode_put(vp);
2248
2249	return error;
2250}
2251
2252static int
2253attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
2254{
2255	/* make sure we have enough source data */
2256	if ((*cursor) + size > end)
2257		return(EINVAL);
2258
2259	bcopy(*cursor, buf, size);
2260	*cursor += size;
2261	return(0);
2262}
2263
2264#define ATTR_UNPACK(v)		do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
2265#define ATTR_UNPACK_CAST(t, v)	do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
2266#define ATTR_UNPACK_TIME(v, is64)				\
2267	do {							\
2268		if (is64) {					\
2269			struct user64_timespec us;		\
2270			ATTR_UNPACK(us);			\
2271			v.tv_sec = us.tv_sec;			\
2272			v.tv_nsec = us.tv_nsec;			\
2273		} else {					\
2274			struct user32_timespec us;		\
2275			ATTR_UNPACK(us);			\
2276			v.tv_sec = us.tv_sec;			\
2277			v.tv_nsec = us.tv_nsec;			\
2278		}						\
2279	} while(0)
2280
2281
2282/*
2283 * Write attributes.
2284 */
2285static int
2286setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_context_t ctx)
2287{
2288	struct attrlist al;
2289	struct vnode_attr va;
2290	struct attrreference ar;
2291	kauth_action_t	action;
2292	char		*user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
2293	int		proc_is64, error;
2294	uint32_t	nace;
2295	kauth_filesec_t rfsec;
2296
2297	user_buf = NULL;
2298	fndrinfo = NULL;
2299	volname = NULL;
2300	error = 0;
2301	proc_is64 = proc_is64bit(p);
2302	VATTR_INIT(&va);
2303
2304	/*
2305	 * Fetch the attribute set and validate.
2306	 */
2307	if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
2308		goto out;
2309	if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
2310		error = EINVAL;
2311		goto out;
2312	}
2313
2314	VFS_DEBUG(ctx, vp, "%p  ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
2315	    vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
2316	    (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
2317
2318	if (al.volattr) {
2319		if ((al.volattr & ~ATTR_VOL_SETMASK) ||
2320		    (al.commonattr & ~ATTR_CMN_VOLSETMASK) ||
2321		    al.fileattr ||
2322		    al.forkattr) {
2323			error = EINVAL;
2324			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
2325			goto out;
2326		}
2327	} else {
2328		if ((al.commonattr & ~ATTR_CMN_SETMASK) ||
2329		    (al.fileattr & ~ATTR_FILE_SETMASK) ||
2330		    (al.dirattr & ~ATTR_DIR_SETMASK) ||
2331		    (al.forkattr & ~ATTR_FORK_SETMASK)) {
2332			error = EINVAL;
2333			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
2334			goto out;
2335		}
2336	}
2337
2338	/*
2339	 * If the caller's bitmaps indicate that there are no attributes to set,
2340	 * then exit early.  In particular, we want to avoid the MALLOC below
2341	 * since the caller's bufferSize could be zero, and MALLOC of zero bytes
2342	 * returns a NULL pointer, which would cause setattrlist to return ENOMEM.
2343	 */
2344	if (al.commonattr == 0 &&
2345		(al.volattr & ~ATTR_VOL_INFO) == 0 &&
2346		al.dirattr == 0 &&
2347		al.fileattr == 0 &&
2348		al.forkattr == 0) {
2349		error = 0;
2350		goto out;
2351	}
2352
2353	/*
2354	 * Make the naive assumption that the caller has supplied a reasonable buffer
2355	 * size.  We could be more careful by pulling in the fixed-size region, checking
2356	 * the attrref structures, then pulling in the variable section.
2357	 * We need to reconsider this for handling large ACLs, as they should probably be
2358	 * brought directly into a buffer.  Multiple copyins will make this slower though.
2359	 *
2360	 * We could also map the user buffer if it is larger than some sensible mimimum.
2361	 */
2362	if (uap->bufferSize > ATTR_MAX_BUFFER) {
2363		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large", uap->bufferSize);
2364		error = ENOMEM;
2365		goto out;
2366	}
2367	MALLOC(user_buf, char *, uap->bufferSize, M_TEMP, M_WAITOK);
2368	if (user_buf == NULL) {
2369		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
2370		error = ENOMEM;
2371		goto out;
2372	}
2373	if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) {
2374		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed");
2375		goto out;
2376	}
2377	VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
2378
2379#if CONFIG_MACF
2380	error = mac_vnode_check_setattrlist(ctx, vp, &al);
2381	if (error)
2382		goto out;
2383#endif /* MAC */
2384
2385	/*
2386	 * Unpack the argument buffer.
2387	 */
2388	cursor = user_buf;
2389	bufend = cursor + uap->bufferSize;
2390
2391	/* common */
2392	if (al.commonattr & ATTR_CMN_SCRIPT) {
2393		ATTR_UNPACK(va.va_encoding);
2394		VATTR_SET_ACTIVE(&va, va_encoding);
2395	}
2396	if (al.commonattr & ATTR_CMN_CRTIME) {
2397		ATTR_UNPACK_TIME(va.va_create_time, proc_is64);
2398		VATTR_SET_ACTIVE(&va, va_create_time);
2399	}
2400	if (al.commonattr & ATTR_CMN_MODTIME) {
2401		ATTR_UNPACK_TIME(va.va_modify_time, proc_is64);
2402		VATTR_SET_ACTIVE(&va, va_modify_time);
2403	}
2404	if (al.commonattr & ATTR_CMN_CHGTIME) {
2405		ATTR_UNPACK_TIME(va.va_change_time, proc_is64);
2406		VATTR_SET_ACTIVE(&va, va_change_time);
2407	}
2408	if (al.commonattr & ATTR_CMN_ACCTIME) {
2409		ATTR_UNPACK_TIME(va.va_access_time, proc_is64);
2410		VATTR_SET_ACTIVE(&va, va_access_time);
2411	}
2412	if (al.commonattr & ATTR_CMN_BKUPTIME) {
2413		ATTR_UNPACK_TIME(va.va_backup_time, proc_is64);
2414		VATTR_SET_ACTIVE(&va, va_backup_time);
2415	}
2416	if (al.commonattr & ATTR_CMN_FNDRINFO) {
2417		if ((cursor + 32) > bufend) {
2418			error = EINVAL;
2419			VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO");
2420			goto out;
2421		}
2422		fndrinfo = cursor;
2423		cursor += 32;
2424	}
2425	if (al.commonattr & ATTR_CMN_OWNERID) {
2426		ATTR_UNPACK(va.va_uid);
2427		VATTR_SET_ACTIVE(&va, va_uid);
2428	}
2429	if (al.commonattr & ATTR_CMN_GRPID) {
2430		ATTR_UNPACK(va.va_gid);
2431		VATTR_SET_ACTIVE(&va, va_gid);
2432	}
2433	if (al.commonattr & ATTR_CMN_ACCESSMASK) {
2434		ATTR_UNPACK_CAST(uint32_t, va.va_mode);
2435		VATTR_SET_ACTIVE(&va, va_mode);
2436	}
2437	if (al.commonattr & ATTR_CMN_FLAGS) {
2438		ATTR_UNPACK(va.va_flags);
2439		VATTR_SET_ACTIVE(&va, va_flags);
2440	}
2441	if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
2442
2443		/*
2444		 * We are (for now) passed a kauth_filesec_t, but all we want from
2445		 * it is the ACL.
2446		 */
2447		cp = cursor;
2448		ATTR_UNPACK(ar);
2449		if (ar.attr_dataoffset < 0) {
2450			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied", ar.attr_dataoffset);
2451			error = EINVAL;
2452			goto out;
2453		}
2454
2455		cp += ar.attr_dataoffset;
2456		rfsec = (kauth_filesec_t)cp;
2457		if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) ||			/* no space for acl */
2458		    (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) ||       /* bad magic */
2459		    (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
2460		    ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) {	/* ACEs overrun buffer */
2461			error = EINVAL;
2462			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
2463			goto out;
2464		}
2465		nace = rfsec->fsec_entrycount;
2466		if (nace == KAUTH_FILESEC_NOACL)
2467			nace = 0;
2468		if (nace > KAUTH_ACL_MAX_ENTRIES) {			/* ACL size invalid */
2469			error = EINVAL;
2470			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
2471			goto out;
2472		}
2473		nace = rfsec->fsec_acl.acl_entrycount;
2474		if (nace == KAUTH_FILESEC_NOACL) {
2475			/* deleting ACL */
2476			VATTR_SET(&va, va_acl, NULL);
2477		} else {
2478
2479			if (nace > KAUTH_ACL_MAX_ENTRIES) {			/* ACL size invalid */
2480				error = EINVAL;
2481				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
2482				goto out;
2483			}
2484			VATTR_SET(&va, va_acl, &rfsec->fsec_acl);
2485		}
2486	}
2487	if (al.commonattr & ATTR_CMN_UUID) {
2488		ATTR_UNPACK(va.va_uuuid);
2489		VATTR_SET_ACTIVE(&va, va_uuuid);
2490	}
2491	if (al.commonattr & ATTR_CMN_GRPUUID) {
2492		ATTR_UNPACK(va.va_guuid);
2493		VATTR_SET_ACTIVE(&va, va_guuid);
2494	}
2495
2496	/* volume */
2497	if (al.volattr & ATTR_VOL_INFO) {
2498		if (al.volattr & ATTR_VOL_NAME) {
2499			volname = cursor;
2500			ATTR_UNPACK(ar);
2501			/* attr_length cannot be 0! */
2502			if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0)) {
2503				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) ", ar.attr_dataoffset);
2504				error = EINVAL;
2505				goto out;
2506			}
2507
2508			volname += ar.attr_dataoffset;
2509			if ((volname + ar.attr_length) > bufend) {
2510				error = EINVAL;
2511				VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
2512				goto out;
2513			}
2514			/* guarantee NUL termination */
2515			volname[ar.attr_length - 1] = 0;
2516		}
2517	}
2518
2519	/* file */
2520	if (al.fileattr & ATTR_FILE_DEVTYPE) {
2521		/* XXX does it actually make any sense to change this? */
2522		error = EINVAL;
2523		VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented");
2524		goto out;
2525	}
2526
2527	/*
2528	 * Validate and authorize.
2529	 */
2530	action = 0;
2531	if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, ctx)) != 0)) {
2532		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
2533		goto out;
2534	}
2535	/*
2536	 * We can auth file Finder Info here.  HFS volume FinderInfo is really boot data,
2537	 * and will be auth'ed by the FS.
2538	 */
2539	if (fndrinfo != NULL) {
2540		if (al.volattr & ATTR_VOL_INFO) {
2541			if (vp->v_tag != VT_HFS) {
2542				error = EINVAL;
2543				goto out;
2544			}
2545		} else {
2546			action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
2547		}
2548	}
2549
2550	if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) {
2551		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
2552		goto out;
2553	}
2554
2555	/*
2556	 * When we're setting both the access mask and the finder info, then
2557	 * check if were about to remove write access for the owner.  Since
2558	 * vnode_setattr and vn_setxattr invoke two separate vnops, we need
2559	 * to consider their ordering.
2560	 *
2561	 * If were about to remove write access for the owner we'll set the
2562	 * Finder Info here before vnode_setattr.  Otherwise we'll set it
2563	 * after vnode_setattr since it may be adding owner write access.
2564	 */
2565	if ((fndrinfo != NULL) && !(al.volattr & ATTR_VOL_INFO) &&
2566	    (al.commonattr & ATTR_CMN_ACCESSMASK) && !(va.va_mode & S_IWUSR)) {
2567		if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
2568			goto out;
2569		}
2570		fndrinfo = NULL;  /* it was set here so skip setting below */
2571	}
2572
2573	/*
2574	 * Write the attributes if we have any.
2575	 */
2576	if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, ctx)) != 0)) {
2577		VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
2578		goto out;
2579	}
2580
2581	/*
2582	 * Write the Finder Info if we have any.
2583	 */
2584	if (fndrinfo != NULL) {
2585		if (al.volattr & ATTR_VOL_INFO) {
2586			if (vp->v_tag == VT_HFS) {
2587				error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx);
2588				if (error != 0)
2589					goto out;
2590			} else {
2591				/* XXX should never get here */
2592			}
2593		} else if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
2594			goto out;
2595		}
2596	}
2597
2598	/*
2599	 * Set the volume name, if we have one
2600	 */
2601	if (volname != NULL)
2602	{
2603		struct vfs_attr vs;
2604
2605		VFSATTR_INIT(&vs);
2606
2607		vs.f_vol_name = volname;	/* References the setattrlist buffer directly */
2608		VFSATTR_WANTED(&vs, f_vol_name);
2609
2610#if CONFIG_MACF
2611		error = mac_mount_check_setattr(ctx, vp->v_mount, &vs);
2612		if (error != 0)
2613			goto out;
2614#endif
2615
2616		if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
2617			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
2618			goto out;
2619		}
2620
2621		if (!VFSATTR_ALL_SUPPORTED(&vs)) {
2622			error = EINVAL;
2623			VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
2624			goto out;
2625		}
2626	}
2627
2628	/* all done and successful */
2629
2630out:
2631	if (user_buf != NULL)
2632		FREE(user_buf, M_TEMP);
2633	VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
2634	return(error);
2635}
2636
2637int
2638setattrlist(proc_t p, struct setattrlist_args *uap, __unused int32_t *retval)
2639{
2640	struct vfs_context *ctx;
2641	struct nameidata nd;
2642	vnode_t		vp = NULL;
2643	u_long		nameiflags;
2644	int error = 0;
2645
2646	ctx = vfs_context_current();
2647
2648	/*
2649	 * Look up the file.
2650	 */
2651	nameiflags = AUDITVNPATH1;
2652	if ((uap->options & FSOPT_NOFOLLOW) == 0)
2653		nameiflags |= FOLLOW;
2654	NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
2655	if ((error = namei(&nd)) != 0)
2656		goto out;
2657	vp = nd.ni_vp;
2658	nameidone(&nd);
2659
2660	error = setattrlist_internal(vp, uap, p, ctx);
2661out:
2662	if (vp != NULL)
2663		vnode_put(vp);
2664	return error;
2665}
2666
2667int
2668fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
2669{
2670	struct vfs_context *ctx;
2671	vnode_t		vp = NULL;
2672	int		error;
2673	struct setattrlist_args ap;
2674
2675	ctx = vfs_context_current();
2676
2677	if ((error = file_vnode(uap->fd, &vp)) != 0)
2678		return (error);
2679
2680	if ((error = vnode_getwithref(vp)) != 0) {
2681		file_drop(uap->fd);
2682		return(error);
2683	}
2684
2685	ap.path = 0;
2686	ap.alist = uap->alist;
2687	ap.attributeBuffer = uap->attributeBuffer;
2688	ap.bufferSize = uap->bufferSize;
2689	ap.options = uap->options;
2690
2691	error = setattrlist_internal(vp, &ap, p, ctx);
2692	file_drop(uap->fd);
2693	if (vp != NULL)
2694		vnode_put(vp);
2695
2696	return error;
2697}
2698
2699