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