]> git.saurik.com Git - apple/xnu.git/blame - bsd/vfs/vfs_attrlist.c
xnu-2422.90.20.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
CommitLineData
91447636 1/*
39236c6e 2 * Copyright (c) 1995-2012 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>
40#include <sys/vnode_internal.h>
41#include <sys/mount_internal.h>
42#include <sys/proc_internal.h>
43#include <sys/kauth.h>
44#include <sys/uio_internal.h>
45#include <sys/malloc.h>
46#include <sys/attr.h>
47#include <sys/sysproto.h>
48#include <sys/xattr.h>
49#include <sys/fsevents.h>
50#include <kern/kalloc.h>
51#include <miscfs/specfs/specdev.h>
52#include <hfs/hfs.h>
53
2d21ac55
A
54#if CONFIG_MACF
55#include <security/mac_framework.h>
56#endif
57
91447636
A
58#define ATTR_TIME_SIZE -1
59
60/*
61 * Structure describing the state of an in-progress attrlist operation.
62 */
63struct _attrlist_buf {
64 char *base;
65 char *fixedcursor;
66 char *varcursor;
67 ssize_t allocated;
68 ssize_t needed;
b0d623f7
A
69 attribute_set_t actual;
70 attribute_set_t valid;
91447636
A
71};
72
73
74/*
39236c6e
A
75 * Attempt to pack a fixed width attribute of size (count) bytes from
76 * source to our attrlist buffer.
91447636
A
77 */
78static void
79attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
80{
39236c6e
A
81 /*
82 * Use ssize_t for pointer math purposes,
83 * since a ssize_t is a signed long
84 */
91447636
A
85 ssize_t fit;
86
39236c6e
A
87 /*
88 * Compute the amount of remaining space in the attrlist buffer
89 * based on how much we've used for fixed width fields vs. the
90 * start of the attributes.
91 *
92 * If we've still got room, then 'fit' will contain the amount of
93 * remaining space.
94 *
95 * Note that this math is safe because, in the event that the
96 * fixed-width cursor has moved beyond the end of the buffer,
97 * then, the second input into lmin() below will be negative, and
98 * we will fail the (fit > 0) check below.
99 */
100 fit = lmin(count, ab->allocated - (ab->fixedcursor - ab->base));
101 if (fit > 0) {
102 /* Copy in as much as we can */
91447636 103 bcopy(source, ab->fixedcursor, fit);
39236c6e 104 }
91447636 105
39236c6e 106 /* always move in increments of 4, even if we didn't pack an attribute. */
91447636
A
107 ab->fixedcursor += roundup(count, 4);
108}
39236c6e
A
109
110/*
111 * Attempt to pack one (or two) variable width attributes into the attrlist
112 * buffer. If we are trying to pack two variable width attributes, they are treated
113 * as a single variable-width attribute from the POV of the system call caller.
114 *
115 * Recall that a variable-width attribute has two components: the fixed-width
116 * attribute that tells the caller where to look, and the actual variable width data.
117 */
91447636 118static void
39236c6e
A
119attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count,
120 const void *ext, ssize_t extcount) {
121
122 /* Use ssize_t's for pointer math ease */
123 struct attrreference ar;
91447636
A
124 ssize_t fit;
125
39236c6e
A
126 /*
127 * Pack the fixed-width component to the variable object.
128 * Note that we may be able to pack the fixed width attref, but not
129 * the variable (if there's no room).
130 */
91447636
A
131 ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
132 ar.attr_length = count + extcount;
133 attrlist_pack_fixed(ab, &ar, sizeof(ar));
134
39236c6e
A
135 /*
136 * Use an lmin() to do a signed comparison. We use a signed comparison
137 * to detect the 'out of memory' conditions as described above in the
138 * fixed width check above.
139 *
140 * Then pack the first variable attribute as space allows. Note that we advance
141 * the variable cursor only if we we had some available space.
142 */
143 fit = lmin(count, ab->allocated - (ab->varcursor - ab->base));
91447636 144 if (fit > 0) {
39236c6e 145 if (source != NULL) {
91447636 146 bcopy(source, ab->varcursor, fit);
39236c6e 147 }
91447636
A
148 ab->varcursor += fit;
149 }
39236c6e
A
150
151 /* Compute the available space for the second attribute */
152 fit = lmin(extcount, ab->allocated - (ab->varcursor - ab->base));
91447636 153 if (fit > 0) {
39236c6e
A
154 /* Copy in data for the second attribute (if needed) if there is room */
155 if (ext != NULL) {
91447636 156 bcopy(ext, ab->varcursor, fit);
39236c6e 157 }
91447636
A
158 ab->varcursor += fit;
159 }
160 /* always move in increments of 4 */
161 ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4);
162}
39236c6e
A
163
164/*
165 * Packing a single variable-width attribute is the same as calling the two, but with
166 * an invalid 2nd attribute.
167 */
91447636
A
168static void
169attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
170{
171 attrlist_pack_variable2(ab, source, count, NULL, 0);
172}
39236c6e
A
173
174/*
175 * Attempt to pack a string. This is a special case of a variable width attribute.
176 *
177 * If "source" is NULL, then an empty string ("") will be packed. If "source" is
178 * not NULL, but "count" is zero, then "source" is assumed to be a NUL-terminated
179 * C-string. If "source" is not NULL and "count" is not zero, then only the first
180 * "count" bytes of "source" will be copied, and a NUL terminator will be added.
181 *
182 * If the attrlist buffer doesn't have enough room to hold the entire string (including
183 * NUL terminator), then copy as much as will fit. The attrlist buffer's "varcursor"
184 * will always be updated based on the entire length of the string (including NUL
185 * terminator); this means "varcursor" may end up pointing beyond the end of the
186 * allocated buffer space.
187 */
91447636
A
188static void
189attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count)
190{
39236c6e 191 struct attrreference ar;
91447636
A
192 ssize_t fit, space;
193
91447636
A
194 /*
195 * Supplied count is character count of string text, excluding trailing nul
196 * which we always supply here.
197 */
198 if (source == NULL) {
199 count = 0;
200 } else if (count == 0) {
201 count = strlen(source);
202 }
203
204 /*
39236c6e 205 * Construct the fixed-width attribute that refers to this string.
91447636
A
206 */
207 ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
208 ar.attr_length = count + 1;
209 attrlist_pack_fixed(ab, &ar, sizeof(ar));
39236c6e
A
210
211 /*
212 * Now compute how much available memory we have to copy the string text.
213 *
214 * space = the number of bytes available in the attribute buffer to hold the
215 * string's value.
216 *
217 * fit = the number of bytes to copy from the start of the string into the
218 * attribute buffer, NOT including the NUL terminator. If the attribute
219 * buffer is large enough, this will be the string's length; otherwise, it
220 * will be equal to "space".
221 */
91447636 222 space = ab->allocated - (ab->varcursor - ab->base);
39236c6e
A
223 fit = lmin(count, space);
224 if (space > 0) {
225 /*
226 * If there is space remaining, copy data in, and
227 * accommodate the trailing NUL terminator.
228 *
229 * NOTE: if "space" is too small to hold the string and its NUL
230 * terminator (space < fit + 1), then the string value in the attribute
231 * buffer will NOT be NUL terminated!
232 *
233 * NOTE 2: bcopy() will do nothing if the length ("fit") is zero.
234 * Therefore, we don't bother checking for that here.
235 */
91447636 236 bcopy(source, ab->varcursor, fit);
39236c6e
A
237 /* is there room for our trailing nul? */
238 if (space > fit) {
239 ab->varcursor[fit++] = '\0';
240 /* 'fit' now the number of bytes AFTER adding in the NUL */
241 }
242 }
243 /*
244 * always move in increments of 4 (including the trailing NUL)
245 */
246 ab->varcursor += roundup((count+1), 4);
91447636 247
91447636
A
248}
249
2d21ac55
A
250#define ATTR_PACK4(AB, V) \
251 do { \
252 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) { \
253 *(uint32_t *)AB.fixedcursor = V; \
254 AB.fixedcursor += 4; \
255 } \
256 } while (0)
257
258#define ATTR_PACK8(AB, V) \
259 do { \
260 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \
261 *(uint64_t *)AB.fixedcursor = *(uint64_t *)&V; \
262 AB.fixedcursor += 8; \
263 } \
264 } while (0)
265
91447636
A
266#define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
267#define ATTR_PACK_CAST(b, t, v) \
268 do { \
269 t _f = (t)v; \
270 ATTR_PACK(b, _f); \
271 } while (0)
272
273#define ATTR_PACK_TIME(b, v, is64) \
274 do { \
275 if (is64) { \
b0d623f7 276 struct user64_timespec us = {v.tv_sec, v.tv_nsec}; \
2d21ac55 277 ATTR_PACK(&b, us); \
91447636 278 } else { \
b0d623f7
A
279 struct user32_timespec us = {v.tv_sec, v.tv_nsec}; \
280 ATTR_PACK(&b, us); \
91447636
A
281 } \
282 } while(0)
283
284
285/*
286 * Table-driven setup for all valid common/volume attributes.
287 */
288struct getvolattrlist_attrtab {
289 attrgroup_t attr;
290 uint64_t bits;
291#define VFSATTR_BIT(b) (VFSATTR_ ## b)
292 ssize_t size;
293};
294static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = {
295 {ATTR_CMN_NAME, 0, sizeof(struct attrreference)},
296 {ATTR_CMN_DEVID, 0, sizeof(dev_t)},
297 {ATTR_CMN_FSID, 0, sizeof(fsid_t)},
298 {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t)},
299 {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t)},
300 {ATTR_CMN_OBJID, 0, sizeof(fsobj_id_t)},
301 {ATTR_CMN_OBJPERMANENTID, 0, sizeof(fsobj_id_t)},
302 {ATTR_CMN_PAROBJID, 0, sizeof(fsobj_id_t)},
303 {ATTR_CMN_SCRIPT, 0, sizeof(text_encoding_t)},
304 {ATTR_CMN_CRTIME, VFSATTR_BIT(f_create_time), ATTR_TIME_SIZE},
305 {ATTR_CMN_MODTIME, VFSATTR_BIT(f_modify_time), ATTR_TIME_SIZE},
306 {ATTR_CMN_CHGTIME, VFSATTR_BIT(f_modify_time), ATTR_TIME_SIZE},
307 {ATTR_CMN_ACCTIME, VFSATTR_BIT(f_access_time), ATTR_TIME_SIZE},
308 {ATTR_CMN_BKUPTIME, VFSATTR_BIT(f_backup_time), ATTR_TIME_SIZE},
309 {ATTR_CMN_FNDRINFO, 0, 32},
310 {ATTR_CMN_OWNERID, 0, sizeof(uid_t)},
311 {ATTR_CMN_GRPID, 0, sizeof(gid_t)},
312 {ATTR_CMN_ACCESSMASK, 0, sizeof(uint32_t)},
313 {ATTR_CMN_FLAGS, 0, sizeof(uint32_t)},
314 {ATTR_CMN_USERACCESS, 0, sizeof(uint32_t)},
b0d623f7
A
315 {ATTR_CMN_EXTENDED_SECURITY, 0, sizeof(struct attrreference)},
316 {ATTR_CMN_UUID, 0, sizeof(guid_t)},
317 {ATTR_CMN_GRPUUID, 0, sizeof(guid_t)},
318 {ATTR_CMN_FILEID, 0, sizeof(uint64_t)},
319 {ATTR_CMN_PARENTID, 0, sizeof(uint64_t)},
320 {ATTR_CMN_RETURNED_ATTRS, 0, sizeof(attribute_set_t)},
91447636
A
321 {0, 0, 0}
322};
b0d623f7
A
323#define ATTR_CMN_VOL_INVALID \
324 (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID | \
325 ATTR_CMN_FILEID | ATTR_CMN_PARENTID)
91447636
A
326
327static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
328 {ATTR_VOL_FSTYPE, 0, sizeof(uint32_t)},
329 {ATTR_VOL_SIGNATURE, VFSATTR_BIT(f_signature), sizeof(uint32_t)},
330 {ATTR_VOL_SIZE, VFSATTR_BIT(f_blocks), sizeof(off_t)},
331 {ATTR_VOL_SPACEFREE, VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
332 {ATTR_VOL_SPACEAVAIL, VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
333 {ATTR_VOL_MINALLOCATION, VFSATTR_BIT(f_bsize), sizeof(off_t)},
334 {ATTR_VOL_ALLOCATIONCLUMP, VFSATTR_BIT(f_bsize), sizeof(off_t)},
335 {ATTR_VOL_IOBLOCKSIZE, VFSATTR_BIT(f_iosize), sizeof(uint32_t)},
336 {ATTR_VOL_OBJCOUNT, VFSATTR_BIT(f_objcount), sizeof(uint32_t)},
337 {ATTR_VOL_FILECOUNT, VFSATTR_BIT(f_filecount), sizeof(uint32_t)},
338 {ATTR_VOL_DIRCOUNT, VFSATTR_BIT(f_dircount), sizeof(uint32_t)},
339 {ATTR_VOL_MAXOBJCOUNT, VFSATTR_BIT(f_maxobjcount), sizeof(uint32_t)},
340 {ATTR_VOL_MOUNTPOINT, 0, sizeof(struct attrreference)},
341 {ATTR_VOL_NAME, VFSATTR_BIT(f_vol_name), sizeof(struct attrreference)},
342 {ATTR_VOL_MOUNTFLAGS, 0, sizeof(uint32_t)},
343 {ATTR_VOL_MOUNTEDDEVICE, 0, sizeof(struct attrreference)},
344 {ATTR_VOL_ENCODINGSUSED, 0, sizeof(uint64_t)},
345 {ATTR_VOL_CAPABILITIES, VFSATTR_BIT(f_capabilities), sizeof(vol_capabilities_attr_t)},
b0d623f7 346 {ATTR_VOL_UUID, VFSATTR_BIT(f_uuid), sizeof(uuid_t)},
91447636
A
347 {ATTR_VOL_ATTRIBUTES, VFSATTR_BIT(f_attributes), sizeof(vol_attributes_attr_t)},
348 {ATTR_VOL_INFO, 0, 0},
349 {0, 0, 0}
350};
351
352static int
353getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, struct vfs_attr *vsp,
354 ssize_t *sizep, int is_64bit)
355{
356 attrgroup_t recognised;
357
358 recognised = 0;
359 do {
360 /* is this attribute set? */
361 if (tab->attr & attrs) {
362 recognised |= tab->attr;
363 vsp->f_active |= tab->bits;
364 if (tab->size == ATTR_TIME_SIZE) {
365 if (is_64bit) {
b0d623f7 366 *sizep += sizeof(struct user64_timespec);
91447636 367 } else {
b0d623f7 368 *sizep += sizeof(struct user32_timespec);
91447636
A
369 }
370 } else {
371 *sizep += tab->size;
372 }
373 }
374 } while ((++tab)->attr != 0);
375
376 /* check to make sure that we recognised all of the passed-in attributes */
377 if (attrs & ~recognised)
378 return(EINVAL);
379 return(0);
380}
381
382/*
383 * Given the attributes listed in alp, configure vap to request
384 * the data from a filesystem.
385 */
386static int
387getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
388{
389 int error;
390
391 /*
392 * Parse the above tables.
393 */
394 *sizep = sizeof(uint32_t); /* length count */
b0d623f7
A
395 if (alp->commonattr) {
396 if ((alp->commonattr & ATTR_CMN_VOL_INVALID) &&
397 (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) == 0) {
398 return (EINVAL);
399 }
400 if ((error = getvolattrlist_parsetab(getvolattrlist_common_tab,
401 alp->commonattr, vsp, sizep,
402 is_64bit)) != 0) {
403 return(error);
404 }
405 }
91447636
A
406 if (alp->volattr &&
407 (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
408 return(error);
409
410 return(0);
411}
412
b0d623f7
A
413/*
414 * Given the attributes listed in asp and those supported
415 * in the vsp, fixup the asp attributes to reflect any
416 * missing attributes from the file system
417 */
418static void
419getvolattrlist_fixupattrs(attribute_set_t *asp, struct vfs_attr *vsp)
420{
421 struct getvolattrlist_attrtab *tab;
422
423 if (asp->commonattr) {
424 tab = getvolattrlist_common_tab;
425 do {
426 if ((tab->attr & asp->commonattr) &&
427 (tab->bits != 0) &&
428 ((tab->bits & vsp->f_supported) == 0)) {
429 asp->commonattr &= ~tab->attr;
430 }
431 } while ((++tab)->attr != 0);
432 }
433 if (asp->volattr) {
434 tab = getvolattrlist_vol_tab;
435 do {
436 if ((tab->attr & asp->volattr) &&
437 (tab->bits != 0) &&
438 ((tab->bits & vsp->f_supported) == 0)) {
439 asp->volattr &= ~tab->attr;
440 }
441 } while ((++tab)->attr != 0);
442 }
443}
444
91447636
A
445/*
446 * Table-driven setup for all valid common/dir/file/fork attributes against files.
447 */
448struct getattrlist_attrtab {
449 attrgroup_t attr;
450 uint64_t bits;
451#define VATTR_BIT(b) (VNODE_ATTR_ ## b)
452 ssize_t size;
453 kauth_action_t action;
454};
b0d623f7
A
455
456/*
457 * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this
458 * information, and we will synthesize it at the VFS level.
459 */
91447636
A
460static struct getattrlist_attrtab getattrlist_common_tab[] = {
461 {ATTR_CMN_NAME, VATTR_BIT(va_name), sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES},
462 {ATTR_CMN_DEVID, 0, sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
463 {ATTR_CMN_FSID, VATTR_BIT(va_fsid), sizeof(fsid_t), KAUTH_VNODE_READ_ATTRIBUTES},
464 {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t), KAUTH_VNODE_READ_ATTRIBUTES},
465 {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t), KAUTH_VNODE_READ_ATTRIBUTES},
466 {ATTR_CMN_OBJID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
467 {ATTR_CMN_OBJPERMANENTID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
468 {ATTR_CMN_PAROBJID, VATTR_BIT(va_parentid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
469 {ATTR_CMN_SCRIPT, VATTR_BIT(va_encoding), sizeof(text_encoding_t), KAUTH_VNODE_READ_ATTRIBUTES},
470 {ATTR_CMN_CRTIME, VATTR_BIT(va_create_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
471 {ATTR_CMN_MODTIME, VATTR_BIT(va_modify_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
472 {ATTR_CMN_CHGTIME, VATTR_BIT(va_change_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
473 {ATTR_CMN_ACCTIME, VATTR_BIT(va_access_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
474 {ATTR_CMN_BKUPTIME, VATTR_BIT(va_backup_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
475 {ATTR_CMN_FNDRINFO, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES},
476 {ATTR_CMN_OWNERID, VATTR_BIT(va_uid), sizeof(uid_t), KAUTH_VNODE_READ_ATTRIBUTES},
477 {ATTR_CMN_GRPID, VATTR_BIT(va_gid), sizeof(gid_t), KAUTH_VNODE_READ_ATTRIBUTES},
478 {ATTR_CMN_ACCESSMASK, VATTR_BIT(va_mode), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
479 {ATTR_CMN_FLAGS, VATTR_BIT(va_flags), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
480 {ATTR_CMN_USERACCESS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
481 {ATTR_CMN_EXTENDED_SECURITY, VATTR_BIT(va_acl), sizeof(struct attrreference), KAUTH_VNODE_READ_SECURITY},
482 {ATTR_CMN_UUID, VATTR_BIT(va_uuuid), sizeof(guid_t), KAUTH_VNODE_READ_ATTRIBUTES},
483 {ATTR_CMN_GRPUUID, VATTR_BIT(va_guuid), sizeof(guid_t), KAUTH_VNODE_READ_ATTRIBUTES},
2d21ac55
A
484 {ATTR_CMN_FILEID, VATTR_BIT(va_fileid), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES},
485 {ATTR_CMN_PARENTID, VATTR_BIT(va_parentid), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES},
b0d623f7 486 {ATTR_CMN_FULLPATH, 0, sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES },
316670eb 487 {ATTR_CMN_ADDEDTIME, VATTR_BIT(va_addedtime), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
b0d623f7 488 {ATTR_CMN_RETURNED_ATTRS, 0, sizeof(attribute_set_t), 0},
91447636
A
489 {0, 0, 0, 0}
490};
b0d623f7 491
91447636 492static struct getattrlist_attrtab getattrlist_dir_tab[] = {
2d21ac55 493 {ATTR_DIR_LINKCOUNT, VATTR_BIT(va_dirlinkcount), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
91447636 494 {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nchildren), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
91447636
A
495 {ATTR_DIR_MOUNTSTATUS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
496 {0, 0, 0, 0}
497};
498static struct getattrlist_attrtab getattrlist_file_tab[] = {
499 {ATTR_FILE_LINKCOUNT, VATTR_BIT(va_nlink), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
500 {ATTR_FILE_TOTALSIZE, VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
501 {ATTR_FILE_ALLOCSIZE, VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
502 {ATTR_FILE_IOBLOCKSIZE, VATTR_BIT(va_iosize), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
503 {ATTR_FILE_DEVTYPE, VATTR_BIT(va_rdev), sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
504 {ATTR_FILE_DATALENGTH, VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
505 {ATTR_FILE_DATAALLOCSIZE, VATTR_BIT(va_total_alloc)| VATTR_BIT(va_data_alloc), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
506 {ATTR_FILE_RSRCLENGTH, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
507 {ATTR_FILE_RSRCALLOCSIZE, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
508 {0, 0, 0, 0}
509};
510
2d21ac55
A
511/*
512 * The following are attributes that VFS can derive.
513 *
514 * A majority of them are the same attributes that are required for stat(2) and statfs(2).
515 */
516#define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | \
517 ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | \
518 ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \
519 ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
520 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
521 ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \
b0d623f7 522 ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
2d21ac55
A
523
524#define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \
525 ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
526 ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \
527 ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \
528 ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
b0d623f7 529 ATTR_CMN_FNDRINFO | \
2d21ac55
A
530 ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
531 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
532 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
b0d623f7 533 ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS)
2d21ac55
A
534
535#define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
536
537#define VFS_DFLT_ATTR_FILE (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | \
538 ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \
539 ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \
540 ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \
541 ATTR_FILE_RSRCALLOCSIZE)
542
91447636
A
543static int
544getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs, struct vnode_attr *vap,
545 ssize_t *sizep, kauth_action_t *actionp, int is_64bit)
546{
547 attrgroup_t recognised;
548
549 recognised = 0;
550 do {
551 /* is this attribute set? */
552 if (tab->attr & attrs) {
553 recognised |= tab->attr;
554 vap->va_active |= tab->bits;
555 if (tab->size == ATTR_TIME_SIZE) {
556 if (is_64bit) {
b0d623f7 557 *sizep += sizeof(struct user64_timespec);
91447636 558 } else {
b0d623f7 559 *sizep += sizeof(struct user32_timespec);
91447636
A
560 }
561 } else {
562 *sizep += tab->size;
563 }
564 *actionp |= tab->action;
b0d623f7
A
565 if (attrs == recognised)
566 break; /* all done, get out */
91447636
A
567 }
568 } while ((++tab)->attr != 0);
569
570 /* check to make sure that we recognised all of the passed-in attributes */
571 if (attrs & ~recognised)
572 return(EINVAL);
573 return(0);
574}
575
576/*
577 * Given the attributes listed in alp, configure vap to request
578 * the data from a filesystem.
579 */
580static int
581getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir)
582{
583 int error;
584
585 /*
586 * Parse the above tables.
587 */
588 *sizep = sizeof(uint32_t); /* length count */
589 *actionp = 0;
590 if (alp->commonattr &&
591 (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit)) != 0)
592 return(error);
593 if (isdir && alp->dirattr &&
594 (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit)) != 0)
595 return(error);
596 if (!isdir && alp->fileattr &&
597 (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit)) != 0)
598 return(error);
599
600 return(0);
601}
602
b0d623f7
A
603/*
604 * Given the attributes listed in asp and those supported
605 * in the vap, fixup the asp attributes to reflect any
606 * missing attributes from the file system
607 */
608static void
609getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap)
610{
611 struct getattrlist_attrtab *tab;
612
613 if (asp->commonattr) {
614 tab = getattrlist_common_tab;
615 do {
6d2010ae
A
616 /*
617 * This if() statement is slightly confusing. We're trying to
618 * iterate through all of the bits listed in the array
619 * getattr_common_tab, and see if the filesystem was expected
620 * to support it, and whether or not we need to do anything about this.
621 *
622 * This array is full of structs that have 4 fields (attr, bits, size, action).
623 * The first is used to store the ATTR_CMN_* bit that was being requested
624 * from userland. The second stores the VATTR_BIT corresponding to the field
625 * filled in vnode_attr struct. If it is 0, then we don't typically expect
626 * the filesystem to fill in this field. The third is the size of the field,
627 * and the fourth is the type of kauth actions needed.
628 *
629 * So, for all of the ATTR_CMN bits listed in this array, we iterate through
630 * them, and check to see if it was both passed down to the filesystem via the
631 * va_active bitfield, and whether or not we expect it to be emitted from
632 * the filesystem. If it wasn't supported, then we un-twiddle the bit and move
633 * on. This is done so that we can uncheck those bits and re-request
634 * a vnode_getattr from the filesystem again.
635 */
b0d623f7
A
636 if ((tab->attr & asp->commonattr) &&
637 (tab->bits & vap->va_active) &&
638 (tab->bits & vap->va_supported) == 0) {
639 asp->commonattr &= ~tab->attr;
640 }
641 } while ((++tab)->attr != 0);
642 }
643 if (asp->dirattr) {
644 tab = getattrlist_dir_tab;
645 do {
646 if ((tab->attr & asp->dirattr) &&
647 (tab->bits & vap->va_active) &&
648 (vap->va_supported & tab->bits) == 0) {
649 asp->dirattr &= ~tab->attr;
650 }
651 } while ((++tab)->attr != 0);
652 }
653 if (asp->fileattr) {
654 tab = getattrlist_file_tab;
655 do {
656 if ((tab->attr & asp->fileattr) &&
657 (tab->bits & vap->va_active) &&
658 (vap->va_supported & tab->bits) == 0) {
659 asp->fileattr &= ~tab->attr;
660 }
661 } while ((++tab)->attr != 0);
662 }
663}
664
8f6c56a5
A
665static int
666setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
667{
668 uio_t auio;
669 char uio_buf[UIO_SIZEOF(1)];
670 int error;
671
672 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
673 error = ENOMEM;
674 } else {
675 uio_addiov(auio, CAST_USER_ADDR_T(fndrinfo), 32);
676 error = vn_setxattr(vp, XATTR_FINDERINFO_NAME, auio, XATTR_NOSECURITY, ctx);
677 uio_free(auio);
678 }
679
2d21ac55 680#if CONFIG_FSE
8f6c56a5
A
681 if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) {
682 add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
683 }
2d21ac55 684#endif
8f6c56a5
A
685 return (error);
686}
687
91447636
A
688
689/*
690 * Find something resembling a terminal component name in the mountedonname for vp
691 *
692 */
693static void
694getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
695{
696 int counting;
697 const char *cp;
698
699 /*
700 * We're looking for the last sequence of non / characters, but
701 * not including any trailing / characters.
702 */
703 *np = NULL;
704 *nl = 0;
705 counting = 0;
706 for (cp = mn; *cp != 0; cp++) {
707 if (!counting) {
708 /* start of run of chars */
709 if (*cp != '/') {
710 *np = cp;
711 counting = 1;
712 }
713 } else {
714 /* end of run of chars */
715 if (*cp == '/') {
716 *nl = cp - *np;
717 counting = 0;
718 }
719 }
720 }
721 /* need to close run? */
722 if (counting)
723 *nl = cp - *np;
724}
725
726
727static int
b0d623f7
A
728getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
729 vfs_context_t ctx, int is_64bit)
91447636
A
730{
731 struct vfs_attr vs;
732 struct vnode_attr va;
733 struct _attrlist_buf ab;
734 int error;
735 ssize_t fixedsize, varsize;
2d21ac55
A
736 const char *cnp = NULL; /* protected by ATTR_CMN_NAME */
737 ssize_t cnl = 0; /* protected by ATTR_CMN_NAME */
b0d623f7 738 int release_str = 0;
91447636 739 mount_t mnt;
b0d623f7
A
740 int return_valid;
741 int pack_invalid;
91447636
A
742
743 ab.base = NULL;
744 VATTR_INIT(&va);
745 VFSATTR_INIT(&vs);
746 vs.f_vol_name = NULL;
747 mnt = vp->v_mount;
748
b0d623f7
A
749 /* Check for special packing semantics */
750 return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS);
751 pack_invalid = (uap->options & FSOPT_PACK_INVAL_ATTRS);
752 if (pack_invalid) {
753 /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
754 if (!return_valid) {
755 error = EINVAL;
756 goto out;
757 }
758 /* Keep invalid attrs from being uninitialized */
759 bzero(&vs, sizeof (vs));
760 /* Generate a valid mask for post processing */
761 bcopy(&alp->commonattr, &ab.valid, sizeof (attribute_set_t));
762 }
763
91447636
A
764 /*
765 * For now, the vnode must be the root of its filesystem.
766 * To relax this, we need to be able to find the root vnode of a filesystem
767 * from any vnode in the filesystem.
768 */
769 if (!vnode_isvroot(vp)) {
770 error = EINVAL;
771 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
772 goto out;
773 }
b0d623f7 774
91447636
A
775 /*
776 * Set up the vfs_attr structure and call the filesystem.
777 */
778 if ((error = getvolattrlist_setupvfsattr(alp, &vs, &fixedsize, is_64bit)) != 0) {
779 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
780 goto out;
781 }
782 if (vs.f_active != 0) {
783 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
784 if (VFSATTR_IS_ACTIVE(&vs, f_vol_name)) {
785 vs.f_vol_name = (char *) kalloc(MAXPATHLEN);
786 if (vs.f_vol_name == NULL) {
787 error = ENOMEM;
788 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
789 goto out;
790 }
791 }
792
2d21ac55
A
793#if CONFIG_MACF
794 error = mac_mount_check_getattr(ctx, mnt, &vs);
795 if (error != 0)
796 goto out;
797#endif
91447636
A
798 VFS_DEBUG(ctx, vp, "ATTRLIST - calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported);
799 if ((error = vfs_getattr(mnt, &vs, ctx)) != 0) {
800 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
801 goto out;
802 }
803
804 /*
805 * Did we ask for something the filesystem doesn't support?
806 */
807 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
808 /* default value for volume subtype */
809 if (VFSATTR_IS_ACTIVE(&vs, f_fssubtype)
810 && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype))
811 VFSATTR_RETURN(&vs, f_fssubtype, 0);
812
813 /*
814 * If the file system didn't supply f_signature, then
815 * default it to 'BD', which is the generic signature
816 * that most Carbon file systems should return.
817 */
818 if (VFSATTR_IS_ACTIVE(&vs, f_signature)
819 && !VFSATTR_IS_SUPPORTED(&vs, f_signature))
820 VFSATTR_RETURN(&vs, f_signature, 0x4244);
821
822 /* default for block size */
823 if (VFSATTR_IS_ACTIVE(&vs, f_bsize)
824 && !VFSATTR_IS_SUPPORTED(&vs, f_bsize))
825 VFSATTR_RETURN(&vs, f_bsize, mnt->mnt_devblocksize);
826
2d21ac55
A
827 /* default value for volume f_attributes */
828 if (VFSATTR_IS_ACTIVE(&vs, f_attributes)
829 && !VFSATTR_IS_SUPPORTED(&vs, f_attributes)) {
830 vol_attributes_attr_t *attrp = &vs.f_attributes;
831
832 attrp->validattr.commonattr = VFS_DFLT_ATTR_CMN;
833 attrp->validattr.volattr = VFS_DFLT_ATTR_VOL;
834 attrp->validattr.dirattr = VFS_DFLT_ATTR_DIR;
835 attrp->validattr.fileattr = VFS_DFLT_ATTR_FILE;
836 attrp->validattr.forkattr = 0;
837
838 attrp->nativeattr.commonattr = 0;
839 attrp->nativeattr.volattr = 0;
840 attrp->nativeattr.dirattr = 0;
841 attrp->nativeattr.fileattr = 0;
842 attrp->nativeattr.forkattr = 0;
843 VFSATTR_SET_SUPPORTED(&vs, f_attributes);
844 }
845
846 /* default value for volume f_capabilities */
847 if (VFSATTR_IS_ACTIVE(&vs, f_capabilities)) {
848 /* getattrlist is always supported now. */
849 if (!VFSATTR_IS_SUPPORTED(&vs, f_capabilities)) {
850 vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = 0;
851 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
852 vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
853 vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
854
855 vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0;
856 vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
857 vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
858 vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
859 VFSATTR_SET_SUPPORTED(&vs, f_capabilities);
860 }
861 else {
862 /* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
863 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
864 vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
865 }
866 }
867
91447636
A
868 /* check to see if our fixups were enough */
869 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
b0d623f7
A
870 if (return_valid) {
871 if (pack_invalid) {
872 /* Fix up valid mask for post processing */
873 getvolattrlist_fixupattrs(&ab.valid, &vs);
874
875 /* Force packing of everything asked for */
876 vs.f_supported = vs.f_active;
877 } else {
878 /* Adjust the requested attributes */
879 getvolattrlist_fixupattrs((attribute_set_t *)&alp->commonattr, &vs);
880 }
881 } else {
882 error = EINVAL;
883 goto out;
884 }
91447636
A
885 }
886 }
887 }
888
889 /*
890 * Some fields require data from the root vp
891 */
892 if (alp->commonattr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | ATTR_CMN_SCRIPT)) {
893 VATTR_WANTED(&va, va_uid);
894 VATTR_WANTED(&va, va_gid);
895 VATTR_WANTED(&va, va_mode);
896 VATTR_WANTED(&va, va_flags);
897 VATTR_WANTED(&va, va_encoding);
898
899 if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
900 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp);
901 goto out;
902 }
903
b0d623f7
A
904 if (VATTR_IS_ACTIVE(&va, va_encoding) &&
905 !VATTR_IS_SUPPORTED(&va, va_encoding)) {
906 if (!return_valid || pack_invalid)
907 /* use kTextEncodingMacUnicode */
908 VATTR_RETURN(&va, va_encoding, 0x7e);
909 else
910 /* don't use a default */
911 alp->commonattr &= ~ATTR_CMN_SCRIPT;
912 }
91447636
A
913 }
914
915 /*
916 * Compute variable-size buffer requirements.
917 */
918 varsize = 0;
919 if (alp->commonattr & ATTR_CMN_NAME) {
920 if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
921 vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
922 /* special case for boot volume. Use root name when it's
923 * available (which is the volume name) or just the mount on
924 * name of "/". we must do this for binary compatibility with
925 * pre Tiger code. returning nothing for the boot volume name
926 * breaks installers - 3961058
927 */
928 cnp = vnode_getname(vp);
929 if (cnp == NULL) {
930 /* just use "/" as name */
931 cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
932 }
b0d623f7
A
933 else {
934 release_str = 1;
935 }
91447636
A
936 cnl = strlen(cnp);
937 }
938 else {
939 getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
940 }
941 if (alp->commonattr & ATTR_CMN_NAME)
942 varsize += roundup(cnl + 1, 4);
943 }
944 if (alp->volattr & ATTR_VOL_MOUNTPOINT)
945 varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntonname) + 1, 4);
946 if (alp->volattr & ATTR_VOL_NAME) {
947 vs.f_vol_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
948 varsize += roundup(strlen(vs.f_vol_name) + 1, 4);
949 }
950 if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
951 varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4);
952
953 /*
954 * Allocate a target buffer for attribute results.
955 * Note that since we won't ever copy out more than the caller requested,
956 * we never need to allocate more than they offer.
957 */
39236c6e 958 ab.allocated = ulmin(uap->bufferSize, fixedsize + varsize);
91447636
A
959 if (ab.allocated > ATTR_MAX_BUFFER) {
960 error = ENOMEM;
961 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
962 goto out;
963 }
964 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
965 if (ab.base == NULL) {
966 error = ENOMEM;
967 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
968 goto out;
969 }
970
971 /*
972 * Pack results into the destination buffer.
973 */
974 ab.fixedcursor = ab.base + sizeof(uint32_t);
b0d623f7
A
975 if (return_valid) {
976 ab.fixedcursor += sizeof (attribute_set_t);
977 bzero(&ab.actual, sizeof (ab.actual));
978 }
91447636
A
979 ab.varcursor = ab.base + fixedsize;
980 ab.needed = fixedsize + varsize;
981
982 /* common attributes **************************************************/
b0d623f7 983 if (alp->commonattr & ATTR_CMN_NAME) {
91447636 984 attrlist_pack_string(&ab, cnp, cnl);
b0d623f7
A
985 ab.actual.commonattr |= ATTR_CMN_NAME;
986 }
987 if (alp->commonattr & ATTR_CMN_DEVID) {
2d21ac55 988 ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]);
b0d623f7
A
989 ab.actual.commonattr |= ATTR_CMN_DEVID;
990 }
991 if (alp->commonattr & ATTR_CMN_FSID) {
2d21ac55 992 ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid);
b0d623f7
A
993 ab.actual.commonattr |= ATTR_CMN_FSID;
994 }
995 if (alp->commonattr & ATTR_CMN_OBJTYPE) {
996 if (!return_valid || pack_invalid)
997 ATTR_PACK4(ab, 0);
998 }
999 if (alp->commonattr & ATTR_CMN_OBJTAG) {
2d21ac55 1000 ATTR_PACK4(ab, vp->v_tag);
b0d623f7
A
1001 ab.actual.commonattr |= ATTR_CMN_OBJTAG;
1002 }
91447636 1003 if (alp->commonattr & ATTR_CMN_OBJID) {
b0d623f7
A
1004 if (!return_valid || pack_invalid) {
1005 fsobj_id_t f = {0, 0};
1006 ATTR_PACK8(ab, f);
1007 }
91447636
A
1008 }
1009 if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
b0d623f7
A
1010 if (!return_valid || pack_invalid) {
1011 fsobj_id_t f = {0, 0};
1012 ATTR_PACK8(ab, f);
1013 }
91447636
A
1014 }
1015 if (alp->commonattr & ATTR_CMN_PAROBJID) {
b0d623f7
A
1016 if (!return_valid || pack_invalid) {
1017 fsobj_id_t f = {0, 0};
1018 ATTR_PACK8(ab, f);
1019 }
91447636
A
1020 }
1021 /* note that this returns the encoding for the volume name, not the node name */
b0d623f7 1022 if (alp->commonattr & ATTR_CMN_SCRIPT) {
2d21ac55 1023 ATTR_PACK4(ab, va.va_encoding);
b0d623f7
A
1024 ab.actual.commonattr |= ATTR_CMN_SCRIPT;
1025 }
1026 if (alp->commonattr & ATTR_CMN_CRTIME) {
2d21ac55 1027 ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit);
b0d623f7
A
1028 ab.actual.commonattr |= ATTR_CMN_CRTIME;
1029 }
1030 if (alp->commonattr & ATTR_CMN_MODTIME) {
2d21ac55 1031 ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
b0d623f7
A
1032 ab.actual.commonattr |= ATTR_CMN_MODTIME;
1033 }
1034 if (alp->commonattr & ATTR_CMN_CHGTIME) {
1035 if (!return_valid || pack_invalid)
1036 ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
1037 }
1038 if (alp->commonattr & ATTR_CMN_ACCTIME) {
2d21ac55 1039 ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
b0d623f7
A
1040 ab.actual.commonattr |= ATTR_CMN_ACCTIME;
1041 }
1042 if (alp->commonattr & ATTR_CMN_BKUPTIME) {
2d21ac55 1043 ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit);
b0d623f7
A
1044 ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
1045 }
91447636
A
1046 if (alp->commonattr & ATTR_CMN_FNDRINFO) {
1047 char f[32];
1048 /*
1049 * This attribute isn't really Finder Info, at least for HFS.
1050 */
1051 if (vp->v_tag == VT_HFS) {
b0d623f7
A
1052 error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx);
1053 if (error == 0) {
1054 attrlist_pack_fixed(&ab, f, sizeof(f));
1055 ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
1056 } else if (!return_valid) {
91447636 1057 goto out;
b0d623f7
A
1058 }
1059 } else if (!return_valid || pack_invalid) {
91447636
A
1060 /* XXX we could at least pass out the volume UUID here */
1061 bzero(&f, sizeof(f));
b0d623f7 1062 attrlist_pack_fixed(&ab, f, sizeof(f));
91447636 1063 }
91447636 1064 }
b0d623f7 1065 if (alp->commonattr & ATTR_CMN_OWNERID) {
2d21ac55 1066 ATTR_PACK4(ab, va.va_uid);
b0d623f7
A
1067 ab.actual.commonattr |= ATTR_CMN_OWNERID;
1068 }
1069 if (alp->commonattr & ATTR_CMN_GRPID) {
2d21ac55 1070 ATTR_PACK4(ab, va.va_gid);
b0d623f7
A
1071 ab.actual.commonattr |= ATTR_CMN_GRPID;
1072 }
1073 if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
91447636 1074 ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
b0d623f7
A
1075 ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
1076 }
1077 if (alp->commonattr & ATTR_CMN_FLAGS) {
2d21ac55 1078 ATTR_PACK4(ab, va.va_flags);
b0d623f7
A
1079 ab.actual.commonattr |= ATTR_CMN_FLAGS;
1080 }
91447636
A
1081 if (alp->commonattr & ATTR_CMN_USERACCESS) { /* XXX this is expensive and also duplicate work */
1082 uint32_t perms = 0;
1083 if (vnode_isdir(vp)) {
1084 if (vnode_authorize(vp, NULL,
1085 KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
1086 perms |= W_OK;
1087 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
1088 perms |= R_OK;
1089 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
1090 perms |= X_OK;
1091 } else {
1092 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
1093 perms |= W_OK;
1094 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
1095 perms |= R_OK;
1096 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
1097 perms |= X_OK;
1098 }
2d21ac55
A
1099#if CONFIG_MACF
1100 /*
1101 * Rather than MAC preceding DAC, in this case we want
1102 * the smallest set of permissions granted by both MAC & DAC
1103 * checks. We won't add back any permissions.
1104 */
1105 if (perms & W_OK)
1106 if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
1107 perms &= ~W_OK;
1108 if (perms & R_OK)
1109 if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
1110 perms &= ~R_OK;
1111 if (perms & X_OK)
1112 if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
1113 perms &= ~X_OK;
1114#endif /* MAC */
91447636 1115 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
2d21ac55 1116 ATTR_PACK4(ab, perms);
b0d623f7
A
1117 ab.actual.commonattr |= ATTR_CMN_USERACCESS;
1118 }
1119 /*
1120 * The following common volume attributes are only
1121 * packed when the pack_invalid mode is enabled.
1122 */
1123 if (pack_invalid) {
1124 uint64_t fid = 0;
1125
1126 if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY)
1127 attrlist_pack_variable(&ab, NULL, 0);
1128 if (alp->commonattr & ATTR_CMN_UUID)
1129 ATTR_PACK(&ab, kauth_null_guid);
1130 if (alp->commonattr & ATTR_CMN_GRPUUID)
1131 ATTR_PACK(&ab, kauth_null_guid);
1132 if (alp->commonattr & ATTR_CMN_FILEID)
1133 ATTR_PACK8(ab, fid);
1134 if (alp->commonattr & ATTR_CMN_PARENTID)
1135 ATTR_PACK8(ab, fid);
91447636
A
1136 }
1137
1138 /* volume attributes **************************************************/
1139
b0d623f7 1140 if (alp->volattr & ATTR_VOL_FSTYPE) {
91447636 1141 ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
b0d623f7
A
1142 ab.actual.volattr |= ATTR_VOL_FSTYPE;
1143 }
1144 if (alp->volattr & ATTR_VOL_SIGNATURE) {
91447636 1145 ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
b0d623f7
A
1146 ab.actual.volattr |= ATTR_VOL_SIGNATURE;
1147 }
1148 if (alp->volattr & ATTR_VOL_SIZE) {
91447636 1149 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
b0d623f7
A
1150 ab.actual.volattr |= ATTR_VOL_SIZE;
1151 }
1152 if (alp->volattr & ATTR_VOL_SPACEFREE) {
91447636 1153 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
b0d623f7
A
1154 ab.actual.volattr |= ATTR_VOL_SPACEFREE;
1155 }
1156 if (alp->volattr & ATTR_VOL_SPACEAVAIL) {
91447636 1157 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
b0d623f7
A
1158 ab.actual.volattr |= ATTR_VOL_SPACEAVAIL;
1159 }
1160 if (alp->volattr & ATTR_VOL_MINALLOCATION) {
91447636 1161 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
b0d623f7
A
1162 ab.actual.volattr |= ATTR_VOL_MINALLOCATION;
1163 }
1164 if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP) {
91447636 1165 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); /* not strictly true */
b0d623f7
A
1166 ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP;
1167 }
1168 if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) {
91447636 1169 ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
b0d623f7
A
1170 ab.actual.volattr |= ATTR_VOL_IOBLOCKSIZE;
1171 }
1172 if (alp->volattr & ATTR_VOL_OBJCOUNT) {
91447636 1173 ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
b0d623f7
A
1174 ab.actual.volattr |= ATTR_VOL_OBJCOUNT;
1175 }
1176 if (alp->volattr & ATTR_VOL_FILECOUNT) {
91447636 1177 ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
b0d623f7
A
1178 ab.actual.volattr |= ATTR_VOL_FILECOUNT;
1179 }
1180 if (alp->volattr & ATTR_VOL_DIRCOUNT) {
91447636 1181 ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
b0d623f7
A
1182 ab.actual.volattr |= ATTR_VOL_DIRCOUNT;
1183 }
1184 if (alp->volattr & ATTR_VOL_MAXOBJCOUNT) {
91447636 1185 ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
b0d623f7
A
1186 ab.actual.volattr |= ATTR_VOL_MAXOBJCOUNT;
1187 }
1188 if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
91447636 1189 attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
b0d623f7
A
1190 ab.actual.volattr |= ATTR_VOL_MOUNTPOINT;
1191 }
1192 if (alp->volattr & ATTR_VOL_NAME) {
91447636 1193 attrlist_pack_string(&ab, vs.f_vol_name, 0);
b0d623f7
A
1194 ab.actual.volattr |= ATTR_VOL_NAME;
1195 }
1196 if (alp->volattr & ATTR_VOL_MOUNTFLAGS) {
91447636 1197 ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
b0d623f7
A
1198 ab.actual.volattr |= ATTR_VOL_MOUNTFLAGS;
1199 }
1200 if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
91447636 1201 attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
b0d623f7
A
1202 ab.actual.volattr |= ATTR_VOL_MOUNTEDDEVICE;
1203 }
1204 if (alp->volattr & ATTR_VOL_ENCODINGSUSED) {
1205 if (!return_valid || pack_invalid)
1206 ATTR_PACK_CAST(&ab, uint64_t, ~0LL); /* return all encodings */
1207 }
91447636
A
1208 if (alp->volattr & ATTR_VOL_CAPABILITIES) {
1209 /* fix up volume capabilities */
1210 if (vfs_extendedsecurity(mnt)) {
1211 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
1212 } else {
1213 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
1214 }
1215 vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
1216 ATTR_PACK(&ab, vs.f_capabilities);
b0d623f7
A
1217 ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
1218 }
1219 if (alp->volattr & ATTR_VOL_UUID) {
1220 ATTR_PACK(&ab, vs.f_uuid);
6d2010ae 1221 ab.actual.volattr |= ATTR_VOL_UUID;
91447636
A
1222 }
1223 if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
1224 /* fix up volume attribute information */
2d21ac55
A
1225
1226 vs.f_attributes.validattr.commonattr |= VFS_DFLT_ATTR_CMN;
1227 vs.f_attributes.validattr.volattr |= VFS_DFLT_ATTR_VOL;
1228 vs.f_attributes.validattr.dirattr |= VFS_DFLT_ATTR_DIR;
1229 vs.f_attributes.validattr.fileattr |= VFS_DFLT_ATTR_FILE;
1230
91447636
A
1231 if (vfs_extendedsecurity(mnt)) {
1232 vs.f_attributes.validattr.commonattr |= (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1233 } else {
1234 vs.f_attributes.validattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1235 vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
1236 }
1237 ATTR_PACK(&ab, vs.f_attributes);
b0d623f7 1238 ab.actual.volattr |= ATTR_VOL_ATTRIBUTES;
91447636
A
1239 }
1240
1241 /* diagnostic */
b0d623f7
A
1242 if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
1243 panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
1244 fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
1245 if (!return_valid && ab.varcursor != (ab.base + ab.needed))
1246 panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
91447636
A
1247
1248 /*
1249 * In the compatible case, we report the smaller of the required and returned sizes.
1250 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1251 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1252 * they gave us, so they can always check for truncation themselves.
1253 */
1254 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
1255
b0d623f7
A
1256 /* Return attribute set output if requested. */
1257 if (return_valid) {
1258 ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
1259 if (pack_invalid) {
1260 /* Only report the attributes that are valid */
1261 ab.actual.commonattr &= ab.valid.commonattr;
1262 ab.actual.volattr &= ab.valid.volattr;
1263 }
1264 bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
1265 }
91447636
A
1266 error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
1267
1268out:
1269 if (vs.f_vol_name != NULL)
1270 kfree(vs.f_vol_name, MAXPATHLEN);
b0d623f7
A
1271 if (release_str) {
1272 vnode_putname(cnp);
1273 }
91447636
A
1274 if (ab.base != NULL)
1275 FREE(ab.base, M_TEMP);
1276 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
1277 return(error);
1278}
1279
1280/*
1281 * Obtain attribute information about a filesystem object.
1282 */
b0d623f7
A
1283
1284static int
39236c6e
A
1285getattrlist_internal(vnode_t vp, struct getattrlist_args *uap,
1286 __unused struct componentname *getattr_name, proc_t p, vfs_context_t ctx)
91447636
A
1287{
1288 struct attrlist al;
1289 struct vnode_attr va;
91447636 1290 struct _attrlist_buf ab;
91447636
A
1291 kauth_action_t action;
1292 ssize_t fixedsize, varsize;
1293 const char *cnp;
2d21ac55 1294 const char *vname = NULL;
b0d623f7
A
1295 char *fullpathptr;
1296 ssize_t fullpathlen;
91447636 1297 ssize_t cnl;
2d21ac55 1298 int proc_is64;
91447636 1299 int error;
b0d623f7
A
1300 int return_valid;
1301 int pack_invalid;
1302 int vtype = 0;
6d2010ae 1303 uint32_t perms = 0;
91447636 1304
2d21ac55 1305 proc_is64 = proc_is64bit(p);
91447636
A
1306 VATTR_INIT(&va);
1307 va.va_name = NULL;
1308 ab.base = NULL;
1309 cnp = "unknown";
1310 cnl = 0;
b0d623f7
A
1311 fullpathptr = NULL;
1312 fullpathlen = 0;
91447636
A
1313
1314 /*
1315 * Fetch the attribute request.
1316 */
1317 if ((error = copyin(uap->alist, &al, sizeof(al))) != 0)
1318 goto out;
1319 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
1320 error = EINVAL;
1321 goto out;
1322 }
1323
1324 VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1325 vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
1326 (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
b0d623f7 1327
2d21ac55
A
1328#if CONFIG_MACF
1329 error = mac_vnode_check_getattrlist(ctx, vp, &al);
1330 if (error)
1331 goto out;
1332#endif /* MAC */
1333
91447636
A
1334 /*
1335 * It is legal to request volume or file attributes,
1336 * but not both.
1337 */
1338 if (al.volattr) {
1339 if (al.fileattr || al.dirattr || al.forkattr) {
1340 error = EINVAL;
1341 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
1342 goto out;
1343 }
1344 /* handle volume attribute request */
2d21ac55 1345 error = getvolattrlist(vp, uap, &al, ctx, proc_is64);
91447636
A
1346 goto out;
1347 }
1348
b0d623f7
A
1349 /* Check for special packing semantics */
1350 return_valid = (al.commonattr & ATTR_CMN_RETURNED_ATTRS);
1351 pack_invalid = (uap->options & FSOPT_PACK_INVAL_ATTRS);
1352 if (pack_invalid) {
1353 /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
1354 if (!return_valid || al.forkattr) {
1355 error = EINVAL;
1356 goto out;
1357 }
1358 /* Keep invalid attrs from being uninitialized */
1359 bzero(&va, sizeof (va));
1360 /* Generate a valid mask for post processing */
1361 bcopy(&al.commonattr, &ab.valid, sizeof (attribute_set_t));
1362 }
1363
1364 /* Pick up the vnode type. If the FS is bad and changes vnode types on us, we
1365 * will have a valid snapshot that we can work from here.
1366 */
1367 vtype = vp->v_type;
1368
1369
91447636
A
1370 /*
1371 * Set up the vnode_attr structure and authorise.
1372 */
b0d623f7 1373 if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) {
91447636
A
1374 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
1375 goto out;
1376 }
2d21ac55 1377 if ((error = vnode_authorize(vp, NULL, action, ctx)) != 0) {
91447636
A
1378 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorisation failed/denied");
1379 goto out;
1380 }
1381
b0d623f7
A
1382 /*
1383 * If we're asking for the full path, allocate a buffer for that.
1384 */
1385 if (al.commonattr & (ATTR_CMN_FULLPATH)) {
1386 fullpathptr = (char*) kalloc(MAXPATHLEN);
1387 if (fullpathptr == NULL) {
1388 error = ENOMEM;
1389 VFS_DEBUG(ctx,vp, "ATTRLIST - ERROR: cannot allocate fullpath buffer");
1390 goto out;
1391 }
1392 }
1393
1394
91447636
A
1395 if (va.va_active != 0) {
1396 /*
1397 * If we're going to ask for va_name, allocate a buffer to point it at
1398 */
1399 if (VATTR_IS_ACTIVE(&va, va_name)) {
1400 va.va_name = (char *) kalloc(MAXPATHLEN);
1401 if (va.va_name == NULL) {
1402 error = ENOMEM;
1403 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
1404 goto out;
1405 }
1406 }
1407
1408 /*
1409 * Call the filesystem.
1410 */
2d21ac55 1411 if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
91447636
A
1412 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
1413 goto out;
1414 }
1415
1416 /* did we ask for something the filesystem doesn't support? */
1417 if (!VATTR_ALL_SUPPORTED(&va)) {
1418
1419 /*
1420 * There are a couple of special cases. If we are after object IDs,
1421 * we can make do with va_fileid.
1422 */
2d21ac55 1423 if ((al.commonattr & (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) && !VATTR_IS_SUPPORTED(&va, va_linkid))
91447636 1424 VATTR_CLEAR_ACTIVE(&va, va_linkid); /* forget we wanted this */
b0d623f7 1425
91447636 1426 /*
b0d623f7
A
1427 * Many filesystems don't know their parent object id.
1428 * If necessary, attempt to derive it from the vnode.
91447636 1429 */
b0d623f7
A
1430 if ((al.commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) &&
1431 !VATTR_IS_SUPPORTED(&va, va_parentid)) {
1432 vnode_t dvp;
1433
1434 if ((dvp = vnode_getparent(vp)) != NULLVP) {
1435 struct vnode_attr lva;
1436
1437 VATTR_INIT(&lva);
1438 VATTR_WANTED(&lva, va_fileid);
1439 if (vnode_getattr(dvp, &lva, ctx) == 0 &&
1440 VATTR_IS_SUPPORTED(&va, va_fileid)) {
1441 va.va_parentid = lva.va_fileid;
1442 VATTR_SET_SUPPORTED(&va, va_parentid);
1443 }
1444 vnode_put(dvp);
1445 }
1446 }
91447636
A
1447 /*
1448 * And we can report datasize/alloc from total.
1449 */
1450 if ((al.fileattr & ATTR_FILE_DATALENGTH) && !VATTR_IS_SUPPORTED(&va, va_data_size))
1451 VATTR_CLEAR_ACTIVE(&va, va_data_size);
1452 if ((al.fileattr & ATTR_FILE_DATAALLOCSIZE) && !VATTR_IS_SUPPORTED(&va, va_data_alloc))
1453 VATTR_CLEAR_ACTIVE(&va, va_data_alloc);
1454
1455 /*
1456 * If we don't have an encoding, go with UTF-8
1457 */
b0d623f7
A
1458 if ((al.commonattr & ATTR_CMN_SCRIPT) &&
1459 !VATTR_IS_SUPPORTED(&va, va_encoding) && !return_valid)
91447636
A
1460 VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
1461
1462 /*
1463 * If we don't have a name, we'll get one from the vnode or mount point.
1464 */
1465 if ((al.commonattr & ATTR_CMN_NAME) && !VATTR_IS_SUPPORTED(&va, va_name)) {
1466 VATTR_CLEAR_ACTIVE(&va, va_name);
1467 }
1468
2d21ac55
A
1469 /* If va_dirlinkcount isn't supported use a default of 1. */
1470 if ((al.dirattr & ATTR_DIR_LINKCOUNT) && !VATTR_IS_SUPPORTED(&va, va_dirlinkcount)) {
1471 VATTR_RETURN(&va, va_dirlinkcount, 1);
91447636
A
1472 }
1473
1474 /* check again */
1475 if (!VATTR_ALL_SUPPORTED(&va)) {
b0d623f7
A
1476 if (return_valid) {
1477 if (pack_invalid) {
1478 /* Fix up valid mask for post processing */
1479 getattrlist_fixupattrs(&ab.valid, &va);
1480
1481 /* Force packing of everything asked for */
1482 va.va_supported = va.va_active;
1483 } else {
1484 /* Adjust the requested attributes */
1485 getattrlist_fixupattrs((attribute_set_t *)&al.commonattr, &va);
1486 }
1487 } else {
1488 error = EINVAL;
1489 goto out;
1490 }
91447636
A
1491 }
1492 }
1493 }
1494
1495 /*
1496 * Compute variable-space requirements.
1497 */
b0d623f7
A
1498 varsize = 0; /* length count */
1499
1500 /* We may need to fix up the name attribute if requested */
91447636
A
1501 if (al.commonattr & ATTR_CMN_NAME) {
1502 if (VATTR_IS_SUPPORTED(&va, va_name)) {
1503 va.va_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
1504 cnp = va.va_name;
1505 cnl = strlen(cnp);
39236c6e
A
1506 }
1507 else {
1508 /* Filesystem did not support getting the name */
91447636
A
1509 if (vnode_isvroot(vp)) {
1510 if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
39236c6e 1511 vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
91447636
A
1512 /* special case for boot volume. Use root name when it's
1513 * available (which is the volume name) or just the mount on
1514 * name of "/". we must do this for binary compatibility with
1515 * pre Tiger code. returning nothing for the boot volume name
1516 * breaks installers - 3961058
1517 */
1518 cnp = vname = vnode_getname(vp);
1519 if (cnp == NULL) {
1520 /* just use "/" as name */
1521 cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
1522 }
1523 cnl = strlen(cnp);
1524 }
1525 else {
1526 getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
1527 }
39236c6e
A
1528 }
1529 else {
91447636
A
1530 cnp = vname = vnode_getname(vp);
1531 cnl = 0;
1532 if (cnp != NULL) {
1533 cnl = strlen(cnp);
1534 }
1535 }
1536 }
1537 varsize += roundup(cnl + 1, 4);
1538 }
1539
b0d623f7
A
1540 /*
1541 * Compute the full path to this vnode, if necessary. This attribute is almost certainly
1542 * not supported by any filesystem, so build the path to this vnode at this time.
1543 */
1544 if (al.commonattr & ATTR_CMN_FULLPATH) {
1545 int len = MAXPATHLEN;
1546 int err;
1547 /* call build_path making sure NOT to use the cache-only behavior */
1548 err = build_path(vp, fullpathptr, len, &len, 0, vfs_context_current());
1549 if (err) {
1550 error = err;
1551 goto out;
1552 }
1553 fullpathlen = 0;
1554 if (fullpathptr){
1555 fullpathlen = strlen(fullpathptr);
1556 }
1557 varsize += roundup(fullpathlen+1, 4);
1558 }
1559
91447636
A
1560 /*
1561 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
1562 *
1563 * XXX This needs to change at some point; since the blob is opaque in
1564 * user-space this is OK.
1565 */
1566 if ((al.commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
39236c6e
A
1567 VATTR_IS_SUPPORTED(&va, va_acl) &&
1568 (va.va_acl != NULL)) {
1569
1570 /*
1571 * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
1572 * KAUTH_FILESEC_NOACL ourselves
1573 */
1574 if (va.va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) {
1575 varsize += roundup((KAUTH_FILESEC_SIZE(0)), 4);
1576 }
1577 else {
1578 varsize += roundup ((KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount)), 4);
1579 }
1580 }
1581
91447636
A
1582 /*
1583 * Allocate a target buffer for attribute results.
3a60a9f5
A
1584 *
1585 * Note that we won't ever copy out more than the caller requested, even though
2d21ac55 1586 * we might have to allocate more than they offer so that the diagnostic checks
3a60a9f5 1587 * don't result in a panic if the caller's buffer is too small..
91447636 1588 */
3a60a9f5 1589 ab.allocated = fixedsize + varsize;
39236c6e
A
1590 /* Cast 'allocated' to an unsigned to verify allocation size */
1591 if ( ((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
91447636
A
1592 error = ENOMEM;
1593 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
1594 goto out;
1595 }
1596 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
1597 if (ab.base == NULL) {
1598 error = ENOMEM;
1599 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
1600 goto out;
1601 }
1602
2d21ac55
A
1603 /* set the S_IFMT bits for the mode */
1604 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
1605 switch (vp->v_type) {
1606 case VREG:
1607 va.va_mode |= S_IFREG;
1608 break;
1609 case VDIR:
1610 va.va_mode |= S_IFDIR;
1611 break;
1612 case VBLK:
1613 va.va_mode |= S_IFBLK;
1614 break;
1615 case VCHR:
1616 va.va_mode |= S_IFCHR;
1617 break;
1618 case VLNK:
1619 va.va_mode |= S_IFLNK;
1620 break;
1621 case VSOCK:
1622 va.va_mode |= S_IFSOCK;
1623 break;
1624 case VFIFO:
1625 va.va_mode |= S_IFIFO;
1626 break;
1627 default:
1628 error = EBADF;
1629 goto out;
1630 }
1631 }
1632
91447636
A
1633 /*
1634 * Pack results into the destination buffer.
1635 */
1636 ab.fixedcursor = ab.base + sizeof(uint32_t);
b0d623f7
A
1637 if (return_valid) {
1638 ab.fixedcursor += sizeof (attribute_set_t);
1639 bzero(&ab.actual, sizeof (ab.actual));
1640 }
91447636 1641 ab.varcursor = ab.base + fixedsize;
3a60a9f5 1642 ab.needed = ab.allocated;
91447636
A
1643
1644 /* common attributes **************************************************/
b0d623f7 1645 if (al.commonattr & ATTR_CMN_NAME) {
91447636 1646 attrlist_pack_string(&ab, cnp, cnl);
b0d623f7
A
1647 ab.actual.commonattr |= ATTR_CMN_NAME;
1648 }
1649 if (al.commonattr & ATTR_CMN_DEVID) {
2d21ac55 1650 ATTR_PACK4(ab, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
b0d623f7
A
1651 ab.actual.commonattr |= ATTR_CMN_DEVID;
1652 }
1653 if (al.commonattr & ATTR_CMN_FSID) {
2d21ac55 1654 ATTR_PACK8(ab, vp->v_mount->mnt_vfsstat.f_fsid);
b0d623f7
A
1655 ab.actual.commonattr |= ATTR_CMN_FSID;
1656 }
1657 if (al.commonattr & ATTR_CMN_OBJTYPE) {
1658 ATTR_PACK4(ab, vtype);
1659 ab.actual.commonattr |= ATTR_CMN_OBJTYPE;
1660 }
1661 if (al.commonattr & ATTR_CMN_OBJTAG) {
2d21ac55 1662 ATTR_PACK4(ab, vp->v_tag);
b0d623f7
A
1663 ab.actual.commonattr |= ATTR_CMN_OBJTAG;
1664 }
91447636
A
1665 if (al.commonattr & ATTR_CMN_OBJID) {
1666 fsobj_id_t f;
1667 /*
1668 * Carbon can't deal with us reporting the target ID
1669 * for links. So we ask the filesystem to give us the
1670 * source ID as well, and if it gives us one, we use
1671 * it instead.
1672 */
1673 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1674 f.fid_objno = va.va_linkid;
1675 } else {
1676 f.fid_objno = va.va_fileid;
1677 }
1678 f.fid_generation = 0;
2d21ac55 1679 ATTR_PACK8(ab, f);
b0d623f7 1680 ab.actual.commonattr |= ATTR_CMN_OBJID;
91447636
A
1681 }
1682 if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
1683 fsobj_id_t f;
1684 /*
1685 * Carbon can't deal with us reporting the target ID
1686 * for links. So we ask the filesystem to give us the
1687 * source ID as well, and if it gives us one, we use
1688 * it instead.
1689 */
1690 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1691 f.fid_objno = va.va_linkid;
1692 } else {
1693 f.fid_objno = va.va_fileid;
1694 }
1695 f.fid_generation = 0;
2d21ac55 1696 ATTR_PACK8(ab, f);
b0d623f7 1697 ab.actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
91447636
A
1698 }
1699 if (al.commonattr & ATTR_CMN_PAROBJID) {
1700 fsobj_id_t f;
2d21ac55 1701
b0d623f7 1702 f.fid_objno = va.va_parentid; /* could be lossy here! */
91447636 1703 f.fid_generation = 0;
2d21ac55 1704 ATTR_PACK8(ab, f);
b0d623f7 1705 ab.actual.commonattr |= ATTR_CMN_PAROBJID;
91447636 1706 }
b0d623f7
A
1707 if (al.commonattr & ATTR_CMN_SCRIPT) {
1708 if (VATTR_IS_SUPPORTED(&va, va_encoding)) {
1709 ATTR_PACK4(ab, va.va_encoding);
1710 ab.actual.commonattr |= ATTR_CMN_SCRIPT;
1711 } else if (!return_valid || pack_invalid) {
1712 ATTR_PACK4(ab, 0x7e);
1713 }
1714 }
1715 if (al.commonattr & ATTR_CMN_CRTIME) {
2d21ac55 1716 ATTR_PACK_TIME(ab, va.va_create_time, proc_is64);
b0d623f7
A
1717 ab.actual.commonattr |= ATTR_CMN_CRTIME;
1718 }
1719 if (al.commonattr & ATTR_CMN_MODTIME) {
2d21ac55 1720 ATTR_PACK_TIME(ab, va.va_modify_time, proc_is64);
b0d623f7
A
1721 ab.actual.commonattr |= ATTR_CMN_MODTIME;
1722 }
1723 if (al.commonattr & ATTR_CMN_CHGTIME) {
2d21ac55 1724 ATTR_PACK_TIME(ab, va.va_change_time, proc_is64);
b0d623f7
A
1725 ab.actual.commonattr |= ATTR_CMN_CHGTIME;
1726 }
1727 if (al.commonattr & ATTR_CMN_ACCTIME) {
2d21ac55 1728 ATTR_PACK_TIME(ab, va.va_access_time, proc_is64);
b0d623f7
A
1729 ab.actual.commonattr |= ATTR_CMN_ACCTIME;
1730 }
1731 if (al.commonattr & ATTR_CMN_BKUPTIME) {
2d21ac55 1732 ATTR_PACK_TIME(ab, va.va_backup_time, proc_is64);
b0d623f7
A
1733 ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
1734 }
6d2010ae
A
1735 /*
1736 * They are requesting user access, we should obtain this before getting
1737 * the finder info. For some network file systems this is a performance
1738 * improvement.
1739 */
1740 if (al.commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
1741 if (vtype == VDIR) {
1742 if (vnode_authorize(vp, NULL,
1743 KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
1744 perms |= W_OK;
1745 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
1746 perms |= R_OK;
1747 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
1748 perms |= X_OK;
1749 } else {
1750 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
1751 perms |= W_OK;
1752 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
1753 perms |= R_OK;
1754 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
1755 perms |= X_OK;
1756 }
1757 }
1758
91447636
A
1759 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1760 uio_t auio;
b0d623f7 1761 size_t fisize = 32;
91447636
A
1762 char uio_buf[UIO_SIZEOF(1)];
1763
b0d623f7
A
1764 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
1765 uio_buf, sizeof(uio_buf))) == NULL) {
1766 error = ENOMEM;
1767 goto out;
1768 }
1769 uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
1770 error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio,
1771 &fisize, XATTR_NOSECURITY, ctx);
1772 uio_free(auio);
1773 /*
1774 * Default to zeros if its not available,
1775 * unless ATTR_CMN_RETURNED_ATTRS was requested.
1776 */
1777 if (error &&
1778 (!return_valid || pack_invalid) &&
1779 ((error == ENOATTR) || (error == ENOENT) ||
1780 (error == ENOTSUP) || (error == EPERM))) {
1781 VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1782 bzero(ab.fixedcursor, 32);
1783 error = 0;
1784 }
1785 if (error == 0) {
1786 ab.fixedcursor += 32;
1787 ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
1788 } else if (!return_valid) {
1789 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1790 goto out;
91447636 1791 }
91447636 1792 }
b0d623f7 1793 if (al.commonattr & ATTR_CMN_OWNERID) {
2d21ac55 1794 ATTR_PACK4(ab, va.va_uid);
b0d623f7
A
1795 ab.actual.commonattr |= ATTR_CMN_OWNERID;
1796 }
1797 if (al.commonattr & ATTR_CMN_GRPID) {
2d21ac55 1798 ATTR_PACK4(ab, va.va_gid);
b0d623f7
A
1799 ab.actual.commonattr |= ATTR_CMN_GRPID;
1800 }
1801 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
2d21ac55 1802 ATTR_PACK4(ab, va.va_mode);
b0d623f7
A
1803 ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
1804 }
1805 if (al.commonattr & ATTR_CMN_FLAGS) {
2d21ac55 1806 ATTR_PACK4(ab, va.va_flags);
b0d623f7
A
1807 ab.actual.commonattr |= ATTR_CMN_FLAGS;
1808 }
6d2010ae
A
1809 /* We already obtain the user access, so just fill in the buffer here */
1810 if (al.commonattr & ATTR_CMN_USERACCESS) {
2d21ac55
A
1811#if CONFIG_MACF
1812 /*
1813 * Rather than MAC preceding DAC, in this case we want
1814 * the smallest set of permissions granted by both MAC & DAC
1815 * checks. We won't add back any permissions.
1816 */
1817 if (perms & W_OK)
1818 if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
1819 perms &= ~W_OK;
1820 if (perms & R_OK)
1821 if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
1822 perms &= ~R_OK;
1823 if (perms & X_OK)
1824 if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
1825 perms &= ~X_OK;
1826#endif /* MAC */
91447636 1827 VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
2d21ac55 1828 ATTR_PACK4(ab, perms);
b0d623f7 1829 ab.actual.commonattr |= ATTR_CMN_USERACCESS;
91447636
A
1830 }
1831 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1832 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) {
1833 struct kauth_filesec fsec;
1834 /*
1835 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1836 */
1837 fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
1838 fsec.fsec_owner = kauth_null_guid;
1839 fsec.fsec_group = kauth_null_guid;
b0d623f7
A
1840 attrlist_pack_variable2(&ab, &fsec, __offsetof(struct kauth_filesec, fsec_acl), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
1841 ab.actual.commonattr |= ATTR_CMN_EXTENDED_SECURITY;
1842 } else if (!return_valid || pack_invalid) {
91447636
A
1843 attrlist_pack_variable(&ab, NULL, 0);
1844 }
1845 }
1846 if (al.commonattr & ATTR_CMN_UUID) {
b0d623f7
A
1847 if (VATTR_IS_SUPPORTED(&va, va_uuuid)) {
1848 ATTR_PACK(&ab, va.va_uuuid);
1849 ab.actual.commonattr |= ATTR_CMN_UUID;
1850 } else if (!return_valid || pack_invalid) {
1851 ATTR_PACK(&ab, kauth_null_guid);
1852 }
1853 }
1854 if (al.commonattr & ATTR_CMN_GRPUUID) {
1855 if (VATTR_IS_SUPPORTED(&va, va_guuid)) {
1856 ATTR_PACK(&ab, va.va_guuid);
1857 ab.actual.commonattr |= ATTR_CMN_GRPUUID;
1858 } else if (!return_valid || pack_invalid) {
1859 ATTR_PACK(&ab, kauth_null_guid);
1860 }
1861 }
2d21ac55
A
1862 if (al.commonattr & ATTR_CMN_FILEID) {
1863 ATTR_PACK8(ab, va.va_fileid);
b0d623f7 1864 ab.actual.commonattr |= ATTR_CMN_FILEID;
2d21ac55
A
1865 }
1866 if (al.commonattr & ATTR_CMN_PARENTID) {
b0d623f7
A
1867 ATTR_PACK8(ab, va.va_parentid);
1868 ab.actual.commonattr |= ATTR_CMN_PARENTID;
1869 }
1870
1871 if (al.commonattr & ATTR_CMN_FULLPATH) {
1872 attrlist_pack_string (&ab, fullpathptr, fullpathlen);
1873 ab.actual.commonattr |= ATTR_CMN_FULLPATH;
2d21ac55 1874 }
6d2010ae
A
1875
1876 if (al.commonattr & ATTR_CMN_ADDEDTIME) {
1877 ATTR_PACK_TIME(ab, va.va_addedtime, proc_is64);
1878 ab.actual.commonattr |= ATTR_CMN_ADDEDTIME;
1879 }
1880
b0d623f7
A
1881 /* directory attributes *********************************************/
1882 if (al.dirattr && (vtype == VDIR)) {
1883 if (al.dirattr & ATTR_DIR_LINKCOUNT) { /* full count of entries */
2d21ac55 1884 ATTR_PACK4(ab, (uint32_t)va.va_dirlinkcount);
b0d623f7
A
1885 ab.actual.dirattr |= ATTR_DIR_LINKCOUNT;
1886 }
1887 if (al.dirattr & ATTR_DIR_ENTRYCOUNT) {
2d21ac55 1888 ATTR_PACK4(ab, (uint32_t)va.va_nchildren);
b0d623f7
A
1889 ab.actual.dirattr |= ATTR_DIR_ENTRYCOUNT;
1890 }
1891 if (al.dirattr & ATTR_DIR_MOUNTSTATUS) {
6d2010ae
A
1892 uint32_t mntstat;
1893
1894 mntstat = (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0;
1895#if CONFIG_TRIGGERS
1896 /*
1897 * Report back on active vnode triggers
1898 * that can directly trigger a mount
1899 */
1900 if (vp->v_resolve &&
1901 !(vp->v_resolve->vr_flags & VNT_NO_DIRECT_MOUNT)) {
1902 mntstat |= DIR_MNTSTATUS_TRIGGER;
1903 }
1904#endif
1905 ATTR_PACK4(ab, mntstat);
b0d623f7
A
1906 ab.actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
1907 }
91447636
A
1908 }
1909
1910 /* file attributes **************************************************/
b0d623f7 1911 if (al.fileattr && (vtype != VDIR)) {
6d2010ae
A
1912
1913 size_t rsize = 0;
1914 uint64_t rlength = 0;
1915 uint64_t ralloc = 0;
1916 /*
1917 * Pre-fetch the rsrc attributes now so we only get them once.
1918 * Fetch the resource fork size/allocation via xattr interface
1919 */
1920 if (al.fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE | ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
1921 if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, ctx)) != 0) {
1922 if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)|| (error == EACCES)) {
1923 rsize = 0;
1924 error = 0;
1925 } else {
1926 goto out;
1927 }
1928 }
1929 rlength = rsize;
1930
1931 if (al.fileattr & (ATTR_FILE_RSRCALLOCSIZE | ATTR_FILE_ALLOCSIZE)) {
1932 uint32_t blksize = vp->v_mount->mnt_vfsstat.f_bsize;
1933 if (blksize == 0) {
1934 blksize = 512;
1935 }
1936 ralloc = roundup(rsize, blksize);
1937 }
1938 }
1939
b0d623f7 1940 if (al.fileattr & ATTR_FILE_LINKCOUNT) {
2d21ac55 1941 ATTR_PACK4(ab, (uint32_t)va.va_nlink);
b0d623f7
A
1942 ab.actual.fileattr |= ATTR_FILE_LINKCOUNT;
1943 }
6d2010ae
A
1944 /*
1945 * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes:
1946 * We infer that if the filesystem does not support va_data_size or va_data_alloc
1947 * it must not know about alternate forks. So when we need to gather
1948 * the total size or total alloc, it's OK to substitute the total size for
1949 * the data size below. This is because it is likely a flat filesystem and we must
1950 * be using AD files to store the rsrc fork and EAs.
1951 *
1952 * Additionally, note that getattrlist is barred from being called on
1953 * resource fork paths. (Search for CN_ALLOWRSRCFORK). So if the filesystem does
1954 * support va_data_size, it is guaranteed to represent the data fork's size. This
1955 * is an important distinction to make because when we call vnode_getattr on
1956 * an HFS resource fork vnode, to get the size, it will vend out the resource
1957 * fork's size (it only gets the size of the passed-in vnode).
1958 */
b0d623f7 1959 if (al.fileattr & ATTR_FILE_TOTALSIZE) {
6d2010ae
A
1960 uint64_t totalsize = rlength;
1961
1962 if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
1963 totalsize += va.va_data_size;
1964 } else {
1965 totalsize += va.va_total_size;
1966 }
1967
1968 ATTR_PACK8(ab, totalsize);
b0d623f7
A
1969 ab.actual.fileattr |= ATTR_FILE_TOTALSIZE;
1970 }
1971 if (al.fileattr & ATTR_FILE_ALLOCSIZE) {
6d2010ae
A
1972 uint64_t totalalloc = ralloc;
1973
1974 /*
1975 * If data_alloc is supported, then it must represent the
1976 * data fork size.
1977 */
1978 if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
1979 totalalloc += va.va_data_alloc;
1980 }
1981 else {
1982 totalalloc += va.va_total_alloc;
1983 }
1984
1985 ATTR_PACK8(ab, totalalloc);
b0d623f7
A
1986 ab.actual.fileattr |= ATTR_FILE_ALLOCSIZE;
1987 }
1988 if (al.fileattr & ATTR_FILE_IOBLOCKSIZE) {
2d21ac55 1989 ATTR_PACK4(ab, va.va_iosize);
b0d623f7
A
1990 ab.actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
1991 }
1992 if (al.fileattr & ATTR_FILE_CLUMPSIZE) {
1993 if (!return_valid || pack_invalid) {
1994 ATTR_PACK4(ab, 0); /* this value is deprecated */
1995 ab.actual.fileattr |= ATTR_FILE_CLUMPSIZE;
1996 }
1997 }
91447636 1998 if (al.fileattr & ATTR_FILE_DEVTYPE) {
b0d623f7
A
1999 uint32_t dev;
2000
91447636 2001 if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
b0d623f7
A
2002 if (vp->v_specinfo != NULL)
2003 dev = vp->v_specinfo->si_rdev;
2004 else
2005 dev = va.va_rdev;
91447636 2006 } else {
b0d623f7 2007 dev = 0;
91447636 2008 }
b0d623f7
A
2009 ATTR_PACK4(ab, dev);
2010 ab.actual.fileattr |= ATTR_FILE_DEVTYPE;
91447636 2011 }
6d2010ae
A
2012
2013 /*
2014 * If the filesystem does not support datalength
2015 * or dataallocsize, then we infer that totalsize and
2016 * totalalloc are substitutes.
2017 */
91447636
A
2018 if (al.fileattr & ATTR_FILE_DATALENGTH) {
2019 if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
2d21ac55 2020 ATTR_PACK8(ab, va.va_data_size);
91447636 2021 } else {
2d21ac55 2022 ATTR_PACK8(ab, va.va_total_size);
91447636 2023 }
b0d623f7 2024 ab.actual.fileattr |= ATTR_FILE_DATALENGTH;
91447636
A
2025 }
2026 if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
2027 if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
2d21ac55 2028 ATTR_PACK8(ab, va.va_data_alloc);
91447636 2029 } else {
2d21ac55 2030 ATTR_PACK8(ab, va.va_total_alloc);
91447636 2031 }
b0d623f7 2032 ab.actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
91447636 2033 }
6d2010ae
A
2034 /* already got the resource fork size/allocation above */
2035 if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
2036 ATTR_PACK8(ab, rlength);
2037 ab.actual.fileattr |= ATTR_FILE_RSRCLENGTH;
91447636 2038 }
6d2010ae
A
2039 if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
2040 ATTR_PACK8(ab, ralloc);
2041 ab.actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
d1ecb069 2042 }
91447636 2043 }
6d2010ae 2044
91447636 2045 /* diagnostic */
b0d623f7
A
2046 if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
2047 panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
2048 fixedsize, (long) (ab.fixedcursor - ab.base), al.commonattr, al.volattr);
2049 if (!return_valid && ab.varcursor != (ab.base + ab.needed))
2050 panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
91447636
A
2051
2052 /*
2053 * In the compatible case, we report the smaller of the required and returned sizes.
2054 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
2055 * of the result buffer, even if we copied less out. The caller knows how big a buffer
2056 * they gave us, so they can always check for truncation themselves.
2057 */
2058 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
b0d623f7
A
2059
2060 /* Return attribute set output if requested. */
2061 if (return_valid) {
2062 ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
2063 if (pack_invalid) {
2064 /* Only report the attributes that are valid */
2065 ab.actual.commonattr &= ab.valid.commonattr;
2066 ab.actual.dirattr &= ab.valid.dirattr;
2067 ab.actual.fileattr &= ab.valid.fileattr;
2068 }
2069 bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
2070 }
91447636 2071
3a60a9f5
A
2072 /* Only actually copyout as much out as the user buffer can hold */
2073 error = copyout(ab.base, uap->attributeBuffer, imin(uap->bufferSize, ab.allocated));
91447636
A
2074
2075out:
2076 if (va.va_name)
2077 kfree(va.va_name, MAXPATHLEN);
b0d623f7
A
2078 if (fullpathptr)
2079 kfree(fullpathptr, MAXPATHLEN);
91447636 2080 if (vname)
b0d623f7 2081 vnode_putname(vname);
91447636
A
2082 if (ab.base != NULL)
2083 FREE(ab.base, M_TEMP);
2084 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
2085 kauth_acl_free(va.va_acl);
2086
2087 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
2088 return(error);
2089}
2090
b0d623f7
A
2091int
2092fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
2093{
2094 struct vfs_context *ctx;
2095 vnode_t vp = NULL;
2096 int error;
2097 struct getattrlist_args ap;
2098
2099 ctx = vfs_context_current();
2100 error = 0;
2101
2102 if ((error = file_vnode(uap->fd, &vp)) != 0)
2103 return (error);
2104
2105 if ((error = vnode_getwithref(vp)) != 0) {
2106 file_drop(uap->fd);
2107 return(error);
2108 }
2109
2110 ap.path = 0;
2111 ap.alist = uap->alist;
2112 ap.attributeBuffer = uap->attributeBuffer;
2113 ap.bufferSize = uap->bufferSize;
2114 ap.options = uap->options;
2115
39236c6e
A
2116 /* Default to using the vnode's name. */
2117 error = getattrlist_internal(vp, &ap, NULL, p, ctx);
b0d623f7
A
2118
2119 file_drop(uap->fd);
2120 if (vp)
2121 vnode_put(vp);
2122
2123 return error;
2124}
2125
2126int
2127getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval)
2128{
2129 struct vfs_context *ctx;
2130 struct nameidata nd;
2131 vnode_t vp = NULL;
2132 u_long nameiflags;
2133 int error;
2134
2135 ctx = vfs_context_current();
2136 error = 0;
2137
2138 /*
2139 * Look up the file.
2140 */
2141 nameiflags = NOTRIGGER | AUDITVNPATH1;
2142 if (!(uap->options & FSOPT_NOFOLLOW))
2143 nameiflags |= FOLLOW;
6d2010ae 2144 NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
b0d623f7 2145
39236c6e
A
2146 if ((error = namei(&nd)) != 0) {
2147 /* vp is still uninitialized */
2148 return error;
2149 }
2150
b0d623f7 2151 vp = nd.ni_vp;
39236c6e
A
2152 /* Pass along our componentname to getattrlist_internal */
2153 error = getattrlist_internal(vp, uap, &(nd.ni_cnd), p, ctx);
2154
2155 /* Retain the namei reference until the getattrlist completes. */
b0d623f7 2156 nameidone(&nd);
b0d623f7
A
2157 if (vp)
2158 vnode_put(vp);
39236c6e 2159
b0d623f7
A
2160 return error;
2161}
2162
91447636
A
2163static int
2164attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
2165{
2166 /* make sure we have enough source data */
2167 if ((*cursor) + size > end)
2168 return(EINVAL);
2169
2170 bcopy(*cursor, buf, size);
2171 *cursor += size;
2172 return(0);
2173}
2174
2175#define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
2176#define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
2177#define ATTR_UNPACK_TIME(v, is64) \
2178 do { \
2179 if (is64) { \
b0d623f7 2180 struct user64_timespec us; \
91447636
A
2181 ATTR_UNPACK(us); \
2182 v.tv_sec = us.tv_sec; \
2183 v.tv_nsec = us.tv_nsec; \
2184 } else { \
b0d623f7
A
2185 struct user32_timespec us; \
2186 ATTR_UNPACK(us); \
2187 v.tv_sec = us.tv_sec; \
2188 v.tv_nsec = us.tv_nsec; \
91447636
A
2189 } \
2190 } while(0)
2191
2192
2193/*
2194 * Write attributes.
2195 */
b0d623f7
A
2196static int
2197setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_context_t ctx)
91447636
A
2198{
2199 struct attrlist al;
91447636
A
2200 struct vnode_attr va;
2201 struct attrreference ar;
91447636
A
2202 kauth_action_t action;
2203 char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
2204 int proc_is64, error;
2205 uint32_t nace;
2206 kauth_filesec_t rfsec;
2207
91447636
A
2208 user_buf = NULL;
2209 fndrinfo = NULL;
2210 volname = NULL;
2211 error = 0;
2212 proc_is64 = proc_is64bit(p);
2213 VATTR_INIT(&va);
2214
91447636
A
2215 /*
2216 * Fetch the attribute set and validate.
2217 */
2218 if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
2219 goto out;
2220 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
2221 error = EINVAL;
2222 goto out;
2223 }
2224
2225 VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
2226 vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
2227 (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
2228
2229 if (al.volattr) {
2230 if ((al.volattr & ~ATTR_VOL_SETMASK) ||
2231 (al.commonattr & ~ATTR_CMN_VOLSETMASK) ||
2232 al.fileattr ||
2233 al.forkattr) {
2234 error = EINVAL;
2235 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
2236 goto out;
2237 }
2238 } else {
2239 if ((al.commonattr & ~ATTR_CMN_SETMASK) ||
2240 (al.fileattr & ~ATTR_FILE_SETMASK) ||
2241 (al.dirattr & ~ATTR_DIR_SETMASK) ||
2242 (al.forkattr & ~ATTR_FORK_SETMASK)) {
2243 error = EINVAL;
2244 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
2245 goto out;
2246 }
2247 }
2248
316670eb
A
2249 /*
2250 * If the caller's bitmaps indicate that there are no attributes to set,
2251 * then exit early. In particular, we want to avoid the MALLOC below
2252 * since the caller's bufferSize could be zero, and MALLOC of zero bytes
2253 * returns a NULL pointer, which would cause setattrlist to return ENOMEM.
2254 */
2255 if (al.commonattr == 0 &&
2256 (al.volattr & ~ATTR_VOL_INFO) == 0 &&
2257 al.dirattr == 0 &&
2258 al.fileattr == 0 &&
2259 al.forkattr == 0) {
2260 error = 0;
2261 goto out;
2262 }
2263
91447636
A
2264 /*
2265 * Make the naive assumption that the caller has supplied a reasonable buffer
2266 * size. We could be more careful by pulling in the fixed-size region, checking
2267 * the attrref structures, then pulling in the variable section.
2268 * We need to reconsider this for handling large ACLs, as they should probably be
2269 * brought directly into a buffer. Multiple copyins will make this slower though.
2270 *
2271 * We could also map the user buffer if it is larger than some sensible mimimum.
2272 */
2273 if (uap->bufferSize > ATTR_MAX_BUFFER) {
2274 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large", uap->bufferSize);
2275 error = ENOMEM;
2276 goto out;
2277 }
2278 MALLOC(user_buf, char *, uap->bufferSize, M_TEMP, M_WAITOK);
2279 if (user_buf == NULL) {
2280 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
2281 error = ENOMEM;
2282 goto out;
2283 }
2284 if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) {
2285 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed");
2286 goto out;
2287 }
2288 VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
2289
2d21ac55 2290#if CONFIG_MACF
b0d623f7 2291 error = mac_vnode_check_setattrlist(ctx, vp, &al);
2d21ac55
A
2292 if (error)
2293 goto out;
2294#endif /* MAC */
2295
91447636
A
2296 /*
2297 * Unpack the argument buffer.
2298 */
2299 cursor = user_buf;
2300 bufend = cursor + uap->bufferSize;
2301
2302 /* common */
2303 if (al.commonattr & ATTR_CMN_SCRIPT) {
2304 ATTR_UNPACK(va.va_encoding);
2305 VATTR_SET_ACTIVE(&va, va_encoding);
2306 }
2307 if (al.commonattr & ATTR_CMN_CRTIME) {
2308 ATTR_UNPACK_TIME(va.va_create_time, proc_is64);
2309 VATTR_SET_ACTIVE(&va, va_create_time);
2310 }
2311 if (al.commonattr & ATTR_CMN_MODTIME) {
2312 ATTR_UNPACK_TIME(va.va_modify_time, proc_is64);
2313 VATTR_SET_ACTIVE(&va, va_modify_time);
2314 }
2315 if (al.commonattr & ATTR_CMN_CHGTIME) {
2316 ATTR_UNPACK_TIME(va.va_change_time, proc_is64);
2317 VATTR_SET_ACTIVE(&va, va_change_time);
2318 }
2319 if (al.commonattr & ATTR_CMN_ACCTIME) {
2320 ATTR_UNPACK_TIME(va.va_access_time, proc_is64);
2321 VATTR_SET_ACTIVE(&va, va_access_time);
2322 }
2323 if (al.commonattr & ATTR_CMN_BKUPTIME) {
2324 ATTR_UNPACK_TIME(va.va_backup_time, proc_is64);
2325 VATTR_SET_ACTIVE(&va, va_backup_time);
2326 }
2327 if (al.commonattr & ATTR_CMN_FNDRINFO) {
2328 if ((cursor + 32) > bufend) {
2329 error = EINVAL;
2330 VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO");
2331 goto out;
2332 }
2333 fndrinfo = cursor;
2334 cursor += 32;
2335 }
2336 if (al.commonattr & ATTR_CMN_OWNERID) {
2337 ATTR_UNPACK(va.va_uid);
2338 VATTR_SET_ACTIVE(&va, va_uid);
2339 }
2340 if (al.commonattr & ATTR_CMN_GRPID) {
2341 ATTR_UNPACK(va.va_gid);
2342 VATTR_SET_ACTIVE(&va, va_gid);
2343 }
2344 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
2345 ATTR_UNPACK_CAST(uint32_t, va.va_mode);
2346 VATTR_SET_ACTIVE(&va, va_mode);
2347 }
2348 if (al.commonattr & ATTR_CMN_FLAGS) {
2349 ATTR_UNPACK(va.va_flags);
2350 VATTR_SET_ACTIVE(&va, va_flags);
2351 }
2352 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
2353
2354 /*
2355 * We are (for now) passed a kauth_filesec_t, but all we want from
2356 * it is the ACL.
2357 */
2358 cp = cursor;
2359 ATTR_UNPACK(ar);
39236c6e
A
2360 if (ar.attr_dataoffset < 0) {
2361 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied", ar.attr_dataoffset);
2362 error = EINVAL;
2363 goto out;
2364 }
2365
91447636
A
2366 cp += ar.attr_dataoffset;
2367 rfsec = (kauth_filesec_t)cp;
b0d623f7 2368 if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) || /* no space for acl */
91447636
A
2369 (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) || /* bad magic */
2370 (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
2371 ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */
2372 error = EINVAL;
2373 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
2374 goto out;
2375 }
2376 nace = rfsec->fsec_entrycount;
2377 if (nace == KAUTH_FILESEC_NOACL)
2378 nace = 0;
2379 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
2380 error = EINVAL;
2381 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
2382 goto out;
2383 }
2384 nace = rfsec->fsec_acl.acl_entrycount;
2385 if (nace == KAUTH_FILESEC_NOACL) {
2386 /* deleting ACL */
2387 VATTR_SET(&va, va_acl, NULL);
2388 } else {
2389
2390 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
2391 error = EINVAL;
2392 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
2393 goto out;
2394 }
2395 VATTR_SET(&va, va_acl, &rfsec->fsec_acl);
2396 }
2397 }
2398 if (al.commonattr & ATTR_CMN_UUID) {
2399 ATTR_UNPACK(va.va_uuuid);
2400 VATTR_SET_ACTIVE(&va, va_uuuid);
2401 }
2402 if (al.commonattr & ATTR_CMN_GRPUUID) {
2403 ATTR_UNPACK(va.va_guuid);
2404 VATTR_SET_ACTIVE(&va, va_guuid);
2405 }
2406
2407 /* volume */
2408 if (al.volattr & ATTR_VOL_INFO) {
2409 if (al.volattr & ATTR_VOL_NAME) {
2410 volname = cursor;
39236c6e
A
2411 ATTR_UNPACK(ar);
2412 /* attr_length cannot be 0! */
2413 if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0)) {
2414 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) ", ar.attr_dataoffset);
2415 error = EINVAL;
2416 goto out;
2417 }
2418
91447636
A
2419 volname += ar.attr_dataoffset;
2420 if ((volname + ar.attr_length) > bufend) {
2421 error = EINVAL;
2422 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
2423 goto out;
2424 }
2425 /* guarantee NUL termination */
2426 volname[ar.attr_length - 1] = 0;
2427 }
2428 }
2429
2430 /* file */
2431 if (al.fileattr & ATTR_FILE_DEVTYPE) {
2432 /* XXX does it actually make any sense to change this? */
2433 error = EINVAL;
2434 VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented");
2435 goto out;
2436 }
2437
2438 /*
2439 * Validate and authorize.
2440 */
2441 action = 0;
b0d623f7 2442 if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, ctx)) != 0)) {
91447636
A
2443 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
2444 goto out;
2445 }
2446 /*
2447 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
2448 * and will be auth'ed by the FS.
2449 */
2450 if (fndrinfo != NULL) {
2451 if (al.volattr & ATTR_VOL_INFO) {
2452 if (vp->v_tag != VT_HFS) {
2453 error = EINVAL;
2454 goto out;
2455 }
2456 } else {
b0d623f7 2457 action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
91447636
A
2458 }
2459 }
2460
b0d623f7 2461 if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) {
91447636
A
2462 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
2463 goto out;
2464 }
2465
8f6c56a5
A
2466 /*
2467 * When we're setting both the access mask and the finder info, then
2468 * check if were about to remove write access for the owner. Since
2469 * vnode_setattr and vn_setxattr invoke two separate vnops, we need
2470 * to consider their ordering.
2471 *
2472 * If were about to remove write access for the owner we'll set the
2473 * Finder Info here before vnode_setattr. Otherwise we'll set it
2474 * after vnode_setattr since it may be adding owner write access.
2475 */
2476 if ((fndrinfo != NULL) && !(al.volattr & ATTR_VOL_INFO) &&
2477 (al.commonattr & ATTR_CMN_ACCESSMASK) && !(va.va_mode & S_IWUSR)) {
2d21ac55 2478 if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
8f6c56a5
A
2479 goto out;
2480 }
2481 fndrinfo = NULL; /* it was set here so skip setting below */
2482 }
2483
91447636
A
2484 /*
2485 * Write the attributes if we have any.
2486 */
b0d623f7 2487 if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, ctx)) != 0)) {
91447636
A
2488 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
2489 goto out;
2490 }
2491
2492 /*
2493 * Write the Finder Info if we have any.
2494 */
2495 if (fndrinfo != NULL) {
2496 if (al.volattr & ATTR_VOL_INFO) {
2497 if (vp->v_tag == VT_HFS) {
b0d623f7 2498 error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx);
91447636
A
2499 if (error != 0)
2500 goto out;
2501 } else {
2502 /* XXX should never get here */
2503 }
2d21ac55 2504 } else if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
8f6c56a5 2505 goto out;
91447636
A
2506 }
2507 }
2508
2509 /*
2510 * Set the volume name, if we have one
2511 */
2512 if (volname != NULL)
2513 {
2514 struct vfs_attr vs;
2515
2516 VFSATTR_INIT(&vs);
2517
2518 vs.f_vol_name = volname; /* References the setattrlist buffer directly */
2519 VFSATTR_WANTED(&vs, f_vol_name);
2520
2d21ac55
A
2521#if CONFIG_MACF
2522 error = mac_mount_check_setattr(ctx, vp->v_mount, &vs);
2523 if (error != 0)
2524 goto out;
2525#endif
2526
91447636
A
2527 if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
2528 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
2529 goto out;
2530 }
2531
2532 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
2533 error = EINVAL;
2534 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
2535 goto out;
2536 }
2537 }
2538
2539 /* all done and successful */
2540
2541out:
91447636
A
2542 if (user_buf != NULL)
2543 FREE(user_buf, M_TEMP);
2544 VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
2545 return(error);
2546}
b0d623f7
A
2547
2548int
2549setattrlist(proc_t p, struct setattrlist_args *uap, __unused int32_t *retval)
2550{
2551 struct vfs_context *ctx;
2552 struct nameidata nd;
2553 vnode_t vp = NULL;
2554 u_long nameiflags;
2555 int error = 0;
2556
2557 ctx = vfs_context_current();
2558
2559 /*
2560 * Look up the file.
2561 */
6d2010ae 2562 nameiflags = AUDITVNPATH1;
b0d623f7
A
2563 if ((uap->options & FSOPT_NOFOLLOW) == 0)
2564 nameiflags |= FOLLOW;
6d2010ae 2565 NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
b0d623f7
A
2566 if ((error = namei(&nd)) != 0)
2567 goto out;
2568 vp = nd.ni_vp;
2569 nameidone(&nd);
2570
2571 error = setattrlist_internal(vp, uap, p, ctx);
2572out:
2573 if (vp != NULL)
2574 vnode_put(vp);
2575 return error;
2576}
2577
2578int
2579fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
2580{
2581 struct vfs_context *ctx;
2582 vnode_t vp = NULL;
2583 int error;
2584 struct setattrlist_args ap;
2585
2586 ctx = vfs_context_current();
2587
2588 if ((error = file_vnode(uap->fd, &vp)) != 0)
2589 return (error);
2590
2591 if ((error = vnode_getwithref(vp)) != 0) {
2592 file_drop(uap->fd);
2593 return(error);
2594 }
2595
2596 ap.path = 0;
2597 ap.alist = uap->alist;
2598 ap.attributeBuffer = uap->attributeBuffer;
2599 ap.bufferSize = uap->bufferSize;
2600 ap.options = uap->options;
2601
2602 error = setattrlist_internal(vp, &ap, p, ctx);
2603 file_drop(uap->fd);
2604 if (vp != NULL)
2605 vnode_put(vp);
2606
2607 return error;
2608}
2609