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