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