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