]> git.saurik.com Git - apple/xnu.git/blame - bsd/vfs/vfs_attrlist.c
xnu-1504.15.3.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
CommitLineData
91447636 1/*
b0d623f7 2 * Copyright (c) 1995-2008 Apple Inc. All rights reserved.
91447636 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
91447636 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55
A
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.
91447636
A
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
2d21ac55
A
54#if CONFIG_MACF
55#include <security/mac_framework.h>
56#endif
57
91447636
A
58#define ATTR_TIME_SIZE -1
59
60/*
61 * Structure describing the state of an in-progress attrlist operation.
62 */
63struct _attrlist_buf {
64 char *base;
65 char *fixedcursor;
66 char *varcursor;
67 ssize_t allocated;
68 ssize_t needed;
b0d623f7
A
69 attribute_set_t actual;
70 attribute_set_t valid;
91447636
A
71};
72
73
74/*
75 * Pack (count) bytes from (source) into (buf).
76 */
77static void
78attrlist_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}
90static void
91attrlist_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}
117static void
118attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
119{
120 attrlist_pack_variable2(ab, source, count, NULL, 0);
121}
122static void
123attrlist_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
2d21ac55
A
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
91447636
A
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) { \
b0d623f7 187 struct user64_timespec us = {v.tv_sec, v.tv_nsec}; \
2d21ac55 188 ATTR_PACK(&b, us); \
91447636 189 } else { \
b0d623f7
A
190 struct user32_timespec us = {v.tv_sec, v.tv_nsec}; \
191 ATTR_PACK(&b, us); \
91447636
A
192 } \
193 } while(0)
194
195
196/*
197 * Table-driven setup for all valid common/volume attributes.
198 */
199struct getvolattrlist_attrtab {
200 attrgroup_t attr;
201 uint64_t bits;
202#define VFSATTR_BIT(b) (VFSATTR_ ## b)
203 ssize_t size;
204};
205static 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)},
b0d623f7
A
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)},
91447636
A
232 {0, 0, 0}
233};
b0d623f7
A
234#define ATTR_CMN_VOL_INVALID \
235 (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID | \
236 ATTR_CMN_FILEID | ATTR_CMN_PARENTID)
91447636
A
237
238static 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)},
b0d623f7 257 {ATTR_VOL_UUID, VFSATTR_BIT(f_uuid), sizeof(uuid_t)},
91447636
A
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
263static int
264getvolattrlist_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) {
b0d623f7 277 *sizep += sizeof(struct user64_timespec);
91447636 278 } else {
b0d623f7 279 *sizep += sizeof(struct user32_timespec);
91447636
A
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 */
297static int
298getvolattrlist_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 */
b0d623f7
A
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 }
91447636
A
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
b0d623f7
A
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 */
329static void
330getvolattrlist_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
91447636
A
356/*
357 * Table-driven setup for all valid common/dir/file/fork attributes against files.
358 */
359struct 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};
b0d623f7
A
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 */
91447636
A
371static 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},
2d21ac55
A
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},
b0d623f7
A
397 {ATTR_CMN_FULLPATH, 0, sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES },
398 {ATTR_CMN_RETURNED_ATTRS, 0, sizeof(attribute_set_t), 0},
91447636
A
399 {0, 0, 0, 0}
400};
b0d623f7 401
91447636 402static struct getattrlist_attrtab getattrlist_dir_tab[] = {
2d21ac55 403 {ATTR_DIR_LINKCOUNT, VATTR_BIT(va_dirlinkcount), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
91447636 404 {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nchildren), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
91447636
A
405 {ATTR_DIR_MOUNTSTATUS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
406 {0, 0, 0, 0}
407};
408static 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
2d21ac55
A
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 | \
b0d623f7 432 ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
2d21ac55
A
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 | \
b0d623f7 439 ATTR_CMN_FNDRINFO | \
2d21ac55
A
440 ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
441 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
442 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
b0d623f7 443 ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS)
2d21ac55
A
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
91447636
A
453static int
454getattrlist_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) {
b0d623f7 467 *sizep += sizeof(struct user64_timespec);
91447636 468 } else {
b0d623f7 469 *sizep += sizeof(struct user32_timespec);
91447636
A
470 }
471 } else {
472 *sizep += tab->size;
473 }
474 *actionp |= tab->action;
b0d623f7
A
475 if (attrs == recognised)
476 break; /* all done, get out */
91447636
A
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 */
490static int
491getattrlist_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
b0d623f7
A
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 */
518static void
519getattrlist_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
8f6c56a5
A
555static int
556setattrlist_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
2d21ac55 570#if CONFIG_FSE
8f6c56a5
A
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 }
2d21ac55 574#endif
8f6c56a5
A
575 return (error);
576}
577
91447636
A
578
579/*
580 * Find something resembling a terminal component name in the mountedonname for vp
581 *
582 */
583static void
584getattrlist_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
617static int
b0d623f7
A
618getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
619 vfs_context_t ctx, int is_64bit)
91447636
A
620{
621 struct vfs_attr vs;
622 struct vnode_attr va;
623 struct _attrlist_buf ab;
624 int error;
625 ssize_t fixedsize, varsize;
2d21ac55
A
626 const char *cnp = NULL; /* protected by ATTR_CMN_NAME */
627 ssize_t cnl = 0; /* protected by ATTR_CMN_NAME */
b0d623f7 628 int release_str = 0;
91447636 629 mount_t mnt;
b0d623f7
A
630 int return_valid;
631 int pack_invalid;
91447636
A
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
b0d623f7
A
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
91447636
A
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 }
b0d623f7 664
91447636
A
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
2d21ac55
A
683#if CONFIG_MACF
684 error = mac_mount_check_getattr(ctx, mnt, &vs);
685 if (error != 0)
686 goto out;
687#endif
91447636
A
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
2d21ac55
A
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
91447636
A
758 /* check to see if our fixups were enough */
759 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
b0d623f7
A
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 }
91447636
A
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
b0d623f7
A
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 }
91447636
A
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 }
b0d623f7
A
823 else {
824 release_str = 1;
825 }
91447636
A
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);
b0d623f7
A
865 if (return_valid) {
866 ab.fixedcursor += sizeof (attribute_set_t);
867 bzero(&ab.actual, sizeof (ab.actual));
868 }
91447636
A
869 ab.varcursor = ab.base + fixedsize;
870 ab.needed = fixedsize + varsize;
871
872 /* common attributes **************************************************/
b0d623f7 873 if (alp->commonattr & ATTR_CMN_NAME) {
91447636 874 attrlist_pack_string(&ab, cnp, cnl);
b0d623f7
A
875 ab.actual.commonattr |= ATTR_CMN_NAME;
876 }
877 if (alp->commonattr & ATTR_CMN_DEVID) {
2d21ac55 878 ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]);
b0d623f7
A
879 ab.actual.commonattr |= ATTR_CMN_DEVID;
880 }
881 if (alp->commonattr & ATTR_CMN_FSID) {
2d21ac55 882 ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid);
b0d623f7
A
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) {
2d21ac55 890 ATTR_PACK4(ab, vp->v_tag);
b0d623f7
A
891 ab.actual.commonattr |= ATTR_CMN_OBJTAG;
892 }
91447636 893 if (alp->commonattr & ATTR_CMN_OBJID) {
b0d623f7
A
894 if (!return_valid || pack_invalid) {
895 fsobj_id_t f = {0, 0};
896 ATTR_PACK8(ab, f);
897 }
91447636
A
898 }
899 if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
b0d623f7
A
900 if (!return_valid || pack_invalid) {
901 fsobj_id_t f = {0, 0};
902 ATTR_PACK8(ab, f);
903 }
91447636
A
904 }
905 if (alp->commonattr & ATTR_CMN_PAROBJID) {
b0d623f7
A
906 if (!return_valid || pack_invalid) {
907 fsobj_id_t f = {0, 0};
908 ATTR_PACK8(ab, f);
909 }
91447636
A
910 }
911 /* note that this returns the encoding for the volume name, not the node name */
b0d623f7 912 if (alp->commonattr & ATTR_CMN_SCRIPT) {
2d21ac55 913 ATTR_PACK4(ab, va.va_encoding);
b0d623f7
A
914 ab.actual.commonattr |= ATTR_CMN_SCRIPT;
915 }
916 if (alp->commonattr & ATTR_CMN_CRTIME) {
2d21ac55 917 ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit);
b0d623f7
A
918 ab.actual.commonattr |= ATTR_CMN_CRTIME;
919 }
920 if (alp->commonattr & ATTR_CMN_MODTIME) {
2d21ac55 921 ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
b0d623f7
A
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) {
2d21ac55 929 ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
b0d623f7
A
930 ab.actual.commonattr |= ATTR_CMN_ACCTIME;
931 }
932 if (alp->commonattr & ATTR_CMN_BKUPTIME) {
2d21ac55 933 ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit);
b0d623f7
A
934 ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
935 }
91447636
A
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) {
b0d623f7
A
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) {
91447636 947 goto out;
b0d623f7
A
948 }
949 } else if (!return_valid || pack_invalid) {
91447636
A
950 /* XXX we could at least pass out the volume UUID here */
951 bzero(&f, sizeof(f));
b0d623f7 952 attrlist_pack_fixed(&ab, f, sizeof(f));
91447636 953 }
91447636 954 }
b0d623f7 955 if (alp->commonattr & ATTR_CMN_OWNERID) {
2d21ac55 956 ATTR_PACK4(ab, va.va_uid);
b0d623f7
A
957 ab.actual.commonattr |= ATTR_CMN_OWNERID;
958 }
959 if (alp->commonattr & ATTR_CMN_GRPID) {
2d21ac55 960 ATTR_PACK4(ab, va.va_gid);
b0d623f7
A
961 ab.actual.commonattr |= ATTR_CMN_GRPID;
962 }
963 if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
91447636 964 ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
b0d623f7
A
965 ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
966 }
967 if (alp->commonattr & ATTR_CMN_FLAGS) {
2d21ac55 968 ATTR_PACK4(ab, va.va_flags);
b0d623f7
A
969 ab.actual.commonattr |= ATTR_CMN_FLAGS;
970 }
91447636
A
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 }
2d21ac55
A
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 */
91447636 1005 KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
2d21ac55 1006 ATTR_PACK4(ab, perms);
b0d623f7
A
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);
91447636
A
1026 }
1027
1028 /* volume attributes **************************************************/
1029
b0d623f7 1030 if (alp->volattr & ATTR_VOL_FSTYPE) {
91447636 1031 ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
b0d623f7
A
1032 ab.actual.volattr |= ATTR_VOL_FSTYPE;
1033 }
1034 if (alp->volattr & ATTR_VOL_SIGNATURE) {
91447636 1035 ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
b0d623f7
A
1036 ab.actual.volattr |= ATTR_VOL_SIGNATURE;
1037 }
1038 if (alp->volattr & ATTR_VOL_SIZE) {
91447636 1039 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
b0d623f7
A
1040 ab.actual.volattr |= ATTR_VOL_SIZE;
1041 }
1042 if (alp->volattr & ATTR_VOL_SPACEFREE) {
91447636 1043 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
b0d623f7
A
1044 ab.actual.volattr |= ATTR_VOL_SPACEFREE;
1045 }
1046 if (alp->volattr & ATTR_VOL_SPACEAVAIL) {
91447636 1047 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
b0d623f7
A
1048 ab.actual.volattr |= ATTR_VOL_SPACEAVAIL;
1049 }
1050 if (alp->volattr & ATTR_VOL_MINALLOCATION) {
91447636 1051 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
b0d623f7
A
1052 ab.actual.volattr |= ATTR_VOL_MINALLOCATION;
1053 }
1054 if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP) {
91447636 1055 ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); /* not strictly true */
b0d623f7
A
1056 ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP;
1057 }
1058 if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) {
91447636 1059 ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
b0d623f7
A
1060 ab.actual.volattr |= ATTR_VOL_IOBLOCKSIZE;
1061 }
1062 if (alp->volattr & ATTR_VOL_OBJCOUNT) {
91447636 1063 ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
b0d623f7
A
1064 ab.actual.volattr |= ATTR_VOL_OBJCOUNT;
1065 }
1066 if (alp->volattr & ATTR_VOL_FILECOUNT) {
91447636 1067 ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
b0d623f7
A
1068 ab.actual.volattr |= ATTR_VOL_FILECOUNT;
1069 }
1070 if (alp->volattr & ATTR_VOL_DIRCOUNT) {
91447636 1071 ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
b0d623f7
A
1072 ab.actual.volattr |= ATTR_VOL_DIRCOUNT;
1073 }
1074 if (alp->volattr & ATTR_VOL_MAXOBJCOUNT) {
91447636 1075 ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
b0d623f7
A
1076 ab.actual.volattr |= ATTR_VOL_MAXOBJCOUNT;
1077 }
1078 if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
91447636 1079 attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
b0d623f7
A
1080 ab.actual.volattr |= ATTR_VOL_MOUNTPOINT;
1081 }
1082 if (alp->volattr & ATTR_VOL_NAME) {
91447636 1083 attrlist_pack_string(&ab, vs.f_vol_name, 0);
b0d623f7
A
1084 ab.actual.volattr |= ATTR_VOL_NAME;
1085 }
1086 if (alp->volattr & ATTR_VOL_MOUNTFLAGS) {
91447636 1087 ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
b0d623f7
A
1088 ab.actual.volattr |= ATTR_VOL_MOUNTFLAGS;
1089 }
1090 if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
91447636 1091 attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
b0d623f7
A
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 }
91447636
A
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);
b0d623f7
A
1107 ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
1108 }
1109 if (alp->volattr & ATTR_VOL_UUID) {
1110 ATTR_PACK(&ab, vs.f_uuid);
91447636
A
1111 }
1112 if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
1113 /* fix up volume attribute information */
2d21ac55
A
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
91447636
A
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);
b0d623f7 1127 ab.actual.volattr |= ATTR_VOL_ATTRIBUTES;
91447636
A
1128 }
1129
1130 /* diagnostic */
b0d623f7
A
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);
91447636
A
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
b0d623f7
A
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 }
91447636
A
1155 error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
1156
1157out:
1158 if (vs.f_vol_name != NULL)
1159 kfree(vs.f_vol_name, MAXPATHLEN);
b0d623f7
A
1160 if (release_str) {
1161 vnode_putname(cnp);
1162 }
91447636
A
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 */
b0d623f7
A
1172
1173static int
1174getattrlist_internal(vnode_t vp, struct getattrlist_args *uap, proc_t p, vfs_context_t ctx)
91447636
A
1175{
1176 struct attrlist al;
1177 struct vnode_attr va;
91447636 1178 struct _attrlist_buf ab;
91447636
A
1179 kauth_action_t action;
1180 ssize_t fixedsize, varsize;
1181 const char *cnp;
2d21ac55 1182 const char *vname = NULL;
b0d623f7
A
1183 char *fullpathptr;
1184 ssize_t fullpathlen;
91447636 1185 ssize_t cnl;
2d21ac55 1186 int proc_is64;
91447636 1187 int error;
b0d623f7
A
1188 int return_valid;
1189 int pack_invalid;
1190 int vtype = 0;
91447636 1191
2d21ac55 1192 proc_is64 = proc_is64bit(p);
91447636
A
1193 VATTR_INIT(&va);
1194 va.va_name = NULL;
1195 ab.base = NULL;
1196 cnp = "unknown";
1197 cnl = 0;
b0d623f7
A
1198 fullpathptr = NULL;
1199 fullpathlen = 0;
91447636
A
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);
b0d623f7 1214
2d21ac55
A
1215#if CONFIG_MACF
1216 error = mac_vnode_check_getattrlist(ctx, vp, &al);
1217 if (error)
1218 goto out;
1219#endif /* MAC */
1220
91447636
A
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 */
2d21ac55 1232 error = getvolattrlist(vp, uap, &al, ctx, proc_is64);
91447636
A
1233 goto out;
1234 }
1235
b0d623f7
A
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
91447636
A
1257 /*
1258 * Set up the vnode_attr structure and authorise.
1259 */
b0d623f7 1260 if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) {
91447636
A
1261 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
1262 goto out;
1263 }
2d21ac55 1264 if ((error = vnode_authorize(vp, NULL, action, ctx)) != 0) {
91447636
A
1265 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorisation failed/denied");
1266 goto out;
1267 }
1268
b0d623f7
A
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
91447636
A
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 */
2d21ac55 1298 if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
91447636
A
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 */
2d21ac55 1310 if ((al.commonattr & (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) && !VATTR_IS_SUPPORTED(&va, va_linkid))
91447636 1311 VATTR_CLEAR_ACTIVE(&va, va_linkid); /* forget we wanted this */
b0d623f7 1312
91447636 1313 /*
b0d623f7
A
1314 * Many filesystems don't know their parent object id.
1315 * If necessary, attempt to derive it from the vnode.
91447636 1316 */
b0d623f7
A
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 }
91447636
A
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 */
b0d623f7
A
1345 if ((al.commonattr & ATTR_CMN_SCRIPT) &&
1346 !VATTR_IS_SUPPORTED(&va, va_encoding) && !return_valid)
91447636
A
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
2d21ac55
A
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);
91447636
A
1359 }
1360
1361 /* check again */
1362 if (!VATTR_ALL_SUPPORTED(&va)) {
b0d623f7
A
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 }
91447636
A
1378 }
1379 }
1380 }
1381
1382 /*
1383 * Compute variable-space requirements.
1384 */
b0d623f7
A
1385 varsize = 0; /* length count */
1386
1387 /* We may need to fix up the name attribute if requested */
91447636
A
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
b0d623f7
A
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
91447636
A
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.
3a60a9f5
A
1457 *
1458 * Note that we won't ever copy out more than the caller requested, even though
2d21ac55 1459 * we might have to allocate more than they offer so that the diagnostic checks
3a60a9f5 1460 * don't result in a panic if the caller's buffer is too small..
91447636 1461 */
3a60a9f5 1462 ab.allocated = fixedsize + varsize;
91447636
A
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
2d21ac55
A
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
91447636
A
1505 /*
1506 * Pack results into the destination buffer.
1507 */
1508 ab.fixedcursor = ab.base + sizeof(uint32_t);
b0d623f7
A
1509 if (return_valid) {
1510 ab.fixedcursor += sizeof (attribute_set_t);
1511 bzero(&ab.actual, sizeof (ab.actual));
1512 }
91447636 1513 ab.varcursor = ab.base + fixedsize;
3a60a9f5 1514 ab.needed = ab.allocated;
91447636
A
1515
1516 /* common attributes **************************************************/
b0d623f7 1517 if (al.commonattr & ATTR_CMN_NAME) {
91447636 1518 attrlist_pack_string(&ab, cnp, cnl);
b0d623f7
A
1519 ab.actual.commonattr |= ATTR_CMN_NAME;
1520 }
1521 if (al.commonattr & ATTR_CMN_DEVID) {
2d21ac55 1522 ATTR_PACK4(ab, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
b0d623f7
A
1523 ab.actual.commonattr |= ATTR_CMN_DEVID;
1524 }
1525 if (al.commonattr & ATTR_CMN_FSID) {
2d21ac55 1526 ATTR_PACK8(ab, vp->v_mount->mnt_vfsstat.f_fsid);
b0d623f7
A
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) {
2d21ac55 1534 ATTR_PACK4(ab, vp->v_tag);
b0d623f7
A
1535 ab.actual.commonattr |= ATTR_CMN_OBJTAG;
1536 }
91447636
A
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;
2d21ac55 1551 ATTR_PACK8(ab, f);
b0d623f7 1552 ab.actual.commonattr |= ATTR_CMN_OBJID;
91447636
A
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;
2d21ac55 1568 ATTR_PACK8(ab, f);
b0d623f7 1569 ab.actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
91447636
A
1570 }
1571 if (al.commonattr & ATTR_CMN_PAROBJID) {
1572 fsobj_id_t f;
2d21ac55 1573
b0d623f7 1574 f.fid_objno = va.va_parentid; /* could be lossy here! */
91447636 1575 f.fid_generation = 0;
2d21ac55 1576 ATTR_PACK8(ab, f);
b0d623f7 1577 ab.actual.commonattr |= ATTR_CMN_PAROBJID;
91447636 1578 }
b0d623f7
A
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) {
2d21ac55 1588 ATTR_PACK_TIME(ab, va.va_create_time, proc_is64);
b0d623f7
A
1589 ab.actual.commonattr |= ATTR_CMN_CRTIME;
1590 }
1591 if (al.commonattr & ATTR_CMN_MODTIME) {
2d21ac55 1592 ATTR_PACK_TIME(ab, va.va_modify_time, proc_is64);
b0d623f7
A
1593 ab.actual.commonattr |= ATTR_CMN_MODTIME;
1594 }
1595 if (al.commonattr & ATTR_CMN_CHGTIME) {
2d21ac55 1596 ATTR_PACK_TIME(ab, va.va_change_time, proc_is64);
b0d623f7
A
1597 ab.actual.commonattr |= ATTR_CMN_CHGTIME;
1598 }
1599 if (al.commonattr & ATTR_CMN_ACCTIME) {
2d21ac55 1600 ATTR_PACK_TIME(ab, va.va_access_time, proc_is64);
b0d623f7
A
1601 ab.actual.commonattr |= ATTR_CMN_ACCTIME;
1602 }
1603 if (al.commonattr & ATTR_CMN_BKUPTIME) {
2d21ac55 1604 ATTR_PACK_TIME(ab, va.va_backup_time, proc_is64);
b0d623f7
A
1605 ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
1606 }
91447636
A
1607 if (al.commonattr & ATTR_CMN_FNDRINFO) {
1608 uio_t auio;
b0d623f7 1609 size_t fisize = 32;
91447636
A
1610 char uio_buf[UIO_SIZEOF(1)];
1611
b0d623f7
A
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;
91447636 1639 }
91447636 1640 }
b0d623f7 1641 if (al.commonattr & ATTR_CMN_OWNERID) {
2d21ac55 1642 ATTR_PACK4(ab, va.va_uid);
b0d623f7
A
1643 ab.actual.commonattr |= ATTR_CMN_OWNERID;
1644 }
1645 if (al.commonattr & ATTR_CMN_GRPID) {
2d21ac55 1646 ATTR_PACK4(ab, va.va_gid);
b0d623f7
A
1647 ab.actual.commonattr |= ATTR_CMN_GRPID;
1648 }
1649 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
2d21ac55 1650 ATTR_PACK4(ab, va.va_mode);
b0d623f7
A
1651 ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
1652 }
1653 if (al.commonattr & ATTR_CMN_FLAGS) {
2d21ac55 1654 ATTR_PACK4(ab, va.va_flags);
b0d623f7
A
1655 ab.actual.commonattr |= ATTR_CMN_FLAGS;
1656 }
91447636
A
1657 if (al.commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
1658 uint32_t perms = 0;
b0d623f7 1659 if (vtype == VDIR) {
91447636 1660 if (vnode_authorize(vp, NULL,
2d21ac55 1661 KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
91447636 1662 perms |= W_OK;
2d21ac55 1663 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
91447636 1664 perms |= R_OK;
2d21ac55 1665 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
91447636
A
1666 perms |= X_OK;
1667 } else {
2d21ac55 1668 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
91447636 1669 perms |= W_OK;
2d21ac55 1670 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
91447636 1671 perms |= R_OK;
2d21ac55 1672 if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
91447636
A
1673 perms |= X_OK;
1674 }
2d21ac55
A
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 */
91447636 1692 VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
2d21ac55 1693 ATTR_PACK4(ab, perms);
b0d623f7 1694 ab.actual.commonattr |= ATTR_CMN_USERACCESS;
91447636
A
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;
b0d623f7
A
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) {
91447636
A
1708 attrlist_pack_variable(&ab, NULL, 0);
1709 }
1710 }
1711 if (al.commonattr & ATTR_CMN_UUID) {
b0d623f7
A
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 }
2d21ac55
A
1727 if (al.commonattr & ATTR_CMN_FILEID) {
1728 ATTR_PACK8(ab, va.va_fileid);
b0d623f7 1729 ab.actual.commonattr |= ATTR_CMN_FILEID;
2d21ac55
A
1730 }
1731 if (al.commonattr & ATTR_CMN_PARENTID) {
b0d623f7
A
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;
2d21ac55 1739 }
91447636 1740
b0d623f7
A
1741 /* directory attributes *********************************************/
1742 if (al.dirattr && (vtype == VDIR)) {
1743 if (al.dirattr & ATTR_DIR_LINKCOUNT) { /* full count of entries */
2d21ac55 1744 ATTR_PACK4(ab, (uint32_t)va.va_dirlinkcount);
b0d623f7
A
1745 ab.actual.dirattr |= ATTR_DIR_LINKCOUNT;
1746 }
1747 if (al.dirattr & ATTR_DIR_ENTRYCOUNT) {
2d21ac55 1748 ATTR_PACK4(ab, (uint32_t)va.va_nchildren);
b0d623f7
A
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 }
91447636
A
1756 }
1757
1758 /* file attributes **************************************************/
b0d623f7
A
1759 if (al.fileattr && (vtype != VDIR)) {
1760 if (al.fileattr & ATTR_FILE_LINKCOUNT) {
2d21ac55 1761 ATTR_PACK4(ab, (uint32_t)va.va_nlink);
b0d623f7
A
1762 ab.actual.fileattr |= ATTR_FILE_LINKCOUNT;
1763 }
1764 if (al.fileattr & ATTR_FILE_TOTALSIZE) {
2d21ac55 1765 ATTR_PACK8(ab, va.va_total_size);
b0d623f7
A
1766 ab.actual.fileattr |= ATTR_FILE_TOTALSIZE;
1767 }
1768 if (al.fileattr & ATTR_FILE_ALLOCSIZE) {
2d21ac55 1769 ATTR_PACK8(ab, va.va_total_alloc);
b0d623f7
A
1770 ab.actual.fileattr |= ATTR_FILE_ALLOCSIZE;
1771 }
1772 if (al.fileattr & ATTR_FILE_IOBLOCKSIZE) {
2d21ac55 1773 ATTR_PACK4(ab, va.va_iosize);
b0d623f7
A
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 }
91447636 1782 if (al.fileattr & ATTR_FILE_DEVTYPE) {
b0d623f7
A
1783 uint32_t dev;
1784
91447636 1785 if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
b0d623f7
A
1786 if (vp->v_specinfo != NULL)
1787 dev = vp->v_specinfo->si_rdev;
1788 else
1789 dev = va.va_rdev;
91447636 1790 } else {
b0d623f7 1791 dev = 0;
91447636 1792 }
b0d623f7
A
1793 ATTR_PACK4(ab, dev);
1794 ab.actual.fileattr |= ATTR_FILE_DEVTYPE;
91447636
A
1795 }
1796 if (al.fileattr & ATTR_FILE_DATALENGTH) {
1797 if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
2d21ac55 1798 ATTR_PACK8(ab, va.va_data_size);
91447636 1799 } else {
2d21ac55 1800 ATTR_PACK8(ab, va.va_total_size);
91447636 1801 }
b0d623f7 1802 ab.actual.fileattr |= ATTR_FILE_DATALENGTH;
91447636
A
1803 }
1804 if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
1805 if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
2d21ac55 1806 ATTR_PACK8(ab, va.va_data_alloc);
91447636 1807 } else {
2d21ac55 1808 ATTR_PACK8(ab, va.va_total_alloc);
91447636 1809 }
b0d623f7 1810 ab.actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
91447636
A
1811 }
1812 /* fetch resource fork size/allocation via xattr interface */
1813 if (al.fileattr & (ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
1814 size_t rsize;
2d21ac55
A
1815 uint64_t rlength;
1816
1817 if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, ctx)) != 0) {
91447636
A
1818 if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)) {
1819 rsize = 0;
1820 error = 0;
1821 } else {
1822 goto out;
1823 }
1824 }
2d21ac55
A
1825 if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
1826 rlength = rsize;
1827 ATTR_PACK8(ab, rlength);
b0d623f7 1828 ab.actual.fileattr |= ATTR_FILE_RSRCLENGTH;
2d21ac55 1829 }
91447636
A
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;
2d21ac55
A
1834 rlength = roundup(rsize, blksize);
1835 ATTR_PACK8(ab, rlength);
b0d623f7 1836 ab.actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
91447636
A
1837 }
1838 }
d1ecb069
A
1839 if (al.fileattr & ATTR_FILE_PROTECTION_CLASS) {
1840 }
91447636
A
1841 }
1842
1843 /* diagnostic */
b0d623f7
A
1844 if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
1845 panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
1846 fixedsize, (long) (ab.fixedcursor - ab.base), al.commonattr, al.volattr);
1847 if (!return_valid && ab.varcursor != (ab.base + ab.needed))
1848 panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
91447636
A
1849
1850 /*
1851 * In the compatible case, we report the smaller of the required and returned sizes.
1852 * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
1853 * of the result buffer, even if we copied less out. The caller knows how big a buffer
1854 * they gave us, so they can always check for truncation themselves.
1855 */
1856 *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
b0d623f7
A
1857
1858 /* Return attribute set output if requested. */
1859 if (return_valid) {
1860 ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
1861 if (pack_invalid) {
1862 /* Only report the attributes that are valid */
1863 ab.actual.commonattr &= ab.valid.commonattr;
1864 ab.actual.dirattr &= ab.valid.dirattr;
1865 ab.actual.fileattr &= ab.valid.fileattr;
1866 }
1867 bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
1868 }
91447636 1869
3a60a9f5
A
1870 /* Only actually copyout as much out as the user buffer can hold */
1871 error = copyout(ab.base, uap->attributeBuffer, imin(uap->bufferSize, ab.allocated));
91447636
A
1872
1873out:
1874 if (va.va_name)
1875 kfree(va.va_name, MAXPATHLEN);
b0d623f7
A
1876 if (fullpathptr)
1877 kfree(fullpathptr, MAXPATHLEN);
91447636 1878 if (vname)
b0d623f7 1879 vnode_putname(vname);
91447636
A
1880 if (ab.base != NULL)
1881 FREE(ab.base, M_TEMP);
1882 if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
1883 kauth_acl_free(va.va_acl);
1884
1885 VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
1886 return(error);
1887}
1888
b0d623f7
A
1889int
1890fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
1891{
1892 struct vfs_context *ctx;
1893 vnode_t vp = NULL;
1894 int error;
1895 struct getattrlist_args ap;
1896
1897 ctx = vfs_context_current();
1898 error = 0;
1899
1900 if ((error = file_vnode(uap->fd, &vp)) != 0)
1901 return (error);
1902
1903 if ((error = vnode_getwithref(vp)) != 0) {
1904 file_drop(uap->fd);
1905 return(error);
1906 }
1907
1908 ap.path = 0;
1909 ap.alist = uap->alist;
1910 ap.attributeBuffer = uap->attributeBuffer;
1911 ap.bufferSize = uap->bufferSize;
1912 ap.options = uap->options;
1913
1914 error = getattrlist_internal(vp, &ap, p, ctx);
1915
1916 file_drop(uap->fd);
1917 if (vp)
1918 vnode_put(vp);
1919
1920 return error;
1921}
1922
1923int
1924getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval)
1925{
1926 struct vfs_context *ctx;
1927 struct nameidata nd;
1928 vnode_t vp = NULL;
1929 u_long nameiflags;
1930 int error;
1931
1932 ctx = vfs_context_current();
1933 error = 0;
1934
1935 /*
1936 * Look up the file.
1937 */
1938 nameiflags = NOTRIGGER | AUDITVNPATH1;
1939 if (!(uap->options & FSOPT_NOFOLLOW))
1940 nameiflags |= FOLLOW;
1941 NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, ctx);
1942
1943 if ((error = namei(&nd)) != 0)
1944 goto out;
1945 vp = nd.ni_vp;
1946 nameidone(&nd);
1947
1948 error = getattrlist_internal(vp, uap, p, ctx);
1949out:
1950 if (vp)
1951 vnode_put(vp);
1952 return error;
1953}
1954
91447636
A
1955static int
1956attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
1957{
1958 /* make sure we have enough source data */
1959 if ((*cursor) + size > end)
1960 return(EINVAL);
1961
1962 bcopy(*cursor, buf, size);
1963 *cursor += size;
1964 return(0);
1965}
1966
1967#define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
1968#define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
1969#define ATTR_UNPACK_TIME(v, is64) \
1970 do { \
1971 if (is64) { \
b0d623f7 1972 struct user64_timespec us; \
91447636
A
1973 ATTR_UNPACK(us); \
1974 v.tv_sec = us.tv_sec; \
1975 v.tv_nsec = us.tv_nsec; \
1976 } else { \
b0d623f7
A
1977 struct user32_timespec us; \
1978 ATTR_UNPACK(us); \
1979 v.tv_sec = us.tv_sec; \
1980 v.tv_nsec = us.tv_nsec; \
91447636
A
1981 } \
1982 } while(0)
1983
1984
1985/*
1986 * Write attributes.
1987 */
b0d623f7
A
1988static int
1989setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_context_t ctx)
91447636
A
1990{
1991 struct attrlist al;
91447636
A
1992 struct vnode_attr va;
1993 struct attrreference ar;
91447636
A
1994 kauth_action_t action;
1995 char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
1996 int proc_is64, error;
1997 uint32_t nace;
1998 kauth_filesec_t rfsec;
1999
91447636
A
2000 user_buf = NULL;
2001 fndrinfo = NULL;
2002 volname = NULL;
2003 error = 0;
2004 proc_is64 = proc_is64bit(p);
2005 VATTR_INIT(&va);
2006
91447636
A
2007 /*
2008 * Fetch the attribute set and validate.
2009 */
2010 if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
2011 goto out;
2012 if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
2013 error = EINVAL;
2014 goto out;
2015 }
2016
2017 VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
2018 vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
2019 (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
2020
2021 if (al.volattr) {
2022 if ((al.volattr & ~ATTR_VOL_SETMASK) ||
2023 (al.commonattr & ~ATTR_CMN_VOLSETMASK) ||
2024 al.fileattr ||
2025 al.forkattr) {
2026 error = EINVAL;
2027 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes");
2028 goto out;
2029 }
2030 } else {
2031 if ((al.commonattr & ~ATTR_CMN_SETMASK) ||
2032 (al.fileattr & ~ATTR_FILE_SETMASK) ||
2033 (al.dirattr & ~ATTR_DIR_SETMASK) ||
2034 (al.forkattr & ~ATTR_FORK_SETMASK)) {
2035 error = EINVAL;
2036 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes");
2037 goto out;
2038 }
2039 }
2040
2041 /*
2042 * Make the naive assumption that the caller has supplied a reasonable buffer
2043 * size. We could be more careful by pulling in the fixed-size region, checking
2044 * the attrref structures, then pulling in the variable section.
2045 * We need to reconsider this for handling large ACLs, as they should probably be
2046 * brought directly into a buffer. Multiple copyins will make this slower though.
2047 *
2048 * We could also map the user buffer if it is larger than some sensible mimimum.
2049 */
2050 if (uap->bufferSize > ATTR_MAX_BUFFER) {
2051 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large", uap->bufferSize);
2052 error = ENOMEM;
2053 goto out;
2054 }
2055 MALLOC(user_buf, char *, uap->bufferSize, M_TEMP, M_WAITOK);
2056 if (user_buf == NULL) {
2057 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
2058 error = ENOMEM;
2059 goto out;
2060 }
2061 if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) {
2062 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed");
2063 goto out;
2064 }
2065 VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
2066
2d21ac55 2067#if CONFIG_MACF
b0d623f7 2068 error = mac_vnode_check_setattrlist(ctx, vp, &al);
2d21ac55
A
2069 if (error)
2070 goto out;
2071#endif /* MAC */
2072
91447636
A
2073 /*
2074 * Unpack the argument buffer.
2075 */
2076 cursor = user_buf;
2077 bufend = cursor + uap->bufferSize;
2078
2079 /* common */
2080 if (al.commonattr & ATTR_CMN_SCRIPT) {
2081 ATTR_UNPACK(va.va_encoding);
2082 VATTR_SET_ACTIVE(&va, va_encoding);
2083 }
2084 if (al.commonattr & ATTR_CMN_CRTIME) {
2085 ATTR_UNPACK_TIME(va.va_create_time, proc_is64);
2086 VATTR_SET_ACTIVE(&va, va_create_time);
2087 }
2088 if (al.commonattr & ATTR_CMN_MODTIME) {
2089 ATTR_UNPACK_TIME(va.va_modify_time, proc_is64);
2090 VATTR_SET_ACTIVE(&va, va_modify_time);
2091 }
2092 if (al.commonattr & ATTR_CMN_CHGTIME) {
2093 ATTR_UNPACK_TIME(va.va_change_time, proc_is64);
2094 VATTR_SET_ACTIVE(&va, va_change_time);
2095 }
2096 if (al.commonattr & ATTR_CMN_ACCTIME) {
2097 ATTR_UNPACK_TIME(va.va_access_time, proc_is64);
2098 VATTR_SET_ACTIVE(&va, va_access_time);
2099 }
2100 if (al.commonattr & ATTR_CMN_BKUPTIME) {
2101 ATTR_UNPACK_TIME(va.va_backup_time, proc_is64);
2102 VATTR_SET_ACTIVE(&va, va_backup_time);
2103 }
2104 if (al.commonattr & ATTR_CMN_FNDRINFO) {
2105 if ((cursor + 32) > bufend) {
2106 error = EINVAL;
2107 VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO");
2108 goto out;
2109 }
2110 fndrinfo = cursor;
2111 cursor += 32;
2112 }
2113 if (al.commonattr & ATTR_CMN_OWNERID) {
2114 ATTR_UNPACK(va.va_uid);
2115 VATTR_SET_ACTIVE(&va, va_uid);
2116 }
2117 if (al.commonattr & ATTR_CMN_GRPID) {
2118 ATTR_UNPACK(va.va_gid);
2119 VATTR_SET_ACTIVE(&va, va_gid);
2120 }
2121 if (al.commonattr & ATTR_CMN_ACCESSMASK) {
2122 ATTR_UNPACK_CAST(uint32_t, va.va_mode);
2123 VATTR_SET_ACTIVE(&va, va_mode);
2124 }
2125 if (al.commonattr & ATTR_CMN_FLAGS) {
2126 ATTR_UNPACK(va.va_flags);
2127 VATTR_SET_ACTIVE(&va, va_flags);
2128 }
2129 if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
2130
2131 /*
2132 * We are (for now) passed a kauth_filesec_t, but all we want from
2133 * it is the ACL.
2134 */
2135 cp = cursor;
2136 ATTR_UNPACK(ar);
2137 cp += ar.attr_dataoffset;
2138 rfsec = (kauth_filesec_t)cp;
b0d623f7 2139 if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) || /* no space for acl */
91447636
A
2140 (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) || /* bad magic */
2141 (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
2142 ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */
2143 error = EINVAL;
2144 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
2145 goto out;
2146 }
2147 nace = rfsec->fsec_entrycount;
2148 if (nace == KAUTH_FILESEC_NOACL)
2149 nace = 0;
2150 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
2151 error = EINVAL;
2152 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
2153 goto out;
2154 }
2155 nace = rfsec->fsec_acl.acl_entrycount;
2156 if (nace == KAUTH_FILESEC_NOACL) {
2157 /* deleting ACL */
2158 VATTR_SET(&va, va_acl, NULL);
2159 } else {
2160
2161 if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
2162 error = EINVAL;
2163 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
2164 goto out;
2165 }
2166 VATTR_SET(&va, va_acl, &rfsec->fsec_acl);
2167 }
2168 }
2169 if (al.commonattr & ATTR_CMN_UUID) {
2170 ATTR_UNPACK(va.va_uuuid);
2171 VATTR_SET_ACTIVE(&va, va_uuuid);
2172 }
2173 if (al.commonattr & ATTR_CMN_GRPUUID) {
2174 ATTR_UNPACK(va.va_guuid);
2175 VATTR_SET_ACTIVE(&va, va_guuid);
2176 }
2177
2178 /* volume */
2179 if (al.volattr & ATTR_VOL_INFO) {
2180 if (al.volattr & ATTR_VOL_NAME) {
2181 volname = cursor;
2182 ATTR_UNPACK(ar);
2183 volname += ar.attr_dataoffset;
2184 if ((volname + ar.attr_length) > bufend) {
2185 error = EINVAL;
2186 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
2187 goto out;
2188 }
2189 /* guarantee NUL termination */
2190 volname[ar.attr_length - 1] = 0;
2191 }
2192 }
2193
2194 /* file */
2195 if (al.fileattr & ATTR_FILE_DEVTYPE) {
2196 /* XXX does it actually make any sense to change this? */
2197 error = EINVAL;
2198 VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented");
2199 goto out;
2200 }
d1ecb069
A
2201 if (al.fileattr & ATTR_FILE_PROTECTION_CLASS) {
2202 }
91447636
A
2203
2204 /*
2205 * Validate and authorize.
2206 */
2207 action = 0;
b0d623f7 2208 if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, ctx)) != 0)) {
91447636
A
2209 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
2210 goto out;
2211 }
2212 /*
2213 * We can auth file Finder Info here. HFS volume FinderInfo is really boot data,
2214 * and will be auth'ed by the FS.
2215 */
2216 if (fndrinfo != NULL) {
2217 if (al.volattr & ATTR_VOL_INFO) {
2218 if (vp->v_tag != VT_HFS) {
2219 error = EINVAL;
2220 goto out;
2221 }
2222 } else {
b0d623f7 2223 action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
91447636
A
2224 }
2225 }
2226
b0d623f7 2227 if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) {
91447636
A
2228 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
2229 goto out;
2230 }
2231
8f6c56a5
A
2232 /*
2233 * When we're setting both the access mask and the finder info, then
2234 * check if were about to remove write access for the owner. Since
2235 * vnode_setattr and vn_setxattr invoke two separate vnops, we need
2236 * to consider their ordering.
2237 *
2238 * If were about to remove write access for the owner we'll set the
2239 * Finder Info here before vnode_setattr. Otherwise we'll set it
2240 * after vnode_setattr since it may be adding owner write access.
2241 */
2242 if ((fndrinfo != NULL) && !(al.volattr & ATTR_VOL_INFO) &&
2243 (al.commonattr & ATTR_CMN_ACCESSMASK) && !(va.va_mode & S_IWUSR)) {
2d21ac55 2244 if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
8f6c56a5
A
2245 goto out;
2246 }
2247 fndrinfo = NULL; /* it was set here so skip setting below */
2248 }
2249
91447636
A
2250 /*
2251 * Write the attributes if we have any.
2252 */
b0d623f7 2253 if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, ctx)) != 0)) {
91447636
A
2254 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
2255 goto out;
2256 }
2257
2258 /*
2259 * Write the Finder Info if we have any.
2260 */
2261 if (fndrinfo != NULL) {
2262 if (al.volattr & ATTR_VOL_INFO) {
2263 if (vp->v_tag == VT_HFS) {
b0d623f7 2264 error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx);
91447636
A
2265 if (error != 0)
2266 goto out;
2267 } else {
2268 /* XXX should never get here */
2269 }
2d21ac55 2270 } else if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) {
8f6c56a5 2271 goto out;
91447636
A
2272 }
2273 }
2274
2275 /*
2276 * Set the volume name, if we have one
2277 */
2278 if (volname != NULL)
2279 {
2280 struct vfs_attr vs;
2281
2282 VFSATTR_INIT(&vs);
2283
2284 vs.f_vol_name = volname; /* References the setattrlist buffer directly */
2285 VFSATTR_WANTED(&vs, f_vol_name);
2286
2d21ac55
A
2287#if CONFIG_MACF
2288 error = mac_mount_check_setattr(ctx, vp->v_mount, &vs);
2289 if (error != 0)
2290 goto out;
2291#endif
2292
91447636
A
2293 if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
2294 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
2295 goto out;
2296 }
2297
2298 if (!VFSATTR_ALL_SUPPORTED(&vs)) {
2299 error = EINVAL;
2300 VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
2301 goto out;
2302 }
2303 }
2304
2305 /* all done and successful */
2306
2307out:
91447636
A
2308 if (user_buf != NULL)
2309 FREE(user_buf, M_TEMP);
2310 VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
2311 return(error);
2312}
b0d623f7
A
2313
2314int
2315setattrlist(proc_t p, struct setattrlist_args *uap, __unused int32_t *retval)
2316{
2317 struct vfs_context *ctx;
2318 struct nameidata nd;
2319 vnode_t vp = NULL;
2320 u_long nameiflags;
2321 int error = 0;
2322
2323 ctx = vfs_context_current();
2324
2325 /*
2326 * Look up the file.
2327 */
2328 nameiflags = 0;
2329 if ((uap->options & FSOPT_NOFOLLOW) == 0)
2330 nameiflags |= FOLLOW;
2331 NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, UIO_USERSPACE, uap->path, ctx);
2332 if ((error = namei(&nd)) != 0)
2333 goto out;
2334 vp = nd.ni_vp;
2335 nameidone(&nd);
2336
2337 error = setattrlist_internal(vp, uap, p, ctx);
2338out:
2339 if (vp != NULL)
2340 vnode_put(vp);
2341 return error;
2342}
2343
2344int
2345fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
2346{
2347 struct vfs_context *ctx;
2348 vnode_t vp = NULL;
2349 int error;
2350 struct setattrlist_args ap;
2351
2352 ctx = vfs_context_current();
2353
2354 if ((error = file_vnode(uap->fd, &vp)) != 0)
2355 return (error);
2356
2357 if ((error = vnode_getwithref(vp)) != 0) {
2358 file_drop(uap->fd);
2359 return(error);
2360 }
2361
2362 ap.path = 0;
2363 ap.alist = uap->alist;
2364 ap.attributeBuffer = uap->attributeBuffer;
2365 ap.bufferSize = uap->bufferSize;
2366 ap.options = uap->options;
2367
2368 error = setattrlist_internal(vp, &ap, p, ctx);
2369 file_drop(uap->fd);
2370 if (vp != NULL)
2371 vnode_put(vp);
2372
2373 return error;
2374}
2375