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