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