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