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