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