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