]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_attrlist.c
xnu-1228.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
1 /*
2 * Copyright (c) 1995-2007 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 };
70
71
72 /*
73 * Pack (count) bytes from (source) into (buf).
74 */
75 static void
76 attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
77 {
78 ssize_t fit;
79
80 /* how much room left in the buffer? */
81 fit = imin(count, ab->allocated - (ab->fixedcursor - ab->base));
82 if (fit > 0)
83 bcopy(source, ab->fixedcursor, fit);
84
85 /* always move in increments of 4 */
86 ab->fixedcursor += roundup(count, 4);
87 }
88 static void
89 attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count, const void *ext, ssize_t extcount)
90 {
91 struct attrreference ar;
92 ssize_t fit;
93
94 /* pack the reference to the variable object */
95 ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
96 ar.attr_length = count + extcount;
97 attrlist_pack_fixed(ab, &ar, sizeof(ar));
98
99 /* calculate space and pack the variable object */
100 fit = imin(count, ab->allocated - (ab->varcursor - ab->base));
101 if (fit > 0) {
102 if (source != NULL)
103 bcopy(source, ab->varcursor, fit);
104 ab->varcursor += fit;
105 }
106 fit = imin(extcount, ab->allocated - (ab->varcursor - ab->base));
107 if (fit > 0) {
108 if (ext != NULL)
109 bcopy(ext, ab->varcursor, fit);
110 ab->varcursor += fit;
111 }
112 /* always move in increments of 4 */
113 ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4);
114 }
115 static void
116 attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
117 {
118 attrlist_pack_variable2(ab, source, count, NULL, 0);
119 }
120 static void
121 attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count)
122 {
123 struct attrreference ar;
124 ssize_t fit, space;
125
126
127 /*
128 * Supplied count is character count of string text, excluding trailing nul
129 * which we always supply here.
130 */
131 if (source == NULL) {
132 count = 0;
133 } else if (count == 0) {
134 count = strlen(source);
135 }
136
137 /*
138 * Make the reference and pack it.
139 * Note that this is entirely independent of how much we get into
140 * the buffer.
141 */
142 ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
143 ar.attr_length = count + 1;
144 attrlist_pack_fixed(ab, &ar, sizeof(ar));
145
146 /* calculate how much of the string text we can copy, and do that */
147 space = ab->allocated - (ab->varcursor - ab->base);
148 fit = imin(count, space);
149 if (fit > 0)
150 bcopy(source, ab->varcursor, fit);
151 /* is there room for our trailing nul? */
152 if (space > fit)
153 ab->varcursor[fit] = '\0';
154
155 /* always move in increments of 4 */
156 ab->varcursor += roundup(count + 1, 4);
157 }
158
159 #define ATTR_PACK4(AB, V) \
160 do { \
161 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) { \
162 *(uint32_t *)AB.fixedcursor = V; \
163 AB.fixedcursor += 4; \
164 } \
165 } while (0)
166
167 #define ATTR_PACK8(AB, V) \
168 do { \
169 if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \
170 *(uint64_t *)AB.fixedcursor = *(uint64_t *)&V; \
171 AB.fixedcursor += 8; \
172 } \
173 } while (0)
174
175 #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
176 #define ATTR_PACK_CAST(b, t, v) \
177 do { \
178 t _f = (t)v; \
179 ATTR_PACK(b, _f); \
180 } while (0)
181
182 #define ATTR_PACK_TIME(b, v, is64) \
183 do { \
184 if (is64) { \
185 struct user_timespec us = {v.tv_sec, v.tv_nsec}; \
186 ATTR_PACK(&b, us); \
187 } else { \
188 ATTR_PACK8(b, v); \
189 } \
190 } while(0)
191
192
193 /*
194 * Table-driven setup for all valid common/volume attributes.
195 */
196 struct getvolattrlist_attrtab {
197 attrgroup_t attr;
198 uint64_t bits;
199 #define VFSATTR_BIT(b) (VFSATTR_ ## b)
200 ssize_t size;
201 };
202 static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = {
203 {ATTR_CMN_NAME, 0, sizeof(struct attrreference)},
204 {ATTR_CMN_DEVID, 0, sizeof(dev_t)},
205 {ATTR_CMN_FSID, 0, sizeof(fsid_t)},
206 {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t)},
207 {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t)},
208 {ATTR_CMN_OBJID, 0, sizeof(fsobj_id_t)},
209 {ATTR_CMN_OBJPERMANENTID, 0, sizeof(fsobj_id_t)},
210 {ATTR_CMN_PAROBJID, 0, sizeof(fsobj_id_t)},
211 {ATTR_CMN_SCRIPT, 0, sizeof(text_encoding_t)},
212 {ATTR_CMN_CRTIME, VFSATTR_BIT(f_create_time), ATTR_TIME_SIZE},
213 {ATTR_CMN_MODTIME, VFSATTR_BIT(f_modify_time), ATTR_TIME_SIZE},
214 {ATTR_CMN_CHGTIME, VFSATTR_BIT(f_modify_time), ATTR_TIME_SIZE},
215 {ATTR_CMN_ACCTIME, VFSATTR_BIT(f_access_time), ATTR_TIME_SIZE},
216 {ATTR_CMN_BKUPTIME, VFSATTR_BIT(f_backup_time), ATTR_TIME_SIZE},
217 {ATTR_CMN_FNDRINFO, 0, 32},
218 {ATTR_CMN_OWNERID, 0, sizeof(uid_t)},
219 {ATTR_CMN_GRPID, 0, sizeof(gid_t)},
220 {ATTR_CMN_ACCESSMASK, 0, sizeof(uint32_t)},
221 {ATTR_CMN_FLAGS, 0, sizeof(uint32_t)},
222 {ATTR_CMN_USERACCESS, 0, sizeof(uint32_t)},
223 {0, 0, 0}
224 };
225
226 static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
227 {ATTR_VOL_FSTYPE, 0, sizeof(uint32_t)},
228 {ATTR_VOL_SIGNATURE, VFSATTR_BIT(f_signature), sizeof(uint32_t)},
229 {ATTR_VOL_SIZE, VFSATTR_BIT(f_blocks), sizeof(off_t)},
230 {ATTR_VOL_SPACEFREE, VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
231 {ATTR_VOL_SPACEAVAIL, VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
232 {ATTR_VOL_MINALLOCATION, VFSATTR_BIT(f_bsize), sizeof(off_t)},
233 {ATTR_VOL_ALLOCATIONCLUMP, VFSATTR_BIT(f_bsize), sizeof(off_t)},
234 {ATTR_VOL_IOBLOCKSIZE, VFSATTR_BIT(f_iosize), sizeof(uint32_t)},
235 {ATTR_VOL_OBJCOUNT, VFSATTR_BIT(f_objcount), sizeof(uint32_t)},
236 {ATTR_VOL_FILECOUNT, VFSATTR_BIT(f_filecount), sizeof(uint32_t)},
237 {ATTR_VOL_DIRCOUNT, VFSATTR_BIT(f_dircount), sizeof(uint32_t)},
238 {ATTR_VOL_MAXOBJCOUNT, VFSATTR_BIT(f_maxobjcount), sizeof(uint32_t)},
239 {ATTR_VOL_MOUNTPOINT, 0, sizeof(struct attrreference)},
240 {ATTR_VOL_NAME, VFSATTR_BIT(f_vol_name), sizeof(struct attrreference)},
241 {ATTR_VOL_MOUNTFLAGS, 0, sizeof(uint32_t)},
242 {ATTR_VOL_MOUNTEDDEVICE, 0, sizeof(struct attrreference)},
243 {ATTR_VOL_ENCODINGSUSED, 0, sizeof(uint64_t)},
244 {ATTR_VOL_CAPABILITIES, VFSATTR_BIT(f_capabilities), sizeof(vol_capabilities_attr_t)},
245 {ATTR_VOL_ATTRIBUTES, VFSATTR_BIT(f_attributes), sizeof(vol_attributes_attr_t)},
246 {ATTR_VOL_INFO, 0, 0},
247 {0, 0, 0}
248 };
249
250 static int
251 getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, struct vfs_attr *vsp,
252 ssize_t *sizep, int is_64bit)
253 {
254 attrgroup_t recognised;
255
256 recognised = 0;
257 do {
258 /* is this attribute set? */
259 if (tab->attr & attrs) {
260 recognised |= tab->attr;
261 vsp->f_active |= tab->bits;
262 if (tab->size == ATTR_TIME_SIZE) {
263 if (is_64bit) {
264 *sizep += sizeof(struct user_timespec);
265 } else {
266 *sizep += sizeof(struct timespec);
267 }
268 } else {
269 *sizep += tab->size;
270 }
271 }
272 } while ((++tab)->attr != 0);
273
274 /* check to make sure that we recognised all of the passed-in attributes */
275 if (attrs & ~recognised)
276 return(EINVAL);
277 return(0);
278 }
279
280 /*
281 * Given the attributes listed in alp, configure vap to request
282 * the data from a filesystem.
283 */
284 static int
285 getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
286 {
287 int error;
288
289 /*
290 * Parse the above tables.
291 */
292 *sizep = sizeof(uint32_t); /* length count */
293 if (alp->commonattr &&
294 (error = getvolattrlist_parsetab(getvolattrlist_common_tab, alp->commonattr, vsp, sizep, is_64bit)) != 0)
295 return(error);
296 if (alp->volattr &&
297 (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
298 return(error);
299
300 return(0);
301 }
302
303 /*
304 * Table-driven setup for all valid common/dir/file/fork attributes against files.
305 */
306 struct getattrlist_attrtab {
307 attrgroup_t attr;
308 uint64_t bits;
309 #define VATTR_BIT(b) (VNODE_ATTR_ ## b)
310 ssize_t size;
311 kauth_action_t action;
312 };
313 static struct getattrlist_attrtab getattrlist_common_tab[] = {
314 {ATTR_CMN_NAME, VATTR_BIT(va_name), sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES},
315 {ATTR_CMN_DEVID, 0, sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
316 {ATTR_CMN_FSID, VATTR_BIT(va_fsid), sizeof(fsid_t), KAUTH_VNODE_READ_ATTRIBUTES},
317 {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t), KAUTH_VNODE_READ_ATTRIBUTES},
318 {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t), KAUTH_VNODE_READ_ATTRIBUTES},
319 {ATTR_CMN_OBJID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
320 {ATTR_CMN_OBJPERMANENTID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
321 {ATTR_CMN_PAROBJID, VATTR_BIT(va_parentid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
322 {ATTR_CMN_SCRIPT, VATTR_BIT(va_encoding), sizeof(text_encoding_t), KAUTH_VNODE_READ_ATTRIBUTES},
323 {ATTR_CMN_CRTIME, VATTR_BIT(va_create_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
324 {ATTR_CMN_MODTIME, VATTR_BIT(va_modify_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
325 {ATTR_CMN_CHGTIME, VATTR_BIT(va_change_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
326 {ATTR_CMN_ACCTIME, VATTR_BIT(va_access_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
327 {ATTR_CMN_BKUPTIME, VATTR_BIT(va_backup_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
328 {ATTR_CMN_FNDRINFO, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES},
329 {ATTR_CMN_OWNERID, VATTR_BIT(va_uid), sizeof(uid_t), KAUTH_VNODE_READ_ATTRIBUTES},
330 {ATTR_CMN_GRPID, VATTR_BIT(va_gid), sizeof(gid_t), KAUTH_VNODE_READ_ATTRIBUTES},
331 {ATTR_CMN_ACCESSMASK, VATTR_BIT(va_mode), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
332 {ATTR_CMN_FLAGS, VATTR_BIT(va_flags), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
333 {ATTR_CMN_USERACCESS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
334 {ATTR_CMN_EXTENDED_SECURITY, VATTR_BIT(va_acl), sizeof(struct attrreference), KAUTH_VNODE_READ_SECURITY},
335 {ATTR_CMN_UUID, VATTR_BIT(va_uuuid), sizeof(guid_t), KAUTH_VNODE_READ_ATTRIBUTES},
336 {ATTR_CMN_GRPUUID, VATTR_BIT(va_guuid), sizeof(guid_t), KAUTH_VNODE_READ_ATTRIBUTES},
337 {ATTR_CMN_FILEID, VATTR_BIT(va_fileid), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES},
338 {ATTR_CMN_PARENTID, VATTR_BIT(va_parentid), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES},
339 {0, 0, 0, 0}
340 };
341 static struct getattrlist_attrtab getattrlist_dir_tab[] = {
342 {ATTR_DIR_LINKCOUNT, VATTR_BIT(va_dirlinkcount), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
343 {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nchildren), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
344 /* ATTR_DIR_ENTRYCOUNT falls back to va_nlink-2 if va_nchildren isn't supported, so request va_nlink just in case */
345 {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nlink), 0, KAUTH_VNODE_READ_ATTRIBUTES},
346 {ATTR_DIR_MOUNTSTATUS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
347 {0, 0, 0, 0}
348 };
349 static struct getattrlist_attrtab getattrlist_file_tab[] = {
350 {ATTR_FILE_LINKCOUNT, VATTR_BIT(va_nlink), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
351 {ATTR_FILE_TOTALSIZE, VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
352 {ATTR_FILE_ALLOCSIZE, VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
353 {ATTR_FILE_IOBLOCKSIZE, VATTR_BIT(va_iosize), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
354 {ATTR_FILE_DEVTYPE, VATTR_BIT(va_rdev), sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
355 {ATTR_FILE_DATALENGTH, VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
356 {ATTR_FILE_DATAALLOCSIZE, VATTR_BIT(va_total_alloc)| VATTR_BIT(va_data_alloc), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
357 {ATTR_FILE_RSRCLENGTH, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
358 {ATTR_FILE_RSRCALLOCSIZE, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
359 {0, 0, 0, 0}
360 };
361
362 /*
363 * The following are attributes that VFS can derive.
364 *
365 * A majority of them are the same attributes that are required for stat(2) and statfs(2).
366 */
367 #define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | \
368 ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | \
369 ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \
370 ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
371 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
372 ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \
373 ATTR_VOL_ATTRIBUTES)
374
375 #define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \
376 ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
377 ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \
378 ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \
379 ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
380 ATTR_CMN_ACCTIME | ATTR_CMN_FNDRINFO | \
381 ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
382 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
383 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
384 ATTR_CMN_PARENTID)
385
386 #define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
387
388 #define VFS_DFLT_ATTR_FILE (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | \
389 ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \
390 ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \
391 ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \
392 ATTR_FILE_RSRCALLOCSIZE)
393
394 static int
395 getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs, struct vnode_attr *vap,
396 ssize_t *sizep, kauth_action_t *actionp, int is_64bit)
397 {
398 attrgroup_t recognised;
399
400 recognised = 0;
401 do {
402 /* is this attribute set? */
403 if (tab->attr & attrs) {
404 recognised |= tab->attr;
405 vap->va_active |= tab->bits;
406 if (tab->size == ATTR_TIME_SIZE) {
407 if (is_64bit) {
408 *sizep += sizeof(struct user_timespec);
409 } else {
410 *sizep += sizeof(struct timespec);
411 }
412 } else {
413 *sizep += tab->size;
414 }
415 *actionp |= tab->action;
416 }
417 } while ((++tab)->attr != 0);
418
419 /* check to make sure that we recognised all of the passed-in attributes */
420 if (attrs & ~recognised)
421 return(EINVAL);
422 return(0);
423 }
424
425 /*
426 * Given the attributes listed in alp, configure vap to request
427 * the data from a filesystem.
428 */
429 static int
430 getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir)
431 {
432 int error;
433
434 /*
435 * Parse the above tables.
436 */
437 *sizep = sizeof(uint32_t); /* length count */
438 *actionp = 0;
439 if (alp->commonattr &&
440 (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit)) != 0)
441 return(error);
442 if (isdir && alp->dirattr &&
443 (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit)) != 0)
444 return(error);
445 if (!isdir && alp->fileattr &&
446 (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit)) != 0)
447 return(error);
448
449 return(0);
450 }
451
452 static int
453 setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
454 {
455 uio_t auio;
456 char uio_buf[UIO_SIZEOF(1)];
457 int error;
458
459 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
460 error = ENOMEM;
461 } else {
462 uio_addiov(auio, CAST_USER_ADDR_T(fndrinfo), 32);
463 error = vn_setxattr(vp, XATTR_FINDERINFO_NAME, auio, XATTR_NOSECURITY, ctx);
464 uio_free(auio);
465 }
466
467 #if CONFIG_FSE
468 if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) {
469 add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
470 }
471 #endif
472 return (error);
473 }
474
475
476 /*
477 * Find something resembling a terminal component name in the mountedonname for vp
478 *
479 */
480 static void
481 getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
482 {
483 int counting;
484 const char *cp;
485
486 /*
487 * We're looking for the last sequence of non / characters, but
488 * not including any trailing / characters.
489 */
490 *np = NULL;
491 *nl = 0;
492 counting = 0;
493 for (cp = mn; *cp != 0; cp++) {
494 if (!counting) {
495 /* start of run of chars */
496 if (*cp != '/') {
497 *np = cp;
498 counting = 1;
499 }
500 } else {
501 /* end of run of chars */
502 if (*cp == '/') {
503 *nl = cp - *np;
504 counting = 0;
505 }
506 }
507 }
508 /* need to close run? */
509 if (counting)
510 *nl = cp - *np;
511 }
512
513
514 static int
515 getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp, vfs_context_t ctx, int is_64bit)
516 {
517 struct vfs_attr vs;
518 struct vnode_attr va;
519 struct _attrlist_buf ab;
520 int error;
521 ssize_t fixedsize, varsize;
522 const char *cnp = NULL; /* protected by ATTR_CMN_NAME */
523 ssize_t cnl = 0; /* protected by ATTR_CMN_NAME */
524 mount_t mnt;
525
526 ab.base = NULL;
527 VATTR_INIT(&va);
528 VFSATTR_INIT(&vs);
529 vs.f_vol_name = NULL;
530 mnt = vp->v_mount;
531
532
533 /*
534 * For now, the vnode must be the root of its filesystem.
535 * To relax this, we need to be able to find the root vnode of a filesystem
536 * from any vnode in the filesystem.
537 */
538 if (!vnode_isvroot(vp)) {
539 error = EINVAL;
540 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
541 goto out;
542 }
543
544 /*
545 * Set up the vfs_attr structure and call the filesystem.
546 */
547 if ((error = getvolattrlist_setupvfsattr(alp, &vs, &fixedsize, is_64bit)) != 0) {
548 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
549 goto out;
550 }
551 if (vs.f_active != 0) {
552 /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
553 if (VFSATTR_IS_ACTIVE(&vs, f_vol_name)) {
554 vs.f_vol_name = (char *) kalloc(MAXPATHLEN);
555 if (vs.f_vol_name == NULL) {
556 error = ENOMEM;
557 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
558 goto out;
559 }
560 }
561
562 #if CONFIG_MACF
563 error = mac_mount_check_getattr(ctx, mnt, &vs);
564 if (error != 0)
565 goto out;
566 #endif
567 VFS_DEBUG(ctx, vp, "ATTRLIST - calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported);
568 if ((error = vfs_getattr(mnt, &vs, ctx)) != 0) {
569 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
570 goto out;
571 }
572
573 /*
574 * Did we ask for something the filesystem doesn't support?
575 */
576 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
577 /* default value for volume subtype */
578 if (VFSATTR_IS_ACTIVE(&vs, f_fssubtype)
579 && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype))
580 VFSATTR_RETURN(&vs, f_fssubtype, 0);
581
582 /*
583 * If the file system didn't supply f_signature, then
584 * default it to 'BD', which is the generic signature
585 * that most Carbon file systems should return.
586 */
587 if (VFSATTR_IS_ACTIVE(&vs, f_signature)
588 && !VFSATTR_IS_SUPPORTED(&vs, f_signature))
589 VFSATTR_RETURN(&vs, f_signature, 0x4244);
590
591 /* default for block size */
592 if (VFSATTR_IS_ACTIVE(&vs, f_bsize)
593 && !VFSATTR_IS_SUPPORTED(&vs, f_bsize))
594 VFSATTR_RETURN(&vs, f_bsize, mnt->mnt_devblocksize);
595
596 /* default value for volume f_attributes */
597 if (VFSATTR_IS_ACTIVE(&vs, f_attributes)
598 && !VFSATTR_IS_SUPPORTED(&vs, f_attributes)) {
599 vol_attributes_attr_t *attrp = &vs.f_attributes;
600
601 attrp->validattr.commonattr = VFS_DFLT_ATTR_CMN;
602 attrp->validattr.volattr = VFS_DFLT_ATTR_VOL;
603 attrp->validattr.dirattr = VFS_DFLT_ATTR_DIR;
604 attrp->validattr.fileattr = VFS_DFLT_ATTR_FILE;
605 attrp->validattr.forkattr = 0;
606
607 attrp->nativeattr.commonattr = 0;
608 attrp->nativeattr.volattr = 0;
609 attrp->nativeattr.dirattr = 0;
610 attrp->nativeattr.fileattr = 0;
611 attrp->nativeattr.forkattr = 0;
612 VFSATTR_SET_SUPPORTED(&vs, f_attributes);
613 }
614
615 /* default value for volume f_capabilities */
616 if (VFSATTR_IS_ACTIVE(&vs, f_capabilities)) {
617 /* getattrlist is always supported now. */
618 if (!VFSATTR_IS_SUPPORTED(&vs, f_capabilities)) {
619 vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = 0;
620 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
621 vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
622 vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
623
624 vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0;
625 vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
626 vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
627 vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
628 VFSATTR_SET_SUPPORTED(&vs, f_capabilities);
629 }
630 else {
631 /* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
632 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
633 vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
634 }
635 }
636
637 /* check to see if our fixups were enough */
638 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
639 error = EINVAL;
640 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not get all requested volume attributes");
641 VFS_DEBUG(ctx, vp, "ATTRLIST - wanted %016llx got %016llx missing %016llx",
642 vs.f_active, vs.f_supported, vs.f_active & ~vs.f_supported);
643 goto out;
644 }
645 }
646 }
647
648 /*
649 * Some fields require data from the root vp
650 */
651 if (alp->commonattr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | ATTR_CMN_SCRIPT)) {
652 VATTR_WANTED(&va, va_uid);
653 VATTR_WANTED(&va, va_gid);
654 VATTR_WANTED(&va, va_mode);
655 VATTR_WANTED(&va, va_flags);
656 VATTR_WANTED(&va, va_encoding);
657
658 if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
659 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp);
660 goto out;
661 }
662
663 if (VATTR_IS_ACTIVE(&va, va_encoding) && !VATTR_IS_SUPPORTED(&va, va_encoding))
664 VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
665 }
666
667 /*
668 * Compute variable-size buffer requirements.
669 */
670 varsize = 0;
671 if (alp->commonattr & ATTR_CMN_NAME) {
672 if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
673 vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
674 /* special case for boot volume. Use root name when it's
675 * available (which is the volume name) or just the mount on
676 * name of "/". we must do this for binary compatibility with
677 * pre Tiger code. returning nothing for the boot volume name
678 * breaks installers - 3961058
679 */
680 cnp = vnode_getname(vp);
681 if (cnp == NULL) {
682 /* just use "/" as name */
683 cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
684 }
685 cnl = strlen(cnp);
686 }
687 else {
688 getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
689 }
690 if (alp->commonattr & ATTR_CMN_NAME)
691 varsize += roundup(cnl + 1, 4);
692 }
693 if (alp->volattr & ATTR_VOL_MOUNTPOINT)
694 varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntonname) + 1, 4);
695 if (alp->volattr & ATTR_VOL_NAME) {
696 vs.f_vol_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
697 varsize += roundup(strlen(vs.f_vol_name) + 1, 4);
698 }
699 if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
700 varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4);
701
702 /*
703 * Allocate a target buffer for attribute results.
704 * Note that since we won't ever copy out more than the caller requested,
705 * we never need to allocate more than they offer.
706 */
707 ab.allocated = imin(uap->bufferSize, fixedsize + varsize);
708 if (ab.allocated > ATTR_MAX_BUFFER) {
709 error = ENOMEM;
710 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
711 goto out;
712 }
713 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
714 if (ab.base == NULL) {
715 error = ENOMEM;
716 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
717 goto out;
718 }
719
720 /*
721 * Pack results into the destination buffer.
722 */
723 ab.fixedcursor = ab.base + sizeof(uint32_t);
724 ab.varcursor = ab.base + fixedsize;
725 ab.needed = fixedsize + varsize;
726
727 /* common attributes **************************************************/
728 if (alp->commonattr & ATTR_CMN_NAME)
729 attrlist_pack_string(&ab, cnp, cnl);
730 if (alp->commonattr & ATTR_CMN_DEVID)
731 ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]);
732 if (alp->commonattr & ATTR_CMN_FSID)
733 ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid);
734 if (alp->commonattr & ATTR_CMN_OBJTYPE)
735 ATTR_PACK4(ab, 0);
736 if (alp->commonattr & ATTR_CMN_OBJTAG)
737 ATTR_PACK4(ab, vp->v_tag);
738 if (alp->commonattr & ATTR_CMN_OBJID) {
739 fsobj_id_t f = {0, 0};
740 ATTR_PACK8(ab, f);
741 }
742 if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
743 fsobj_id_t f = {0, 0};
744 ATTR_PACK8(ab, f);
745 }
746 if (alp->commonattr & ATTR_CMN_PAROBJID) {
747 fsobj_id_t f = {0, 0};
748 ATTR_PACK8(ab, f);
749 }
750 /* note that this returns the encoding for the volume name, not the node name */
751 if (alp->commonattr & ATTR_CMN_SCRIPT)
752 ATTR_PACK4(ab, va.va_encoding);
753 if (alp->commonattr & ATTR_CMN_CRTIME)
754 ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit);
755 if (alp->commonattr & ATTR_CMN_MODTIME)
756 ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
757 if (alp->commonattr & ATTR_CMN_CHGTIME)
758 ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
759 if (alp->commonattr & ATTR_CMN_ACCTIME)
760 ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
761 if (alp->commonattr & ATTR_CMN_BKUPTIME)
762 ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit);
763 if (alp->commonattr & ATTR_CMN_FNDRINFO) {
764 char f[32];
765 /*
766 * This attribute isn't really Finder Info, at least for HFS.
767 */
768 if (vp->v_tag == VT_HFS) {
769 if ((error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx)) != 0)
770 goto out;
771 } else {
772 /* XXX we could at least pass out the volume UUID here */
773 bzero(&f, sizeof(f));
774 }
775 attrlist_pack_fixed(&ab, f, sizeof(f));
776 }
777 if (alp->commonattr & ATTR_CMN_OWNERID)
778 ATTR_PACK4(ab, va.va_uid);
779 if (alp->commonattr & ATTR_CMN_GRPID)
780 ATTR_PACK4(ab, va.va_gid);
781 if (alp->commonattr & ATTR_CMN_ACCESSMASK)
782 ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
783 if (alp->commonattr & ATTR_CMN_FLAGS)
784 ATTR_PACK4(ab, va.va_flags);
785 if (alp->commonattr & ATTR_CMN_USERACCESS) { /* XXX this is expensive and also duplicate work */
786 uint32_t perms = 0;
787 if (vnode_isdir(vp)) {
788 if (vnode_authorize(vp, NULL,
789 KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
790 perms |= W_OK;
791 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
792 perms |= R_OK;
793 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
794 perms |= X_OK;
795 } else {
796 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
797 perms |= W_OK;
798 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
799 perms |= R_OK;
800 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
801 perms |= X_OK;
802 }
803 #if CONFIG_MACF
804 /*
805 * Rather than MAC preceding DAC, in this case we want
806 * the smallest set of permissions granted by both MAC & DAC
807 * checks. We won't add back any permissions.
808 */
809 if (perms & W_OK)
810 if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
811 perms &= ~W_OK;
812 if (perms & R_OK)
813 if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
814 perms &= ~R_OK;
815 if (perms & X_OK)
816 if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
817 perms &= ~X_OK;
818 #endif /* MAC */
819 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
820 ATTR_PACK4(ab, perms);
821 }
822
823 /* volume attributes **************************************************/
824
825 if (alp->volattr & ATTR_VOL_FSTYPE)
826 ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
827 if (alp->volattr & ATTR_VOL_SIGNATURE)
828 ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
829 if (alp->volattr & ATTR_VOL_SIZE)
830 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
831 if (alp->volattr & ATTR_VOL_SPACEFREE)
832 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
833 if (alp->volattr & ATTR_VOL_SPACEAVAIL)
834 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
835 if (alp->volattr & ATTR_VOL_MINALLOCATION)
836 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
837 if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP)
838 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); /* not strictly true */
839 if (alp->volattr & ATTR_VOL_IOBLOCKSIZE)
840 ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
841 if (alp->volattr & ATTR_VOL_OBJCOUNT)
842 ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
843 if (alp->volattr & ATTR_VOL_FILECOUNT)
844 ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
845 if (alp->volattr & ATTR_VOL_DIRCOUNT)
846 ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
847 if (alp->volattr & ATTR_VOL_MAXOBJCOUNT)
848 ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
849 if (alp->volattr & ATTR_VOL_MOUNTPOINT)
850 attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
851 if (alp->volattr & ATTR_VOL_NAME)
852 attrlist_pack_string(&ab, vs.f_vol_name, 0);
853 if (alp->volattr & ATTR_VOL_MOUNTFLAGS)
854 ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
855 if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
856 attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
857 if (alp->volattr & ATTR_VOL_ENCODINGSUSED)
858 ATTR_PACK_CAST(&ab, uint64_t, ~0LL); /* return all encodings */
859 if (alp->volattr & ATTR_VOL_CAPABILITIES) {
860 /* fix up volume capabilities */
861 if (vfs_extendedsecurity(mnt)) {
862 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
863 } else {
864 vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
865 }
866 vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
867 ATTR_PACK(&ab, vs.f_capabilities);
868 }
869 if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
870 /* fix up volume attribute information */
871
872 vs.f_attributes.validattr.commonattr |= VFS_DFLT_ATTR_CMN;
873 vs.f_attributes.validattr.volattr |= VFS_DFLT_ATTR_VOL;
874 vs.f_attributes.validattr.dirattr |= VFS_DFLT_ATTR_DIR;
875 vs.f_attributes.validattr.fileattr |= VFS_DFLT_ATTR_FILE;
876
877 if (vfs_extendedsecurity(mnt)) {
878 vs.f_attributes.validattr.commonattr |= (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
879 } else {
880 vs.f_attributes.validattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
881 vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
882 }
883 ATTR_PACK(&ab, vs.f_attributes);
884 }
885
886 /* diagnostic */
887 if ((ab.fixedcursor - ab.base) != fixedsize)
888 panic("packed field size mismatch; allocated %ld but packed %d for common %08x vol %08x",
889 fixedsize, ab.fixedcursor - ab.base, alp->commonattr, alp->volattr);
890 if (ab.varcursor != (ab.base + ab.needed))
891 panic("packed variable field size mismatch; used %d but expected %ld", ab.varcursor - ab.base, ab.needed);
892
893 /*
894 * In the compatible case, we report the smaller of the required and returned sizes.
895 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
896 * of the result buffer, even if we copied less out. The caller knows how big a buffer
897 * they gave us, so they can always check for truncation themselves.
898 */
899 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
900
901 error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
902
903 out:
904 if (vs.f_vol_name != NULL)
905 kfree(vs.f_vol_name, MAXPATHLEN);
906 if (ab.base != NULL)
907 FREE(ab.base, M_TEMP);
908 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
909 return(error);
910 }
911
912 /*
913 * Obtain attribute information about a filesystem object.
914 */
915 int
916 getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
917 {
918 struct attrlist al;
919 struct vnode_attr va;
920 struct vfs_context *ctx;
921 struct nameidata nd;
922 struct _attrlist_buf ab;
923 vnode_t vp;
924 u_long nameiflags;
925 kauth_action_t action;
926 ssize_t fixedsize, varsize;
927 const char *cnp;
928 const char *vname = NULL;
929 ssize_t cnl;
930 int proc_is64;
931 int error;
932
933 ctx = vfs_context_current();
934 vp = NULL;
935 error = 0;
936 proc_is64 = proc_is64bit(p);
937 VATTR_INIT(&va);
938 va.va_name = NULL;
939 ab.base = NULL;
940 cnp = "unknown";
941 cnl = 0;
942
943 /*
944 * Look up the file.
945 */
946 nameiflags = NOTRIGGER | AUDITVNPATH1;
947 if (!(uap->options & FSOPT_NOFOLLOW))
948 nameiflags |= FOLLOW;
949 NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, ctx);
950
951 if ((error = namei(&nd)) != 0)
952 goto out;
953 vp = nd.ni_vp;
954 nameidone(&nd);
955
956 /*
957 * Fetch the attribute request.
958 */
959 if ((error = copyin(uap->alist, &al, sizeof(al))) != 0)
960 goto out;
961 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
962 error = EINVAL;
963 goto out;
964 }
965
966 VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
967 vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
968 (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
969
970 #if CONFIG_MACF
971 error = mac_vnode_check_getattrlist(ctx, vp, &al);
972 if (error)
973 goto out;
974 #endif /* MAC */
975
976 /*
977 * It is legal to request volume or file attributes,
978 * but not both.
979 */
980 if (al.volattr) {
981 if (al.fileattr || al.dirattr || al.forkattr) {
982 error = EINVAL;
983 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
984 goto out;
985 }
986 /* handle volume attribute request */
987 error = getvolattrlist(vp, uap, &al, ctx, proc_is64);
988 goto out;
989 }
990
991 /*
992 * Set up the vnode_attr structure and authorise.
993 */
994 if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64, vnode_isdir(vp))) != 0) {
995 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
996 goto out;
997 }
998 if ((error = vnode_authorize(vp, NULL, action, ctx)) != 0) {
999 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorisation failed/denied");
1000 goto out;
1001 }
1002
1003 if (va.va_active != 0) {
1004 /*
1005 * If we're going to ask for va_name, allocate a buffer to point it at
1006 */
1007 if (VATTR_IS_ACTIVE(&va, va_name)) {
1008 va.va_name = (char *) kalloc(MAXPATHLEN);
1009 if (va.va_name == NULL) {
1010 error = ENOMEM;
1011 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
1012 goto out;
1013 }
1014 }
1015
1016 /*
1017 * Call the filesystem.
1018 */
1019 if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
1020 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
1021 goto out;
1022 }
1023
1024 /* did we ask for something the filesystem doesn't support? */
1025 if (!VATTR_ALL_SUPPORTED(&va)) {
1026
1027 /*
1028 * There are a couple of special cases. If we are after object IDs,
1029 * we can make do with va_fileid.
1030 */
1031 if ((al.commonattr & (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) && !VATTR_IS_SUPPORTED(&va, va_linkid))
1032 VATTR_CLEAR_ACTIVE(&va, va_linkid); /* forget we wanted this */
1033 /*
1034 * Many (most?) filesystems don't know their parent object id. We can get it the
1035 * hard way.
1036 */
1037 if ((al.commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) && !VATTR_IS_SUPPORTED(&va, va_parentid))
1038 VATTR_CLEAR_ACTIVE(&va, va_parentid);
1039 /*
1040 * And we can report datasize/alloc from total.
1041 */
1042 if ((al.fileattr & ATTR_FILE_DATALENGTH) && !VATTR_IS_SUPPORTED(&va, va_data_size))
1043 VATTR_CLEAR_ACTIVE(&va, va_data_size);
1044 if ((al.fileattr & ATTR_FILE_DATAALLOCSIZE) && !VATTR_IS_SUPPORTED(&va, va_data_alloc))
1045 VATTR_CLEAR_ACTIVE(&va, va_data_alloc);
1046
1047 /*
1048 * If we don't have an encoding, go with UTF-8
1049 */
1050 if ((al.commonattr & ATTR_CMN_SCRIPT) && !VATTR_IS_SUPPORTED(&va, va_encoding))
1051 VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
1052
1053 /*
1054 * If we don't have a name, we'll get one from the vnode or mount point.
1055 */
1056 if ((al.commonattr & ATTR_CMN_NAME) && !VATTR_IS_SUPPORTED(&va, va_name)) {
1057 VATTR_CLEAR_ACTIVE(&va, va_name);
1058 }
1059
1060 /* If va_dirlinkcount isn't supported use a default of 1. */
1061 if ((al.dirattr & ATTR_DIR_LINKCOUNT) && !VATTR_IS_SUPPORTED(&va, va_dirlinkcount)) {
1062 VATTR_RETURN(&va, va_dirlinkcount, 1);
1063 }
1064
1065 /* check again */
1066 if (!VATTR_ALL_SUPPORTED(&va)) {
1067 error = EINVAL;
1068 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not get all requested file attributes");
1069 VFS_DEBUG(ctx, vp, "ATTRLIST - have %016llx wanted %016llx missing %016llx",
1070 va.va_supported, va.va_active, va.va_active & ~va.va_supported);
1071 goto out;
1072 }
1073 }
1074 }
1075
1076 /*
1077 * Compute variable-space requirements.
1078 */
1079 varsize = 0; /* length count */
1080 if (al.commonattr & ATTR_CMN_NAME) {
1081 if (VATTR_IS_SUPPORTED(&va, va_name)) {
1082 va.va_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
1083 cnp = va.va_name;
1084 cnl = strlen(cnp);
1085 } else {
1086 if (vnode_isvroot(vp)) {
1087 if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
1088 vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
1089 /* special case for boot volume. Use root name when it's
1090 * available (which is the volume name) or just the mount on
1091 * name of "/". we must do this for binary compatibility with
1092 * pre Tiger code. returning nothing for the boot volume name
1093 * breaks installers - 3961058
1094 */
1095 cnp = vname = vnode_getname(vp);
1096 if (cnp == NULL) {
1097 /* just use "/" as name */
1098 cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
1099 }
1100 cnl = strlen(cnp);
1101 }
1102 else {
1103 getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
1104 }
1105 } else {
1106 cnp = vname = vnode_getname(vp);
1107 cnl = 0;
1108 if (cnp != NULL) {
1109 cnl = strlen(cnp);
1110 }
1111 }
1112 }
1113 varsize += roundup(cnl + 1, 4);
1114 }
1115
1116 /*
1117 * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
1118 *
1119 * XXX This needs to change at some point; since the blob is opaque in
1120 * user-space this is OK.
1121 */
1122 if ((al.commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
1123 VATTR_IS_SUPPORTED(&va, va_acl) &&
1124 (va.va_acl != NULL))
1125 varsize += roundup(KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount), 4);
1126
1127 /*
1128 * Allocate a target buffer for attribute results.
1129 *
1130 * Note that we won't ever copy out more than the caller requested, even though
1131 * we might have to allocate more than they offer so that the diagnostic checks
1132 * don't result in a panic if the caller's buffer is too small..
1133 */
1134 ab.allocated = fixedsize + varsize;
1135 if (ab.allocated > ATTR_MAX_BUFFER) {
1136 error = ENOMEM;
1137 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
1138 goto out;
1139 }
1140 MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
1141 if (ab.base == NULL) {
1142 error = ENOMEM;
1143 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
1144 goto out;
1145 }
1146
1147 /* set the S_IFMT bits for the mode */
1148 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
1149 switch (vp->v_type) {
1150 case VREG:
1151 va.va_mode |= S_IFREG;
1152 break;
1153 case VDIR:
1154 va.va_mode |= S_IFDIR;
1155 break;
1156 case VBLK:
1157 va.va_mode |= S_IFBLK;
1158 break;
1159 case VCHR:
1160 va.va_mode |= S_IFCHR;
1161 break;
1162 case VLNK:
1163 va.va_mode |= S_IFLNK;
1164 break;
1165 case VSOCK:
1166 va.va_mode |= S_IFSOCK;
1167 break;
1168 case VFIFO:
1169 va.va_mode |= S_IFIFO;
1170 break;
1171 default:
1172 error = EBADF;
1173 goto out;
1174 }
1175 }
1176
1177 /*
1178 * Pack results into the destination buffer.
1179 */
1180 ab.fixedcursor = ab.base + sizeof(uint32_t);
1181 ab.varcursor = ab.base + fixedsize;
1182 ab.needed = ab.allocated;
1183
1184 /* common attributes **************************************************/
1185 if (al.commonattr & ATTR_CMN_NAME)
1186 attrlist_pack_string(&ab, cnp, cnl);
1187 if (al.commonattr & ATTR_CMN_DEVID)
1188 ATTR_PACK4(ab, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
1189 if (al.commonattr & ATTR_CMN_FSID)
1190 ATTR_PACK8(ab, vp->v_mount->mnt_vfsstat.f_fsid);
1191 if (al.commonattr & ATTR_CMN_OBJTYPE)
1192 ATTR_PACK4(ab, vp->v_type);
1193 if (al.commonattr & ATTR_CMN_OBJTAG)
1194 ATTR_PACK4(ab, vp->v_tag);
1195 if (al.commonattr & ATTR_CMN_OBJID) {
1196 fsobj_id_t f;
1197 /*
1198 * Carbon can't deal with us reporting the target ID
1199 * for links. So we ask the filesystem to give us the
1200 * source ID as well, and if it gives us one, we use
1201 * it instead.
1202 */
1203 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1204 f.fid_objno = va.va_linkid;
1205 } else {
1206 f.fid_objno = va.va_fileid;
1207 }
1208 f.fid_generation = 0;
1209 ATTR_PACK8(ab, f);
1210 }
1211 if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
1212 fsobj_id_t f;
1213 /*
1214 * Carbon can't deal with us reporting the target ID
1215 * for links. So we ask the filesystem to give us the
1216 * source ID as well, and if it gives us one, we use
1217 * it instead.
1218 */
1219 if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
1220 f.fid_objno = va.va_linkid;
1221 } else {
1222 f.fid_objno = va.va_fileid;
1223 }
1224 f.fid_generation = 0;
1225 ATTR_PACK8(ab, f);
1226 }
1227 if (al.commonattr & ATTR_CMN_PAROBJID) {
1228 fsobj_id_t f;
1229 /*
1230 * If the filesystem doesn't know the parent ID, we can
1231 * try to get it via v->v_parent. Don't need to worry
1232 * about links here, as we dont allow hardlinks to
1233 * directories.
1234 */
1235 if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
1236 f.fid_objno = va.va_parentid;
1237 } else {
1238 struct vnode_attr lva;
1239 vnode_t pvp;
1240
1241 pvp = vnode_getparent(vp);
1242
1243 if (pvp == NULLVP) {
1244 error = EINVAL;
1245 goto out;
1246 }
1247 VATTR_INIT(&lva);
1248 VATTR_WANTED(&lva, va_fileid);
1249 error = vnode_getattr(pvp, &lva, ctx);
1250 vnode_put(pvp);
1251
1252 if (error != 0)
1253 goto out;
1254 f.fid_objno = lva.va_fileid;
1255 }
1256 f.fid_generation = 0;
1257 ATTR_PACK8(ab, f);
1258 }
1259 if (al.commonattr & ATTR_CMN_SCRIPT)
1260 ATTR_PACK4(ab, va.va_encoding);
1261 if (al.commonattr & ATTR_CMN_CRTIME)
1262 ATTR_PACK_TIME(ab, va.va_create_time, proc_is64);
1263 if (al.commonattr & ATTR_CMN_MODTIME)
1264 ATTR_PACK_TIME(ab, va.va_modify_time, proc_is64);
1265 if (al.commonattr & ATTR_CMN_CHGTIME)
1266 ATTR_PACK_TIME(ab, va.va_change_time, proc_is64);
1267 if (al.commonattr & ATTR_CMN_ACCTIME)
1268 ATTR_PACK_TIME(ab, va.va_access_time, proc_is64);
1269 if (al.commonattr & ATTR_CMN_BKUPTIME)
1270 ATTR_PACK_TIME(ab, va.va_backup_time, proc_is64);
1271 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1272 uio_t auio;
1273 size_t fisize;
1274 char uio_buf[UIO_SIZEOF(1)];
1275
1276 fisize = imin(32, ab.allocated - (ab.fixedcursor - ab.base));
1277 if (fisize > 0) {
1278 if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
1279 error = ENOMEM;
1280 goto out;
1281 } else {
1282 uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
1283 error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio, &fisize, XATTR_NOSECURITY, ctx);
1284 uio_free(auio);
1285 }
1286 if (error != 0) {
1287 if ((error == ENOATTR) || (error == ENOENT) || (error == ENOTSUP) || (error == EPERM)) {
1288 VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
1289 bzero(ab.fixedcursor, 32);
1290 error = 0;
1291 } else {
1292 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
1293 goto out;
1294 }
1295 }
1296 } else {
1297 VFS_DEBUG(ctx, vp, "ATTRLIST - no room in caller buffer for FINDERINFO");
1298 }
1299 ab.fixedcursor += 32;
1300 }
1301 if (al.commonattr & ATTR_CMN_OWNERID)
1302 ATTR_PACK4(ab, va.va_uid);
1303 if (al.commonattr & ATTR_CMN_GRPID)
1304 ATTR_PACK4(ab, va.va_gid);
1305 if (al.commonattr & ATTR_CMN_ACCESSMASK)
1306 ATTR_PACK4(ab, va.va_mode);
1307 if (al.commonattr & ATTR_CMN_FLAGS)
1308 ATTR_PACK4(ab, va.va_flags);
1309 if (al.commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
1310 uint32_t perms = 0;
1311 if (vnode_isdir(vp)) {
1312 if (vnode_authorize(vp, NULL,
1313 KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
1314 perms |= W_OK;
1315 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
1316 perms |= R_OK;
1317 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
1318 perms |= X_OK;
1319 } else {
1320 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
1321 perms |= W_OK;
1322 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
1323 perms |= R_OK;
1324 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
1325 perms |= X_OK;
1326 }
1327
1328 #if CONFIG_MACF
1329 /*
1330 * Rather than MAC preceding DAC, in this case we want
1331 * the smallest set of permissions granted by both MAC & DAC
1332 * checks. We won't add back any permissions.
1333 */
1334 if (perms & W_OK)
1335 if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
1336 perms &= ~W_OK;
1337 if (perms & R_OK)
1338 if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
1339 perms &= ~R_OK;
1340 if (perms & X_OK)
1341 if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
1342 perms &= ~X_OK;
1343 #endif /* MAC */
1344 VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
1345 ATTR_PACK4(ab, perms);
1346 }
1347 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1348 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) {
1349 struct kauth_filesec fsec;
1350 /*
1351 * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
1352 */
1353 fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
1354 fsec.fsec_owner = kauth_null_guid;
1355 fsec.fsec_group = kauth_null_guid;
1356 attrlist_pack_variable2(&ab, &fsec, ((char *)&fsec.fsec_acl - (char *)&fsec), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
1357 } else {
1358 attrlist_pack_variable(&ab, NULL, 0);
1359 }
1360 }
1361 if (al.commonattr & ATTR_CMN_UUID) {
1362 if (!VATTR_IS_SUPPORTED(&va, va_uuuid)) {
1363 ATTR_PACK(&ab, kauth_null_guid);
1364 } else {
1365 ATTR_PACK(&ab, va.va_uuuid);
1366 }
1367 }
1368 if (al.commonattr & ATTR_CMN_GRPUUID) {
1369 if (!VATTR_IS_SUPPORTED(&va, va_guuid)) {
1370 ATTR_PACK(&ab, kauth_null_guid);
1371 } else {
1372 ATTR_PACK(&ab, va.va_guuid);
1373 }
1374 }
1375 if (al.commonattr & ATTR_CMN_FILEID) {
1376 ATTR_PACK8(ab, va.va_fileid);
1377 }
1378 if (al.commonattr & ATTR_CMN_PARENTID) {
1379 uint64_t fileid;
1380 /*
1381 * If the filesystem doesn't know the parent ID, we can
1382 * try to get it via v->v_parent.
1383 */
1384 if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
1385 fileid = va.va_parentid;
1386 } else {
1387 struct vnode_attr lva;
1388 vnode_t pvp;
1389
1390 pvp = vnode_getparent(vp);
1391
1392 if (pvp == NULLVP) {
1393 error = EINVAL;
1394 goto out;
1395 }
1396 VATTR_INIT(&lva);
1397 VATTR_WANTED(&lva, va_fileid);
1398 error = vnode_getattr(pvp, &lva, ctx);
1399 vnode_put(pvp);
1400
1401 if (error != 0)
1402 goto out;
1403 fileid = lva.va_fileid;
1404 }
1405 ATTR_PACK8(ab, fileid);
1406 }
1407
1408 /* directory attributes **************************************************/
1409 if (vnode_isdir(vp)) {
1410 if (al.dirattr & ATTR_DIR_LINKCOUNT) /* full count of entries */
1411 ATTR_PACK4(ab, (uint32_t)va.va_dirlinkcount);
1412 if (al.dirattr & ATTR_DIR_ENTRYCOUNT)
1413 ATTR_PACK4(ab, (uint32_t)va.va_nchildren);
1414 if (al.dirattr & ATTR_DIR_MOUNTSTATUS)
1415 ATTR_PACK_CAST(&ab, uint32_t, (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0);
1416 }
1417
1418 /* file attributes **************************************************/
1419 if (!vnode_isdir(vp)) {
1420 if (al.fileattr & ATTR_FILE_LINKCOUNT)
1421 ATTR_PACK4(ab, (uint32_t)va.va_nlink);
1422 if (al.fileattr & ATTR_FILE_TOTALSIZE)
1423 ATTR_PACK8(ab, va.va_total_size);
1424 if (al.fileattr & ATTR_FILE_ALLOCSIZE)
1425 ATTR_PACK8(ab, va.va_total_alloc);
1426 if (al.fileattr & ATTR_FILE_IOBLOCKSIZE)
1427 ATTR_PACK4(ab, va.va_iosize);
1428 if (al.fileattr & ATTR_FILE_CLUMPSIZE)
1429 ATTR_PACK4(ab, 0); /* XXX value is deprecated */
1430 if (al.fileattr & ATTR_FILE_DEVTYPE) {
1431 if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
1432 ATTR_PACK(&ab, vp->v_specinfo->si_rdev);
1433 } else {
1434 ATTR_PACK_CAST(&ab, uint32_t, 0);
1435 }
1436 }
1437 if (al.fileattr & ATTR_FILE_DATALENGTH) {
1438 if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
1439 ATTR_PACK8(ab, va.va_data_size);
1440 } else {
1441 ATTR_PACK8(ab, va.va_total_size);
1442 }
1443 }
1444 if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
1445 if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
1446 ATTR_PACK8(ab, va.va_data_alloc);
1447 } else {
1448 ATTR_PACK8(ab, va.va_total_alloc);
1449 }
1450 }
1451 /* fetch resource fork size/allocation via xattr interface */
1452 if (al.fileattr & (ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
1453 size_t rsize;
1454 uint64_t rlength;
1455
1456 if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, ctx)) != 0) {
1457 if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)) {
1458 rsize = 0;
1459 error = 0;
1460 } else {
1461 goto out;
1462 }
1463 }
1464 if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
1465 rlength = rsize;
1466 ATTR_PACK8(ab, rlength);
1467 }
1468 if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
1469 uint32_t blksize = vp->v_mount->mnt_vfsstat.f_bsize;
1470 if (blksize == 0)
1471 blksize = 512;
1472 rlength = roundup(rsize, blksize);
1473 ATTR_PACK8(ab, rlength);
1474 }
1475 }
1476 }
1477
1478 /* diagnostic */
1479 if ((ab.fixedcursor - ab.base) != fixedsize)
1480 panic("packed field size mismatch; allocated %ld but packed %d for common %08x vol %08x",
1481 fixedsize, ab.fixedcursor - ab.base, al.commonattr, al.volattr);
1482 if (ab.varcursor != (ab.base + ab.needed))
1483 panic("packed variable field size mismatch; used %d but expected %ld", ab.varcursor - ab.base, ab.needed);
1484
1485 /*
1486 * In the compatible case, we report the smaller of the required and returned sizes.
1487 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1488 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1489 * they gave us, so they can always check for truncation themselves.
1490 */
1491 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
1492
1493 /* Only actually copyout as much out as the user buffer can hold */
1494 error = copyout(ab.base, uap->attributeBuffer, imin(uap->bufferSize, ab.allocated));
1495
1496 out:
1497 if (va.va_name)
1498 kfree(va.va_name, MAXPATHLEN);
1499 if (vname)
1500 vnode_putname(vname);
1501 if (vp)
1502 vnode_put(vp);
1503 if (ab.base != NULL)
1504 FREE(ab.base, M_TEMP);
1505 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
1506 kauth_acl_free(va.va_acl);
1507
1508 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
1509 return(error);
1510 }
1511
1512 static int
1513 attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
1514 {
1515 /* make sure we have enough source data */
1516 if ((*cursor) + size > end)
1517 return(EINVAL);
1518
1519 bcopy(*cursor, buf, size);
1520 *cursor += size;
1521 return(0);
1522 }
1523
1524 #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
1525 #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
1526 #define ATTR_UNPACK_TIME(v, is64) \
1527 do { \
1528 if (is64) { \
1529 struct user_timespec us; \
1530 ATTR_UNPACK(us); \
1531 v.tv_sec = us.tv_sec; \
1532 v.tv_nsec = us.tv_nsec; \
1533 } else { \
1534 ATTR_UNPACK(v); \
1535 } \
1536 } while(0)
1537
1538
1539 /*
1540 * Write attributes.
1541 */
1542 int
1543 setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
1544 {
1545 struct attrlist al;
1546 struct vfs_context context, *ctx;
1547 struct vnode_attr va;
1548 struct attrreference ar;
1549 struct nameidata nd;
1550 vnode_t vp;
1551 u_long nameiflags;
1552 kauth_action_t action;
1553 char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
1554 int proc_is64, error;
1555 uint32_t nace;
1556 kauth_filesec_t rfsec;
1557
1558 context.vc_thread = current_thread();
1559 context.vc_ucred = kauth_cred_get();
1560 ctx = &context;
1561 vp = NULL;
1562 user_buf = NULL;
1563 fndrinfo = NULL;
1564 volname = NULL;
1565 error = 0;
1566 proc_is64 = proc_is64bit(p);
1567 VATTR_INIT(&va);
1568
1569
1570 /*
1571 * Look up the file.
1572 */
1573 nameiflags = 0;
1574 if ((uap->options & FSOPT_NOFOLLOW) == 0)
1575 nameiflags |= FOLLOW;
1576 NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, UIO_USERSPACE, uap->path, &context);
1577 if ((error = namei(&nd)) != 0)
1578 goto out;
1579 vp = nd.ni_vp;
1580 nameidone(&nd);
1581
1582 /*
1583 * Fetch the attribute set and validate.
1584 */
1585 if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
1586 goto out;
1587 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
1588 error = EINVAL;
1589 goto out;
1590 }
1591
1592 VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
1593 vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
1594 (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
1595
1596 if (al.volattr) {
1597 if ((al.volattr & ~ATTR_VOL_SETMASK) ||
1598 (al.commonattr & ~ATTR_CMN_VOLSETMASK) ||
1599 al.fileattr ||
1600 al.forkattr) {
1601 error = EINVAL;
1602 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
1603 goto out;
1604 }
1605 } else {
1606 if ((al.commonattr & ~ATTR_CMN_SETMASK) ||
1607 (al.fileattr & ~ATTR_FILE_SETMASK) ||
1608 (al.dirattr & ~ATTR_DIR_SETMASK) ||
1609 (al.forkattr & ~ATTR_FORK_SETMASK)) {
1610 error = EINVAL;
1611 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
1612 goto out;
1613 }
1614 }
1615
1616 /*
1617 * Make the naive assumption that the caller has supplied a reasonable buffer
1618 * size. We could be more careful by pulling in the fixed-size region, checking
1619 * the attrref structures, then pulling in the variable section.
1620 * We need to reconsider this for handling large ACLs, as they should probably be
1621 * brought directly into a buffer. Multiple copyins will make this slower though.
1622 *
1623 * We could also map the user buffer if it is larger than some sensible mimimum.
1624 */
1625 if (uap->bufferSize > ATTR_MAX_BUFFER) {
1626 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large", uap->bufferSize);
1627 error = ENOMEM;
1628 goto out;
1629 }
1630 MALLOC(user_buf, char *, uap->bufferSize, M_TEMP, M_WAITOK);
1631 if (user_buf == NULL) {
1632 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
1633 error = ENOMEM;
1634 goto out;
1635 }
1636 if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) {
1637 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed");
1638 goto out;
1639 }
1640 VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
1641
1642 #if CONFIG_MACF
1643 error = mac_vnode_check_setattrlist(&context, vp, &al);
1644 if (error)
1645 goto out;
1646 #endif /* MAC */
1647
1648 /*
1649 * Unpack the argument buffer.
1650 */
1651 cursor = user_buf;
1652 bufend = cursor + uap->bufferSize;
1653
1654 /* common */
1655 if (al.commonattr & ATTR_CMN_SCRIPT) {
1656 ATTR_UNPACK(va.va_encoding);
1657 VATTR_SET_ACTIVE(&va, va_encoding);
1658 }
1659 if (al.commonattr & ATTR_CMN_CRTIME) {
1660 ATTR_UNPACK_TIME(va.va_create_time, proc_is64);
1661 VATTR_SET_ACTIVE(&va, va_create_time);
1662 }
1663 if (al.commonattr & ATTR_CMN_MODTIME) {
1664 ATTR_UNPACK_TIME(va.va_modify_time, proc_is64);
1665 VATTR_SET_ACTIVE(&va, va_modify_time);
1666 }
1667 if (al.commonattr & ATTR_CMN_CHGTIME) {
1668 ATTR_UNPACK_TIME(va.va_change_time, proc_is64);
1669 VATTR_SET_ACTIVE(&va, va_change_time);
1670 }
1671 if (al.commonattr & ATTR_CMN_ACCTIME) {
1672 ATTR_UNPACK_TIME(va.va_access_time, proc_is64);
1673 VATTR_SET_ACTIVE(&va, va_access_time);
1674 }
1675 if (al.commonattr & ATTR_CMN_BKUPTIME) {
1676 ATTR_UNPACK_TIME(va.va_backup_time, proc_is64);
1677 VATTR_SET_ACTIVE(&va, va_backup_time);
1678 }
1679 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1680 if ((cursor + 32) > bufend) {
1681 error = EINVAL;
1682 VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO");
1683 goto out;
1684 }
1685 fndrinfo = cursor;
1686 cursor += 32;
1687 }
1688 if (al.commonattr & ATTR_CMN_OWNERID) {
1689 ATTR_UNPACK(va.va_uid);
1690 VATTR_SET_ACTIVE(&va, va_uid);
1691 }
1692 if (al.commonattr & ATTR_CMN_GRPID) {
1693 ATTR_UNPACK(va.va_gid);
1694 VATTR_SET_ACTIVE(&va, va_gid);
1695 }
1696 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
1697 ATTR_UNPACK_CAST(uint32_t, va.va_mode);
1698 VATTR_SET_ACTIVE(&va, va_mode);
1699 }
1700 if (al.commonattr & ATTR_CMN_FLAGS) {
1701 ATTR_UNPACK(va.va_flags);
1702 VATTR_SET_ACTIVE(&va, va_flags);
1703 }
1704 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
1705
1706 /*
1707 * We are (for now) passed a kauth_filesec_t, but all we want from
1708 * it is the ACL.
1709 */
1710 cp = cursor;
1711 ATTR_UNPACK(ar);
1712 cp += ar.attr_dataoffset;
1713 rfsec = (kauth_filesec_t)cp;
1714 if (((char *)(rfsec + 1) > bufend) || /* no space for acl */
1715 (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) || /* bad magic */
1716 (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
1717 ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */
1718 error = EINVAL;
1719 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
1720 goto out;
1721 }
1722 nace = rfsec->fsec_entrycount;
1723 if (nace == KAUTH_FILESEC_NOACL)
1724 nace = 0;
1725 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
1726 error = EINVAL;
1727 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
1728 goto out;
1729 }
1730 nace = rfsec->fsec_acl.acl_entrycount;
1731 if (nace == KAUTH_FILESEC_NOACL) {
1732 /* deleting ACL */
1733 VATTR_SET(&va, va_acl, NULL);
1734 } else {
1735
1736 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
1737 error = EINVAL;
1738 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
1739 goto out;
1740 }
1741 VATTR_SET(&va, va_acl, &rfsec->fsec_acl);
1742 }
1743 }
1744 if (al.commonattr & ATTR_CMN_UUID) {
1745 ATTR_UNPACK(va.va_uuuid);
1746 VATTR_SET_ACTIVE(&va, va_uuuid);
1747 }
1748 if (al.commonattr & ATTR_CMN_GRPUUID) {
1749 ATTR_UNPACK(va.va_guuid);
1750 VATTR_SET_ACTIVE(&va, va_guuid);
1751 }
1752
1753 /* volume */
1754 if (al.volattr & ATTR_VOL_INFO) {
1755 if (al.volattr & ATTR_VOL_NAME) {
1756 volname = cursor;
1757 ATTR_UNPACK(ar);
1758 volname += ar.attr_dataoffset;
1759 if ((volname + ar.attr_length) > bufend) {
1760 error = EINVAL;
1761 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
1762 goto out;
1763 }
1764 /* guarantee NUL termination */
1765 volname[ar.attr_length - 1] = 0;
1766 }
1767 }
1768
1769 /* file */
1770 if (al.fileattr & ATTR_FILE_DEVTYPE) {
1771 /* XXX does it actually make any sense to change this? */
1772 error = EINVAL;
1773 VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented");
1774 goto out;
1775 }
1776
1777 /*
1778 * Validate and authorize.
1779 */
1780 action = 0;
1781 if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, &context)) != 0)) {
1782 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
1783 goto out;
1784 }
1785 /*
1786 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
1787 * and will be auth'ed by the FS.
1788 */
1789 if (fndrinfo != NULL) {
1790 if (al.volattr & ATTR_VOL_INFO) {
1791 if (vp->v_tag != VT_HFS) {
1792 error = EINVAL;
1793 goto out;
1794 }
1795 } else {
1796 action |= KAUTH_VNODE_WRITE_ATTRIBUTES;
1797 }
1798 }
1799
1800 if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) {
1801 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
1802 goto out;
1803 }
1804
1805 /*
1806 * When we're setting both the access mask and the finder info, then
1807 * check if were about to remove write access for the owner. Since
1808 * vnode_setattr and vn_setxattr invoke two separate vnops, we need
1809 * to consider their ordering.
1810 *
1811 * If were about to remove write access for the owner we'll set the
1812 * Finder Info here before vnode_setattr. Otherwise we'll set it
1813 * after vnode_setattr since it may be adding owner write access.
1814 */
1815 if ((fndrinfo != NULL) && !(al.volattr & ATTR_VOL_INFO) &&
1816 (al.commonattr & ATTR_CMN_ACCESSMASK) && !(va.va_mode & S_IWUSR)) {
1817 if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
1818 goto out;
1819 }
1820 fndrinfo = NULL; /* it was set here so skip setting below */
1821 }
1822
1823 /*
1824 * Write the attributes if we have any.
1825 */
1826 if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, &context)) != 0)) {
1827 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
1828 goto out;
1829 }
1830
1831 /*
1832 * Write the Finder Info if we have any.
1833 */
1834 if (fndrinfo != NULL) {
1835 if (al.volattr & ATTR_VOL_INFO) {
1836 if (vp->v_tag == VT_HFS) {
1837 error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, &context);
1838 if (error != 0)
1839 goto out;
1840 } else {
1841 /* XXX should never get here */
1842 }
1843 } else if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
1844 goto out;
1845 }
1846 }
1847
1848 /*
1849 * Set the volume name, if we have one
1850 */
1851 if (volname != NULL)
1852 {
1853 struct vfs_attr vs;
1854
1855 VFSATTR_INIT(&vs);
1856
1857 vs.f_vol_name = volname; /* References the setattrlist buffer directly */
1858 VFSATTR_WANTED(&vs, f_vol_name);
1859
1860 #if CONFIG_MACF
1861 error = mac_mount_check_setattr(ctx, vp->v_mount, &vs);
1862 if (error != 0)
1863 goto out;
1864 #endif
1865
1866 if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
1867 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
1868 goto out;
1869 }
1870
1871 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
1872 error = EINVAL;
1873 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
1874 goto out;
1875 }
1876 }
1877
1878 /* all done and successful */
1879
1880 out:
1881 if (vp != NULL)
1882 vnode_put(vp);
1883 if (user_buf != NULL)
1884 FREE(user_buf, M_TEMP);
1885 VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
1886 return(error);
1887 }