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