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