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