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