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