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