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