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