]>
Commit | Line | Data |
---|---|---|
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 | } |