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