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