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