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