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