1 | /* |
2 | * Copyright (c) 1995-2019 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/syslog.h> |
41 | #include <sys/vnode_internal.h> |
42 | #include <sys/mount_internal.h> |
43 | #include <sys/proc_internal.h> |
44 | #include <sys/file_internal.h> |
45 | #include <sys/kauth.h> |
46 | #include <sys/uio_internal.h> |
47 | #include <kern/kalloc.h> |
48 | #include <sys/attr.h> |
49 | #include <sys/sysproto.h> |
50 | #include <sys/xattr.h> |
51 | #include <sys/fsevents.h> |
52 | #include <kern/zalloc.h> |
53 | #include <miscfs/specfs/specdev.h> |
54 | #include <security/audit/audit.h> |
55 | |
56 | #if CONFIG_MACF |
57 | #include <security/mac_framework.h> |
58 | #endif |
59 | |
60 | #define ATTR_TIME_SIZE -1 |
61 | |
62 | static int readdirattr(vnode_t, struct fd_vn_data *, uio_t, struct attrlist *, |
63 | uint64_t, int *, int *, vfs_context_t ctx) __attribute__((noinline)); |
64 | |
65 | static void |
66 | vattr_get_alt_data(vnode_t, struct attrlist *, struct vnode_attr *, int, int, |
67 | int, vfs_context_t) __attribute__((noinline)); |
68 | |
69 | static void get_error_attributes(vnode_t, struct attrlist *, uint64_t, user_addr_t, |
70 | size_t, int, caddr_t, vfs_context_t) __attribute__((noinline)); |
71 | |
72 | static int getvolattrlist(vfs_context_t, vnode_t, struct attrlist *, user_addr_t, |
73 | size_t, uint64_t, enum uio_seg, int) __attribute__((noinline)); |
74 | |
75 | static int get_direntry(vfs_context_t, vnode_t, struct fd_vn_data *, int *, |
76 | struct direntry **) __attribute__((noinline)); |
77 | |
78 | /* |
79 | * Structure describing the state of an in-progress attrlist operation. |
80 | */ |
81 | struct _attrlist_buf { |
82 | char *base; |
83 | char *fixedcursor; |
84 | char *varcursor; |
85 | ssize_t allocated; |
86 | ssize_t needed; |
87 | attribute_set_t actual; |
88 | attribute_set_t valid; |
89 | }; |
90 | |
91 | #define _ATTRLIST_BUF_INIT(a) do {(a)->base = (a)->fixedcursor = (a)->varcursor = NULL; (a)->allocated = (a)->needed = 0l; ATTRIBUTE_SET_INIT(&((a)->actual)); ATTRIBUTE_SET_INIT(&((a)->valid));} while(0) |
92 | |
93 | |
94 | /* |
95 | * Attempt to pack a fixed width attribute of size (count) bytes from |
96 | * source to our attrlist buffer. |
97 | */ |
98 | static void |
99 | attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count) |
100 | { |
101 | /* |
102 | * Use ssize_t for pointer math purposes, |
103 | * since a ssize_t is a signed long |
104 | */ |
105 | ssize_t fit; |
106 | |
107 | /* |
108 | * Compute the amount of remaining space in the attrlist buffer |
109 | * based on how much we've used for fixed width fields vs. the |
110 | * start of the attributes. |
111 | * |
112 | * If we've still got room, then 'fit' will contain the amount of |
113 | * remaining space. |
114 | * |
115 | * Note that this math is safe because, in the event that the |
116 | * fixed-width cursor has moved beyond the end of the buffer, |
117 | * then, the second input into lmin() below will be negative, and |
118 | * we will fail the (fit > 0) check below. |
119 | */ |
120 | fit = lmin(a: count, b: ab->allocated - (ab->fixedcursor - ab->base)); |
121 | if (fit > 0) { |
122 | /* Copy in as much as we can */ |
123 | bcopy(src: source, dst: ab->fixedcursor, n: fit); |
124 | } |
125 | |
126 | /* always move in increments of 4, even if we didn't pack an attribute. */ |
127 | ab->fixedcursor += roundup(count, 4); |
128 | } |
129 | |
130 | /* |
131 | * Attempt to pack one (or two) variable width attributes into the attrlist |
132 | * buffer. If we are trying to pack two variable width attributes, they are treated |
133 | * as a single variable-width attribute from the POV of the system call caller. |
134 | * |
135 | * Recall that a variable-width attribute has two components: the fixed-width |
136 | * attribute that tells the caller where to look, and the actual variable width data. |
137 | */ |
138 | static void |
139 | attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count, |
140 | const void *ext, ssize_t extcount) |
141 | { |
142 | /* Use ssize_t's for pointer math ease */ |
143 | struct attrreference ar; |
144 | ssize_t fit; |
145 | |
146 | /* |
147 | * Pack the fixed-width component to the variable object. |
148 | * Note that we may be able to pack the fixed width attref, but not |
149 | * the variable (if there's no room). |
150 | */ |
151 | ar.attr_dataoffset = (int32_t)(ab->varcursor - ab->fixedcursor); |
152 | ar.attr_length = (u_int32_t)(count + extcount); |
153 | attrlist_pack_fixed(ab, source: &ar, count: sizeof(ar)); |
154 | |
155 | /* |
156 | * Use an lmin() to do a signed comparison. We use a signed comparison |
157 | * to detect the 'out of memory' conditions as described above in the |
158 | * fixed width check above. |
159 | * |
160 | * Then pack the first variable attribute as space allows. Note that we advance |
161 | * the variable cursor only if we we had some available space. |
162 | */ |
163 | fit = lmin(a: count, b: ab->allocated - (ab->varcursor - ab->base)); |
164 | if (fit > 0) { |
165 | if (source != NULL) { |
166 | bcopy(src: source, dst: ab->varcursor, n: fit); |
167 | } |
168 | ab->varcursor += fit; |
169 | } |
170 | |
171 | /* Compute the available space for the second attribute */ |
172 | fit = lmin(a: extcount, b: ab->allocated - (ab->varcursor - ab->base)); |
173 | if (fit > 0) { |
174 | /* Copy in data for the second attribute (if needed) if there is room */ |
175 | if (ext != NULL) { |
176 | bcopy(src: ext, dst: ab->varcursor, n: fit); |
177 | } |
178 | ab->varcursor += fit; |
179 | } |
180 | /* always move in increments of 4 */ |
181 | ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4); |
182 | } |
183 | |
184 | /* |
185 | * Packing a single variable-width attribute is the same as calling the two, but with |
186 | * an invalid 2nd attribute. |
187 | */ |
188 | static void |
189 | attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count) |
190 | { |
191 | attrlist_pack_variable2(ab, source, count, NULL, extcount: 0); |
192 | } |
193 | |
194 | /* |
195 | * Attempt to pack a string. This is a special case of a variable width attribute. |
196 | * |
197 | * If "source" is NULL, then an empty string ("") will be packed. If "source" is |
198 | * not NULL, but "count" is zero, then "source" is assumed to be a NUL-terminated |
199 | * C-string. If "source" is not NULL and "count" is not zero, then only the first |
200 | * "count" bytes of "source" will be copied, and a NUL terminator will be added. |
201 | * |
202 | * If the attrlist buffer doesn't have enough room to hold the entire string (including |
203 | * NUL terminator), then copy as much as will fit. The attrlist buffer's "varcursor" |
204 | * will always be updated based on the entire length of the string (including NUL |
205 | * terminator); this means "varcursor" may end up pointing beyond the end of the |
206 | * allocated buffer space. |
207 | */ |
208 | static void |
209 | attrlist_pack_string(struct _attrlist_buf *ab, const char *source, size_t count) |
210 | { |
211 | struct attrreference ar; |
212 | ssize_t fit, space; |
213 | |
214 | /* |
215 | * Supplied count is character count of string text, excluding trailing nul |
216 | * which we always supply here. |
217 | */ |
218 | if (source == NULL) { |
219 | count = 0; |
220 | } else if (count == 0) { |
221 | count = strlen(s: source); |
222 | } |
223 | |
224 | /* |
225 | * Construct the fixed-width attribute that refers to this string. |
226 | */ |
227 | ar.attr_dataoffset = (int32_t)(ab->varcursor - ab->fixedcursor); |
228 | ar.attr_length = (u_int32_t)count + 1; |
229 | attrlist_pack_fixed(ab, source: &ar, count: sizeof(ar)); |
230 | |
231 | /* |
232 | * Now compute how much available memory we have to copy the string text. |
233 | * |
234 | * space = the number of bytes available in the attribute buffer to hold the |
235 | * string's value. |
236 | * |
237 | * fit = the number of bytes to copy from the start of the string into the |
238 | * attribute buffer, NOT including the NUL terminator. If the attribute |
239 | * buffer is large enough, this will be the string's length; otherwise, it |
240 | * will be equal to "space". |
241 | */ |
242 | space = ab->allocated - (ab->varcursor - ab->base); |
243 | fit = lmin(a: count, b: space); |
244 | if (space > 0) { |
245 | long bytes_to_zero; |
246 | |
247 | /* |
248 | * If there is space remaining, copy data in, and |
249 | * accommodate the trailing NUL terminator. |
250 | * |
251 | * NOTE: if "space" is too small to hold the string and its NUL |
252 | * terminator (space < fit + 1), then the string value in the attribute |
253 | * buffer will NOT be NUL terminated! |
254 | * |
255 | * NOTE 2: bcopy() will do nothing if the length ("fit") is zero. |
256 | * Therefore, we don't bother checking for that here. |
257 | */ |
258 | bcopy(src: source, dst: ab->varcursor, n: fit); |
259 | /* is there room for our trailing nul? */ |
260 | if (space > fit) { |
261 | ab->varcursor[fit++] = '\0'; |
262 | /* 'fit' now the number of bytes AFTER adding in the NUL */ |
263 | /* |
264 | * Zero out any additional bytes we might have as a |
265 | * result of rounding up. |
266 | */ |
267 | bytes_to_zero = lmin(a: (roundup(fit, 4) - fit), |
268 | b: space - fit); |
269 | if (bytes_to_zero) { |
270 | bzero(s: &(ab->varcursor[fit]), n: bytes_to_zero); |
271 | } |
272 | } |
273 | } |
274 | /* |
275 | * always move in increments of 4 (including the trailing NUL) |
276 | */ |
277 | ab->varcursor += roundup((count + 1), 4); |
278 | } |
279 | |
280 | #define ATTR_PACK4(AB, V) \ |
281 | do { \ |
282 | if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) { \ |
283 | *(uint32_t *)AB.fixedcursor = V; \ |
284 | AB.fixedcursor += 4; \ |
285 | } \ |
286 | } while (0) |
287 | |
288 | #define ATTR_PACK8(AB, V) \ |
289 | do { \ |
290 | if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \ |
291 | memcpy(AB.fixedcursor, &V, 8); \ |
292 | AB.fixedcursor += 8; \ |
293 | } \ |
294 | } while (0) |
295 | |
296 | #define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v)) |
297 | #define ATTR_PACK_CAST(b, t, v) \ |
298 | do { \ |
299 | t _f = (t)v; \ |
300 | ATTR_PACK(b, _f); \ |
301 | } while (0) |
302 | |
303 | #define ATTR_PACK_TIME(b, v, is64) \ |
304 | do { \ |
305 | if (is64) { \ |
306 | struct user64_timespec us = {.tv_sec = v.tv_sec, .tv_nsec = v.tv_nsec}; \ |
307 | ATTR_PACK(&b, us); \ |
308 | } else { \ |
309 | struct user32_timespec us = {.tv_sec = (user32_time_t)v.tv_sec, .tv_nsec = (user32_long_t)v.tv_nsec}; \ |
310 | ATTR_PACK(&b, us); \ |
311 | } \ |
312 | } while(0) |
313 | |
314 | |
315 | /* |
316 | * Table-driven setup for all valid common/volume attributes. |
317 | */ |
318 | struct getvolattrlist_attrtab { |
319 | attrgroup_t attr; |
320 | uint64_t bits; |
321 | #define VFSATTR_BIT(b) (VFSATTR_ ## b) |
322 | ssize_t size; |
323 | }; |
324 | static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = { |
325 | {.attr = ATTR_CMN_NAME, .bits = 0, .size = sizeof(struct attrreference)}, |
326 | {.attr = ATTR_CMN_DEVID, .bits = 0, .size = sizeof(dev_t)}, |
327 | {.attr = ATTR_CMN_FSID, .bits = 0, .size = sizeof(fsid_t)}, |
328 | {.attr = ATTR_CMN_OBJTYPE, .bits = 0, .size = sizeof(fsobj_type_t)}, |
329 | {.attr = ATTR_CMN_OBJTAG, .bits = 0, .size = sizeof(fsobj_tag_t)}, |
330 | {.attr = ATTR_CMN_OBJID, .bits = 0, .size = sizeof(fsobj_id_t)}, |
331 | {.attr = ATTR_CMN_OBJPERMANENTID, .bits = 0, .size = sizeof(fsobj_id_t)}, |
332 | {.attr = ATTR_CMN_PAROBJID, .bits = 0, .size = sizeof(fsobj_id_t)}, |
333 | {.attr = ATTR_CMN_SCRIPT, .bits = 0, .size = sizeof(text_encoding_t)}, |
334 | {.attr = ATTR_CMN_CRTIME, .bits = VFSATTR_BIT(f_create_time), .size = ATTR_TIME_SIZE}, |
335 | {.attr = ATTR_CMN_MODTIME, .bits = VFSATTR_BIT(f_modify_time), .size = ATTR_TIME_SIZE}, |
336 | {.attr = ATTR_CMN_CHGTIME, .bits = VFSATTR_BIT(f_modify_time), .size = ATTR_TIME_SIZE}, |
337 | {.attr = ATTR_CMN_ACCTIME, .bits = VFSATTR_BIT(f_access_time), .size = ATTR_TIME_SIZE}, |
338 | {.attr = ATTR_CMN_BKUPTIME, .bits = VFSATTR_BIT(f_backup_time), .size = ATTR_TIME_SIZE}, |
339 | {.attr = ATTR_CMN_FNDRINFO, .bits = 0, .size = 32}, |
340 | {.attr = ATTR_CMN_OWNERID, .bits = 0, .size = sizeof(uid_t)}, |
341 | {.attr = ATTR_CMN_GRPID, .bits = 0, .size = sizeof(gid_t)}, |
342 | {.attr = ATTR_CMN_ACCESSMASK, .bits = 0, .size = sizeof(uint32_t)}, |
343 | {.attr = ATTR_CMN_FLAGS, .bits = 0, .size = sizeof(uint32_t)}, |
344 | {.attr = ATTR_CMN_USERACCESS, .bits = 0, .size = sizeof(uint32_t)}, |
345 | {.attr = ATTR_CMN_EXTENDED_SECURITY, .bits = 0, .size = sizeof(struct attrreference)}, |
346 | {.attr = ATTR_CMN_UUID, .bits = 0, .size = sizeof(guid_t)}, |
347 | {.attr = ATTR_CMN_GRPUUID, .bits = 0, .size = sizeof(guid_t)}, |
348 | {.attr = ATTR_CMN_FILEID, .bits = 0, .size = sizeof(uint64_t)}, |
349 | {.attr = ATTR_CMN_PARENTID, .bits = 0, .size = sizeof(uint64_t)}, |
350 | {.attr = ATTR_CMN_RETURNED_ATTRS, .bits = 0, .size = sizeof(attribute_set_t)}, |
351 | {.attr = ATTR_CMN_ERROR, .bits = 0, .size = sizeof(uint32_t)}, |
352 | {.attr = 0, .bits = 0, .size = 0} |
353 | }; |
354 | #define ATTR_CMN_VOL_INVALID \ |
355 | (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID | \ |
356 | ATTR_CMN_FILEID | ATTR_CMN_PARENTID) |
357 | |
358 | static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = { |
359 | {.attr = ATTR_VOL_FSTYPE, .bits = 0, .size = sizeof(uint32_t)}, |
360 | {.attr = ATTR_VOL_FSTYPENAME, .bits = 0, .size = sizeof(struct attrreference)}, |
361 | {.attr = ATTR_VOL_FSSUBTYPE, .bits = VFSATTR_BIT(f_fssubtype), .size = sizeof(uint32_t)}, |
362 | {.attr = ATTR_VOL_SIGNATURE, .bits = VFSATTR_BIT(f_signature), .size = sizeof(uint32_t)}, |
363 | {.attr = ATTR_VOL_SIZE, .bits = VFSATTR_BIT(f_blocks) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)}, |
364 | {.attr = ATTR_VOL_SPACEFREE, .bits = VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)}, |
365 | {.attr = ATTR_VOL_SPACEAVAIL, .bits = VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)}, |
366 | {.attr = ATTR_VOL_MINALLOCATION, .bits = VFSATTR_BIT(f_bsize), .size = sizeof(off_t)}, |
367 | {.attr = ATTR_VOL_ALLOCATIONCLUMP, .bits = VFSATTR_BIT(f_bsize), .size = sizeof(off_t)}, |
368 | {.attr = ATTR_VOL_IOBLOCKSIZE, .bits = VFSATTR_BIT(f_iosize), .size = sizeof(uint32_t)}, |
369 | {.attr = ATTR_VOL_OBJCOUNT, .bits = VFSATTR_BIT(f_objcount), .size = sizeof(uint32_t)}, |
370 | {.attr = ATTR_VOL_FILECOUNT, .bits = VFSATTR_BIT(f_filecount), .size = sizeof(uint32_t)}, |
371 | {.attr = ATTR_VOL_DIRCOUNT, .bits = VFSATTR_BIT(f_dircount), .size = sizeof(uint32_t)}, |
372 | {.attr = ATTR_VOL_MAXOBJCOUNT, .bits = VFSATTR_BIT(f_maxobjcount), .size = sizeof(uint32_t)}, |
373 | {.attr = ATTR_VOL_MOUNTPOINT, .bits = 0, .size = sizeof(struct attrreference)}, |
374 | {.attr = ATTR_VOL_NAME, .bits = VFSATTR_BIT(f_vol_name), .size = sizeof(struct attrreference)}, |
375 | {.attr = ATTR_VOL_MOUNTFLAGS, .bits = 0, .size = sizeof(uint32_t)}, |
376 | {.attr = ATTR_VOL_MOUNTEDDEVICE, .bits = 0, .size = sizeof(struct attrreference)}, |
377 | {.attr = ATTR_VOL_ENCODINGSUSED, .bits = 0, .size = sizeof(uint64_t)}, |
378 | {.attr = ATTR_VOL_CAPABILITIES, .bits = VFSATTR_BIT(f_capabilities), .size = sizeof(vol_capabilities_attr_t)}, |
379 | {.attr = ATTR_VOL_UUID, .bits = VFSATTR_BIT(f_uuid), .size = sizeof(uuid_t)}, |
380 | {.attr = ATTR_VOL_SPACEUSED, .bits = VFSATTR_BIT(f_bused) | VFSATTR_BIT(f_bsize) | VFSATTR_BIT(f_bfree), .size = sizeof(off_t)}, |
381 | {.attr = ATTR_VOL_QUOTA_SIZE, .bits = VFSATTR_BIT(f_quota) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)}, |
382 | {.attr = ATTR_VOL_RESERVED_SIZE, .bits = VFSATTR_BIT(f_reserved) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)}, |
383 | {.attr = ATTR_VOL_ATTRIBUTES, .bits = VFSATTR_BIT(f_attributes), .size = sizeof(vol_attributes_attr_t)}, |
384 | {.attr = ATTR_VOL_INFO, .bits = 0, .size = 0}, |
385 | {.attr = 0, .bits = 0, .size = 0} |
386 | }; |
387 | |
388 | static int |
389 | getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, struct vfs_attr *vsp, |
390 | ssize_t *sizep, int is_64bit, unsigned int maxiter) |
391 | { |
392 | attrgroup_t recognised; |
393 | |
394 | recognised = 0; |
395 | do { |
396 | /* is this attribute set? */ |
397 | if (tab->attr & attrs) { |
398 | recognised |= tab->attr; |
399 | vsp->f_active |= tab->bits; |
400 | if (tab->size == ATTR_TIME_SIZE) { |
401 | if (is_64bit) { |
402 | *sizep += sizeof(struct user64_timespec); |
403 | } else { |
404 | *sizep += sizeof(struct user32_timespec); |
405 | } |
406 | } else { |
407 | *sizep += tab->size; |
408 | } |
409 | } |
410 | } while (((++tab)->attr != 0) && (--maxiter > 0)); |
411 | |
412 | /* check to make sure that we recognised all of the passed-in attributes */ |
413 | if (attrs & ~recognised) { |
414 | return EINVAL; |
415 | } |
416 | return 0; |
417 | } |
418 | |
419 | /* |
420 | * Given the attributes listed in alp, configure vap to request |
421 | * the data from a filesystem. |
422 | */ |
423 | static int |
424 | getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit) |
425 | { |
426 | int error; |
427 | if (!alp) { |
428 | return EINVAL; |
429 | } |
430 | |
431 | /* |
432 | * Parse the above tables. |
433 | */ |
434 | *sizep = sizeof(uint32_t); /* length count */ |
435 | if (alp->commonattr) { |
436 | if ((alp->commonattr & ATTR_CMN_VOL_INVALID) && |
437 | (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) == 0) { |
438 | return EINVAL; |
439 | } |
440 | if ((error = getvolattrlist_parsetab(tab: getvolattrlist_common_tab, |
441 | attrs: alp->commonattr, vsp, sizep, |
442 | is_64bit, |
443 | maxiter: sizeof(getvolattrlist_common_tab) / sizeof(getvolattrlist_common_tab[0]))) != 0) { |
444 | return error; |
445 | } |
446 | } |
447 | if (alp->volattr && |
448 | (error = getvolattrlist_parsetab(tab: getvolattrlist_vol_tab, attrs: alp->volattr, vsp, sizep, is_64bit, maxiter: sizeof(getvolattrlist_vol_tab) / sizeof(getvolattrlist_vol_tab[0]))) != 0) { |
449 | return error; |
450 | } |
451 | |
452 | return 0; |
453 | } |
454 | |
455 | /* |
456 | * Given the attributes listed in asp and those supported |
457 | * in the vsp, fixup the asp attributes to reflect any |
458 | * missing attributes from the file system |
459 | */ |
460 | static void |
461 | getvolattrlist_fixupattrs(attribute_set_t *asp, struct vfs_attr *vsp) |
462 | { |
463 | struct getvolattrlist_attrtab *tab; |
464 | |
465 | if (asp->commonattr) { |
466 | tab = getvolattrlist_common_tab; |
467 | do { |
468 | if ((tab->attr & asp->commonattr) && |
469 | (tab->bits != 0) && |
470 | ((tab->bits & vsp->f_supported) == 0)) { |
471 | asp->commonattr &= ~tab->attr; |
472 | } |
473 | } while ((++tab)->attr != 0); |
474 | } |
475 | if (asp->volattr) { |
476 | tab = getvolattrlist_vol_tab; |
477 | do { |
478 | if ((tab->attr & asp->volattr) && |
479 | (tab->bits != 0) && |
480 | ((tab->bits & vsp->f_supported) == 0)) { |
481 | asp->volattr &= ~tab->attr; |
482 | } |
483 | } while ((++tab)->attr != 0); |
484 | } |
485 | } |
486 | |
487 | /* |
488 | * Table-driven setup for all valid common/dir/file/fork attributes against files. |
489 | */ |
490 | struct getattrlist_attrtab { |
491 | attrgroup_t attr; |
492 | uint64_t bits; |
493 | #define VATTR_BIT(b) (VNODE_ATTR_ ## b) |
494 | ssize_t size; |
495 | kauth_action_t action; |
496 | }; |
497 | |
498 | /* |
499 | * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this |
500 | * information, and we will synthesize it at the VFS level. |
501 | */ |
502 | static struct getattrlist_attrtab getattrlist_common_tab[] = { |
503 | {.attr = ATTR_CMN_NAME, .bits = VATTR_BIT(va_name), .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
504 | {.attr = ATTR_CMN_DEVID, .bits = VATTR_BIT(va_fsid), .size = sizeof(dev_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
505 | {.attr = ATTR_CMN_FSID, .bits = VATTR_BIT(va_fsid64), .size = sizeof(fsid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
506 | {.attr = ATTR_CMN_OBJTYPE, .bits = 0, .size = sizeof(fsobj_type_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
507 | {.attr = ATTR_CMN_OBJTAG, .bits = 0, .size = sizeof(fsobj_tag_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
508 | {.attr = ATTR_CMN_OBJID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
509 | {.attr = ATTR_CMN_OBJPERMANENTID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
510 | {.attr = ATTR_CMN_PAROBJID, .bits = VATTR_BIT(va_parentid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
511 | {.attr = ATTR_CMN_SCRIPT, .bits = VATTR_BIT(va_encoding), .size = sizeof(text_encoding_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
512 | {.attr = ATTR_CMN_CRTIME, .bits = VATTR_BIT(va_create_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
513 | {.attr = ATTR_CMN_MODTIME, .bits = VATTR_BIT(va_modify_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
514 | {.attr = ATTR_CMN_CHGTIME, .bits = VATTR_BIT(va_change_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
515 | {.attr = ATTR_CMN_ACCTIME, .bits = VATTR_BIT(va_access_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
516 | {.attr = ATTR_CMN_BKUPTIME, .bits = VATTR_BIT(va_backup_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
517 | {.attr = ATTR_CMN_FNDRINFO, .bits = 0, .size = 32, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
518 | {.attr = ATTR_CMN_OWNERID, .bits = VATTR_BIT(va_uid), .size = sizeof(uid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
519 | {.attr = ATTR_CMN_GRPID, .bits = VATTR_BIT(va_gid), .size = sizeof(gid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
520 | {.attr = ATTR_CMN_ACCESSMASK, .bits = VATTR_BIT(va_mode), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
521 | {.attr = ATTR_CMN_FLAGS, .bits = VATTR_BIT(va_flags), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
522 | {.attr = ATTR_CMN_GEN_COUNT, .bits = VATTR_BIT(va_write_gencount), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
523 | {.attr = ATTR_CMN_DOCUMENT_ID, .bits = VATTR_BIT(va_document_id), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
524 | {.attr = ATTR_CMN_USERACCESS, .bits = 0, .size = sizeof(uint32_t), .action = 0}, |
525 | {.attr = ATTR_CMN_EXTENDED_SECURITY, .bits = VATTR_BIT(va_acl), .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_SECURITY}, |
526 | {.attr = ATTR_CMN_UUID, .bits = VATTR_BIT(va_uuuid), .size = sizeof(guid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
527 | {.attr = ATTR_CMN_GRPUUID, .bits = VATTR_BIT(va_guuid), .size = sizeof(guid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
528 | {.attr = ATTR_CMN_FILEID, .bits = VATTR_BIT(va_fileid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
529 | {.attr = ATTR_CMN_PARENTID, .bits = VATTR_BIT(va_parentid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
530 | {.attr = ATTR_CMN_FULLPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
531 | {.attr = ATTR_CMN_ADDEDTIME, .bits = VATTR_BIT(va_addedtime), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
532 | {.attr = ATTR_CMN_RETURNED_ATTRS, .bits = 0, .size = sizeof(attribute_set_t), .action = 0}, |
533 | {.attr = ATTR_CMN_ERROR, .bits = 0, .size = sizeof(uint32_t), .action = 0}, |
534 | {.attr = ATTR_CMN_DATA_PROTECT_FLAGS, .bits = VATTR_BIT(va_dataprotect_class), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
535 | {.attr = 0, .bits = 0, .size = 0, .action = 0} |
536 | }; |
537 | |
538 | static struct getattrlist_attrtab getattrlist_dir_tab[] = { |
539 | {.attr = ATTR_DIR_LINKCOUNT, .bits = VATTR_BIT(va_dirlinkcount), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
540 | {.attr = ATTR_DIR_ENTRYCOUNT, .bits = VATTR_BIT(va_nchildren), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
541 | {.attr = ATTR_DIR_MOUNTSTATUS, .bits = 0, .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
542 | {.attr = ATTR_DIR_ALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
543 | {.attr = ATTR_DIR_IOBLOCKSIZE, .bits = VATTR_BIT(va_iosize), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
544 | {.attr = ATTR_DIR_DATALENGTH, .bits = VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
545 | {.attr = 0, .bits = 0, .size = 0, .action = 0} |
546 | }; |
547 | static struct getattrlist_attrtab getattrlist_file_tab[] = { |
548 | {.attr = ATTR_FILE_LINKCOUNT, .bits = VATTR_BIT(va_nlink), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
549 | {.attr = ATTR_FILE_TOTALSIZE, .bits = VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
550 | {.attr = ATTR_FILE_ALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
551 | {.attr = ATTR_FILE_IOBLOCKSIZE, .bits = VATTR_BIT(va_iosize), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
552 | {.attr = ATTR_FILE_CLUMPSIZE, .bits = 0, .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
553 | {.attr = ATTR_FILE_DEVTYPE, .bits = VATTR_BIT(va_rdev), .size = sizeof(dev_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
554 | {.attr = ATTR_FILE_DATALENGTH, .bits = VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
555 | {.attr = ATTR_FILE_DATAALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_data_alloc), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
556 | {.attr = ATTR_FILE_RSRCLENGTH, .bits = 0, .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
557 | {.attr = ATTR_FILE_RSRCALLOCSIZE, .bits = 0, .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
558 | {.attr = 0, .bits = 0, .size = 0, .action = 0} |
559 | }; |
560 | |
561 | //for forkattr bits repurposed as new common attributes |
562 | static struct getattrlist_attrtab getattrlist_common_extended_tab[] = { |
563 | {.attr = ATTR_CMNEXT_RELPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
564 | {.attr = ATTR_CMNEXT_PRIVATESIZE, .bits = VATTR_BIT(va_private_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
565 | {.attr = ATTR_CMNEXT_LINKID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
566 | {.attr = ATTR_CMNEXT_NOFIRMLINKPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
567 | {.attr = ATTR_CMNEXT_REALDEVID, .bits = VATTR_BIT(va_devid), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
568 | {.attr = ATTR_CMNEXT_REALFSID, .bits = VATTR_BIT(va_fsid64), .size = sizeof(fsid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
569 | {.attr = ATTR_CMNEXT_CLONEID, .bits = VATTR_BIT(va_clone_id), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
570 | {.attr = ATTR_CMNEXT_EXT_FLAGS, .bits = VATTR_BIT(va_extflags), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
571 | {.attr = ATTR_CMNEXT_RECURSIVE_GENCOUNT, .bits = VATTR_BIT(va_recursive_gencount), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
572 | {.attr = ATTR_CMNEXT_ATTRIBUTION_TAG, .bits = VATTR_BIT(va_attribution_tag), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
573 | {.attr = ATTR_CMNEXT_CLONE_REFCNT, .bits = VATTR_BIT(va_clone_refcnt), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
574 | {.attr = 0, .bits = 0, .size = 0, .action = 0} |
575 | }; |
576 | |
577 | /* |
578 | * This table is for attributes which are only set from the getattrlistbulk(2) |
579 | * call. These attributes have already been set from the common, file and |
580 | * directory tables but the vattr bits have not been recorded. Since these |
581 | * vattr bits are only used from the bulk call, we have a seperate table for |
582 | * these. |
583 | * The sizes are not returned from here since the sizes have already been |
584 | * accounted from the common, file and directory tables. |
585 | */ |
586 | static struct getattrlist_attrtab getattrlistbulk_common_tab[] = { |
587 | {.attr = ATTR_CMN_DEVID, .bits = VATTR_BIT(va_devid), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
588 | {.attr = ATTR_CMN_FSID, .bits = VATTR_BIT(va_fsid64), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
589 | {.attr = ATTR_CMN_OBJTYPE, .bits = VATTR_BIT(va_objtype), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
590 | {.attr = ATTR_CMN_OBJTAG, .bits = VATTR_BIT(va_objtag), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
591 | {.attr = ATTR_CMN_USERACCESS, .bits = VATTR_BIT(va_user_access), .size = 0, .action = 0}, |
592 | {.attr = ATTR_CMN_FNDRINFO, .bits = VATTR_BIT(va_finderinfo), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
593 | {.attr = 0, .bits = 0, .size = 0, .action = 0} |
594 | }; |
595 | |
596 | static struct getattrlist_attrtab getattrlistbulk_file_tab[] = { |
597 | {.attr = ATTR_FILE_RSRCLENGTH, .bits = VATTR_BIT(va_rsrc_length), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
598 | {.attr = ATTR_FILE_RSRCALLOCSIZE, .bits = VATTR_BIT(va_rsrc_alloc), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES}, |
599 | {.attr = 0, .bits = 0, .size = 0, .action = 0} |
600 | }; |
601 | |
602 | static struct getattrlist_attrtab getattrlistbulk_common_extended_tab[] = { |
603 | /* getattrlist_parsetab() expects > 1 entries */ |
604 | {.attr = 0, .bits = 0, .size = 0, .action = 0}, |
605 | {.attr = 0, .bits = 0, .size = 0, .action = 0} |
606 | }; |
607 | |
608 | /* |
609 | * The following are attributes that VFS can derive. |
610 | * |
611 | * A majority of them are the same attributes that are required for stat(2) and statfs(2). |
612 | */ |
613 | #define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_FSTYPENAME | ATTR_VOL_SIGNATURE | \ |
614 | ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | ATTR_VOL_QUOTA_SIZE | ATTR_VOL_RESERVED_SIZE | \ |
615 | ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \ |
616 | ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \ |
617 | ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \ |
618 | ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \ |
619 | ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED) |
620 | |
621 | #define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \ |
622 | ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \ |
623 | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \ |
624 | ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \ |
625 | ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \ |
626 | ATTR_CMN_FNDRINFO | \ |
627 | ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \ |
628 | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \ |
629 | ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \ |
630 | ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS | \ |
631 | ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT | \ |
632 | ATTR_CMN_DATA_PROTECT_FLAGS) |
633 | |
634 | #define VFS_DFLT_ATTR_CMN_EXT (ATTR_CMNEXT_PRIVATESIZE | ATTR_CMNEXT_LINKID | \ |
635 | ATTR_CMNEXT_NOFIRMLINKPATH | ATTR_CMNEXT_REALDEVID | \ |
636 | ATTR_CMNEXT_REALFSID | ATTR_CMNEXT_CLONEID | \ |
637 | ATTR_CMNEXT_EXT_FLAGS) |
638 | |
639 | #define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS) |
640 | |
641 | #define VFS_DFLT_ATTR_FILE (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | \ |
642 | ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \ |
643 | ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \ |
644 | ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \ |
645 | ATTR_FILE_RSRCALLOCSIZE) |
646 | |
647 | static int |
648 | getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs, |
649 | struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, |
650 | int is_64bit, unsigned int maxiter) |
651 | { |
652 | attrgroup_t recognised; |
653 | recognised = 0; |
654 | if (!tab) { |
655 | return EINVAL; |
656 | } |
657 | |
658 | do { |
659 | /* is this attribute set? */ |
660 | if (tab->attr & attrs) { |
661 | recognised |= tab->attr; |
662 | if (vap) { |
663 | vap->va_active |= tab->bits; |
664 | } |
665 | if (sizep) { |
666 | if (tab->size == ATTR_TIME_SIZE) { |
667 | if (is_64bit) { |
668 | *sizep += sizeof( |
669 | struct user64_timespec); |
670 | } else { |
671 | *sizep += sizeof( |
672 | struct user32_timespec); |
673 | } |
674 | } else { |
675 | *sizep += tab->size; |
676 | } |
677 | } |
678 | if (actionp) { |
679 | *actionp |= tab->action; |
680 | } |
681 | if (attrs == recognised) { |
682 | break; /* all done, get out */ |
683 | } |
684 | } |
685 | } while (((++tab)->attr != 0) && (--maxiter > 0)); |
686 | |
687 | /* check to make sure that we recognised all of the passed-in attributes */ |
688 | if (attrs & ~recognised) { |
689 | return EINVAL; |
690 | } |
691 | return 0; |
692 | } |
693 | |
694 | /* |
695 | * Given the attributes listed in alp, configure vap to request |
696 | * the data from a filesystem. |
697 | */ |
698 | static int |
699 | getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir, int use_fork) |
700 | { |
701 | int error; |
702 | |
703 | /* |
704 | * Parse the above tables. |
705 | */ |
706 | *sizep = sizeof(uint32_t); /* length count */ |
707 | *actionp = 0; |
708 | if (alp->commonattr && |
709 | (error = getattrlist_parsetab(tab: getattrlist_common_tab, attrs: alp->commonattr, vap, sizep, actionp, is_64bit, maxiter: sizeof(getattrlist_common_tab) / sizeof(getattrlist_common_tab[0]))) != 0) { |
710 | return error; |
711 | } |
712 | if (isdir && alp->dirattr && |
713 | (error = getattrlist_parsetab(tab: getattrlist_dir_tab, attrs: alp->dirattr, vap, sizep, actionp, is_64bit, maxiter: sizeof(getattrlist_dir_tab) / sizeof(getattrlist_dir_tab[0]))) != 0) { |
714 | return error; |
715 | } |
716 | if (!isdir && alp->fileattr && |
717 | (error = getattrlist_parsetab(tab: getattrlist_file_tab, attrs: alp->fileattr, vap, sizep, actionp, is_64bit, maxiter: sizeof(getattrlist_file_tab) / sizeof(getattrlist_file_tab[0]))) != 0) { |
718 | return error; |
719 | } |
720 | if (use_fork && alp->forkattr && |
721 | (error = getattrlist_parsetab(tab: getattrlist_common_extended_tab, attrs: alp->forkattr, vap, sizep, actionp, is_64bit, maxiter: sizeof(getattrlist_common_extended_tab) / sizeof(getattrlist_common_extended_tab[0]))) != 0) { |
722 | return error; |
723 | } |
724 | |
725 | return 0; |
726 | } |
727 | |
728 | /* |
729 | * Given the attributes listed in alp, configure vap to request |
730 | * the data from a filesystem. |
731 | */ |
732 | static int |
733 | getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap, |
734 | enum vtype obj_type, ssize_t *fixedsize, int is_64bit, int use_fork) |
735 | { |
736 | int error = 0; |
737 | |
738 | /* |
739 | * Parse the above tables. |
740 | */ |
741 | if (fixedsize) { |
742 | *fixedsize = sizeof(uint32_t); |
743 | } |
744 | if (alp->commonattr) { |
745 | error = getattrlist_parsetab(tab: getattrlist_common_tab, |
746 | attrs: alp->commonattr, vap, sizep: fixedsize, NULL, is_64bit, |
747 | maxiter: sizeof(getattrlist_common_tab) / sizeof(getattrlist_common_tab[0])); |
748 | |
749 | if (!error) { |
750 | /* Ignore any errrors from the bulk table */ |
751 | (void)getattrlist_parsetab(tab: getattrlistbulk_common_tab, |
752 | attrs: alp->commonattr, vap, sizep: fixedsize, NULL, is_64bit, |
753 | maxiter: sizeof(getattrlistbulk_common_tab) / sizeof(getattrlistbulk_common_tab[0])); |
754 | } |
755 | } |
756 | |
757 | if (!error && (obj_type == VNON || obj_type == VDIR) && alp->dirattr) { |
758 | error = getattrlist_parsetab(tab: getattrlist_dir_tab, attrs: alp->dirattr, |
759 | vap, sizep: fixedsize, NULL, is_64bit, |
760 | maxiter: sizeof(getattrlist_dir_tab) / sizeof(getattrlist_dir_tab[0])); |
761 | } |
762 | |
763 | if (!error && (obj_type != VDIR) && alp->fileattr) { |
764 | error = getattrlist_parsetab(tab: getattrlist_file_tab, |
765 | attrs: alp->fileattr, vap, sizep: fixedsize, NULL, is_64bit, |
766 | maxiter: sizeof(getattrlist_file_tab) / sizeof(getattrlist_file_tab[0])); |
767 | |
768 | if (!error) { |
769 | /*Ignore any errors from the bulk table */ |
770 | (void)getattrlist_parsetab(tab: getattrlistbulk_file_tab, |
771 | attrs: alp->fileattr, vap, sizep: fixedsize, NULL, is_64bit, |
772 | maxiter: sizeof(getattrlistbulk_file_tab) / sizeof(getattrlistbulk_file_tab[0])); |
773 | } |
774 | } |
775 | |
776 | /* fork attributes are like extended common attributes if enabled*/ |
777 | if (!error && use_fork && alp->forkattr) { |
778 | error = getattrlist_parsetab(tab: getattrlist_common_extended_tab, |
779 | attrs: alp->forkattr, vap, sizep: fixedsize, NULL, is_64bit, |
780 | maxiter: sizeof(getattrlist_common_extended_tab) / sizeof(getattrlist_common_extended_tab[0])); |
781 | |
782 | if (!error) { |
783 | (void)getattrlist_parsetab(tab: getattrlistbulk_common_extended_tab, |
784 | attrs: alp->forkattr, vap, sizep: fixedsize, NULL, is_64bit, |
785 | maxiter: sizeof(getattrlistbulk_common_extended_tab) / sizeof(getattrlistbulk_common_extended_tab[0])); |
786 | } |
787 | } |
788 | |
789 | return error; |
790 | } |
791 | |
792 | int |
793 | vfs_setup_vattr_from_attrlist(struct attrlist *alp, struct vnode_attr *vap, |
794 | enum vtype obj_vtype, ssize_t *attrs_fixed_sizep, vfs_context_t ctx) |
795 | { |
796 | VATTR_INIT(vap); |
797 | |
798 | // the caller passes us no options, we assume the caller wants the new fork |
799 | // attr behavior, hence the hardcoded 1 |
800 | return getattrlist_setupvattr_all(alp, vap, obj_type: obj_vtype, |
801 | fixedsize: attrs_fixed_sizep, is_64bit: vfs_context_is64bit(ctx), use_fork: 1); |
802 | } |
803 | |
804 | |
805 | |
806 | |
807 | /* |
808 | * Given the attributes listed in asp and those supported |
809 | * in the vap, fixup the asp attributes to reflect any |
810 | * missing attributes from the file system |
811 | */ |
812 | static void |
813 | getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap, int use_fork) |
814 | { |
815 | struct getattrlist_attrtab *tab; |
816 | |
817 | if (asp->commonattr) { |
818 | tab = getattrlist_common_tab; |
819 | do { |
820 | /* |
821 | * This if() statement is slightly confusing. We're trying to |
822 | * iterate through all of the bits listed in the array |
823 | * getattr_common_tab, and see if the filesystem was expected |
824 | * to support it, and whether or not we need to do anything about this. |
825 | * |
826 | * This array is full of structs that have 4 fields (attr, bits, size, action). |
827 | * The first is used to store the ATTR_CMN_* bit that was being requested |
828 | * from userland. The second stores the VATTR_BIT corresponding to the field |
829 | * filled in vnode_attr struct. If it is 0, then we don't typically expect |
830 | * the filesystem to fill in this field. The third is the size of the field, |
831 | * and the fourth is the type of kauth actions needed. |
832 | * |
833 | * So, for all of the ATTR_CMN bits listed in this array, we iterate through |
834 | * them, and check to see if it was both passed down to the filesystem via the |
835 | * va_active bitfield, and whether or not we expect it to be emitted from |
836 | * the filesystem. If it wasn't supported, then we un-twiddle the bit and move |
837 | * on. This is done so that we can uncheck those bits and re-request |
838 | * a vnode_getattr from the filesystem again. |
839 | */ |
840 | if ((tab->attr & asp->commonattr) && |
841 | (tab->bits & vap->va_active) && |
842 | (tab->bits & vap->va_supported) == 0) { |
843 | asp->commonattr &= ~tab->attr; |
844 | } |
845 | } while ((++tab)->attr != 0); |
846 | } |
847 | if (asp->dirattr) { |
848 | tab = getattrlist_dir_tab; |
849 | do { |
850 | if ((tab->attr & asp->dirattr) && |
851 | (tab->bits & vap->va_active) && |
852 | (vap->va_supported & tab->bits) == 0) { |
853 | asp->dirattr &= ~tab->attr; |
854 | } |
855 | } while ((++tab)->attr != 0); |
856 | } |
857 | if (asp->fileattr) { |
858 | tab = getattrlist_file_tab; |
859 | do { |
860 | if ((tab->attr & asp->fileattr) && |
861 | (tab->bits & vap->va_active) && |
862 | (vap->va_supported & tab->bits) == 0) { |
863 | asp->fileattr &= ~tab->attr; |
864 | } |
865 | } while ((++tab)->attr != 0); |
866 | } |
867 | if (use_fork && asp->forkattr) { |
868 | tab = getattrlist_common_extended_tab; |
869 | do { |
870 | if ((tab->attr & asp->forkattr) && |
871 | (tab->bits & vap->va_active) && |
872 | (vap->va_supported & tab->bits) == 0) { |
873 | asp->forkattr &= ~tab->attr; |
874 | } |
875 | } while ((++tab)->attr != 0); |
876 | } |
877 | } |
878 | |
879 | static int |
880 | setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx) |
881 | { |
882 | uio_t auio; |
883 | UIO_STACKBUF(uio_buf, 1); |
884 | int error; |
885 | |
886 | if ((auio = uio_createwithbuffer(a_iovcount: 1, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_WRITE, a_buf_p: uio_buf, a_buffer_size: sizeof(uio_buf))) == NULL) { |
887 | error = ENOMEM; |
888 | } else { |
889 | uio_addiov(a_uio: auio, CAST_USER_ADDR_T(fndrinfo), a_length: 32); |
890 | error = vn_setxattr(vp, XATTR_FINDERINFO_NAME, auio, XATTR_NOSECURITY, ctx); |
891 | uio_free(a_uio: auio); |
892 | } |
893 | |
894 | #if CONFIG_FSE |
895 | if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) { |
896 | add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE); |
897 | } |
898 | #endif |
899 | return error; |
900 | } |
901 | |
902 | |
903 | /* |
904 | * Find something resembling a terminal component name in the mountedonname for vp |
905 | * |
906 | */ |
907 | static void |
908 | getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl) |
909 | { |
910 | int counting; |
911 | const char *cp; |
912 | |
913 | /* |
914 | * We're looking for the last sequence of non / characters, but |
915 | * not including any trailing / characters. |
916 | */ |
917 | *np = NULL; |
918 | *nl = 0; |
919 | counting = 0; |
920 | for (cp = mn; *cp != 0; cp++) { |
921 | if (!counting) { |
922 | /* start of run of chars */ |
923 | if (*cp != '/') { |
924 | *np = cp; |
925 | counting = 1; |
926 | } |
927 | } else { |
928 | /* end of run of chars */ |
929 | if (*cp == '/') { |
930 | *nl = cp - *np; |
931 | counting = 0; |
932 | } |
933 | } |
934 | } |
935 | /* need to close run? */ |
936 | if (counting) { |
937 | *nl = cp - *np; |
938 | } |
939 | } |
940 | |
941 | |
942 | static int |
943 | getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, |
944 | user_addr_t attributeBuffer, size_t bufferSize, uint64_t options, |
945 | enum uio_seg segflg, int is_64bit) |
946 | { |
947 | struct vfs_attr vs = {}; |
948 | struct vnode_attr va; |
949 | struct _attrlist_buf ab; |
950 | int error; |
951 | ssize_t fixedsize, varsize; |
952 | const char *cnp = NULL; /* protected by ATTR_CMN_NAME */ |
953 | ssize_t cnl = 0; /* protected by ATTR_CMN_NAME */ |
954 | int release_str = 0; |
955 | mount_t mnt; |
956 | int return_valid; |
957 | int pack_invalid; |
958 | vnode_t root_vp = NULL; |
959 | const char *fstypename = NULL; |
960 | size_t fstypenamelen = 0; |
961 | |
962 | _ATTRLIST_BUF_INIT(&ab); |
963 | VATTR_INIT(&va); |
964 | VFSATTR_INIT(&vs); |
965 | vs.f_vol_name = NULL; |
966 | mnt = vp->v_mount; |
967 | |
968 | |
969 | /* Check for special packing semantics */ |
970 | return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS); |
971 | pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS); |
972 | if (pack_invalid) { |
973 | /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */ |
974 | if (!return_valid) { |
975 | error = EINVAL; |
976 | goto out; |
977 | } |
978 | /* Keep invalid attrs from being uninitialized */ |
979 | bzero(s: &vs, n: sizeof(vs)); |
980 | /* Generate a valid mask for post processing */ |
981 | bcopy(src: &alp->commonattr, dst: &ab.valid, n: sizeof(attribute_set_t)); |
982 | } |
983 | |
984 | /* If we do not have root vnode, look it up and substitute it in */ |
985 | if (!vnode_isvroot(vp)) { |
986 | if (mnt != NULL) { |
987 | error = VFS_ROOT(mnt, &root_vp, ctx); |
988 | if (error) { |
989 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but got an error getting root." ); |
990 | goto out; |
991 | } |
992 | vp = root_vp; |
993 | } else { |
994 | error = EINVAL; |
995 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but no backpointer to mount." ); |
996 | goto out; |
997 | } |
998 | } |
999 | |
1000 | /* |
1001 | * Set up the vfs_attr structure and call the filesystem. |
1002 | */ |
1003 | if ((error = getvolattrlist_setupvfsattr(alp, vsp: &vs, sizep: &fixedsize, is_64bit)) != 0) { |
1004 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed" ); |
1005 | goto out; |
1006 | } |
1007 | if (vs.f_active != 0) { |
1008 | /* If we're going to ask for f_vol_name, allocate a buffer to point it at */ |
1009 | if (VFSATTR_IS_ACTIVE(&vs, f_vol_name)) { |
1010 | vs.f_vol_name = (char *) zalloc(view: ZV_NAMEI); |
1011 | vs.f_vol_name[0] = '\0'; |
1012 | } |
1013 | |
1014 | VFS_DEBUG(ctx, vp, "ATTRLIST - calling to get %016llx with supported %016llx" , vs.f_active, vs.f_supported); |
1015 | if ((error = vfs_getattr(mp: mnt, vfa: &vs, ctx)) != 0) { |
1016 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d" , error); |
1017 | goto out; |
1018 | } |
1019 | #if CONFIG_MACF |
1020 | error = mac_mount_check_getattr(ctx, mp: mnt, vfa: &vs); |
1021 | if (error != 0) { |
1022 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d" , error); |
1023 | goto out; |
1024 | } |
1025 | #endif |
1026 | /* |
1027 | * Did we ask for something the filesystem doesn't support? |
1028 | */ |
1029 | if (!VFSATTR_ALL_SUPPORTED(&vs)) { |
1030 | /* default value for volume subtype */ |
1031 | if (VFSATTR_IS_ACTIVE(&vs, f_fssubtype) |
1032 | && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype)) { |
1033 | VFSATTR_RETURN(&vs, f_fssubtype, 0); |
1034 | } |
1035 | |
1036 | /* |
1037 | * If the file system didn't supply f_signature, then |
1038 | * default it to 'BD', which is the generic signature |
1039 | * that most Carbon file systems should return. |
1040 | */ |
1041 | if (VFSATTR_IS_ACTIVE(&vs, f_signature) |
1042 | && !VFSATTR_IS_SUPPORTED(&vs, f_signature)) { |
1043 | VFSATTR_RETURN(&vs, f_signature, 0x4244); |
1044 | } |
1045 | |
1046 | /* default for block size */ |
1047 | if (VFSATTR_IS_ACTIVE(&vs, f_bsize) |
1048 | && !VFSATTR_IS_SUPPORTED(&vs, f_bsize)) { |
1049 | VFSATTR_RETURN(&vs, f_bsize, mnt->mnt_devblocksize); |
1050 | } |
1051 | |
1052 | /* default value for blocks used */ |
1053 | if (VFSATTR_IS_ACTIVE(&vs, f_bused) |
1054 | && !VFSATTR_IS_SUPPORTED(&vs, f_bused)) { |
1055 | VFSATTR_RETURN(&vs, f_bused, mnt->mnt_vfsstat.f_blocks - vs.f_bfree); |
1056 | } |
1057 | |
1058 | /* default value for volume f_attributes */ |
1059 | if (VFSATTR_IS_ACTIVE(&vs, f_attributes) |
1060 | && !VFSATTR_IS_SUPPORTED(&vs, f_attributes)) { |
1061 | vol_attributes_attr_t *attrp = &vs.f_attributes; |
1062 | |
1063 | attrp->validattr.commonattr = VFS_DFLT_ATTR_CMN; |
1064 | attrp->validattr.volattr = VFS_DFLT_ATTR_VOL; |
1065 | attrp->validattr.dirattr = VFS_DFLT_ATTR_DIR; |
1066 | attrp->validattr.fileattr = VFS_DFLT_ATTR_FILE; |
1067 | attrp->validattr.forkattr = VFS_DFLT_ATTR_CMN_EXT; |
1068 | |
1069 | attrp->nativeattr.commonattr = 0; |
1070 | attrp->nativeattr.volattr = 0; |
1071 | attrp->nativeattr.dirattr = 0; |
1072 | attrp->nativeattr.fileattr = 0; |
1073 | attrp->nativeattr.forkattr = 0; |
1074 | VFSATTR_SET_SUPPORTED(&vs, f_attributes); |
1075 | } |
1076 | |
1077 | /* default value for volume f_capabilities */ |
1078 | if (VFSATTR_IS_ACTIVE(&vs, f_capabilities)) { |
1079 | /* getattrlist is always supported now. */ |
1080 | if (!VFSATTR_IS_SUPPORTED(&vs, f_capabilities)) { |
1081 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = 0; |
1082 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST; |
1083 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0; |
1084 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0; |
1085 | |
1086 | vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0; |
1087 | vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST; |
1088 | vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0; |
1089 | vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0; |
1090 | VFSATTR_SET_SUPPORTED(&vs, f_capabilities); |
1091 | } else { |
1092 | /* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */ |
1093 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST; |
1094 | vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST; |
1095 | } |
1096 | } |
1097 | |
1098 | /* check to see if our fixups were enough */ |
1099 | if (!VFSATTR_ALL_SUPPORTED(&vs)) { |
1100 | if (return_valid) { |
1101 | if (pack_invalid) { |
1102 | /* Fix up valid mask for post processing */ |
1103 | getvolattrlist_fixupattrs(asp: &ab.valid, vsp: &vs); |
1104 | |
1105 | /* Force packing of everything asked for */ |
1106 | vs.f_supported = vs.f_active; |
1107 | } else { |
1108 | /* Adjust the requested attributes */ |
1109 | getvolattrlist_fixupattrs(asp: (attribute_set_t *)&alp->commonattr, vsp: &vs); |
1110 | } |
1111 | } else { |
1112 | error = EINVAL; |
1113 | goto out; |
1114 | } |
1115 | } |
1116 | } |
1117 | } |
1118 | |
1119 | /* |
1120 | * Some fields require data from the root vp |
1121 | */ |
1122 | if (alp->commonattr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | ATTR_CMN_SCRIPT)) { |
1123 | VATTR_WANTED(&va, va_uid); |
1124 | VATTR_WANTED(&va, va_gid); |
1125 | VATTR_WANTED(&va, va_mode); |
1126 | VATTR_WANTED(&va, va_flags); |
1127 | VATTR_WANTED(&va, va_encoding); |
1128 | |
1129 | if ((error = vnode_getattr(vp, vap: &va, ctx)) != 0) { |
1130 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode" , vp); |
1131 | goto out; |
1132 | } |
1133 | #if CONFIG_MACF |
1134 | error = mac_vnode_check_getattr(ctx, NOCRED, vp, va: &va); |
1135 | if (error != 0) { |
1136 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d for root vnode" , error); |
1137 | goto out; |
1138 | } |
1139 | #endif |
1140 | if (VATTR_IS_ACTIVE(&va, va_encoding) && |
1141 | !VATTR_IS_SUPPORTED(&va, va_encoding)) { |
1142 | if (!return_valid || pack_invalid) { |
1143 | /* use kTextEncodingMacUnicode */ |
1144 | VATTR_RETURN(&va, va_encoding, 0x7e); |
1145 | } else { |
1146 | /* don't use a default */ |
1147 | alp->commonattr &= ~ATTR_CMN_SCRIPT; |
1148 | } |
1149 | } |
1150 | } |
1151 | |
1152 | /* |
1153 | * Compute variable-size buffer requirements. |
1154 | */ |
1155 | varsize = 0; |
1156 | if (alp->commonattr & ATTR_CMN_NAME) { |
1157 | if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 && |
1158 | vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') { |
1159 | /* special case for boot volume. Use root name when it's |
1160 | * available (which is the volume name) or just the mount on |
1161 | * name of "/". we must do this for binary compatibility with |
1162 | * pre Tiger code. returning nothing for the boot volume name |
1163 | * breaks installers - 3961058 |
1164 | */ |
1165 | cnp = vnode_getname(vp); |
1166 | if (cnp == NULL) { |
1167 | /* just use "/" as name */ |
1168 | cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0]; |
1169 | } else { |
1170 | release_str = 1; |
1171 | } |
1172 | cnl = strlen(s: cnp); |
1173 | } else { |
1174 | getattrlist_findnamecomp(mn: vp->v_mount->mnt_vfsstat.f_mntonname, np: &cnp, nl: &cnl); |
1175 | } |
1176 | if (alp->commonattr & ATTR_CMN_NAME) { |
1177 | varsize += roundup(cnl + 1, 4); |
1178 | } |
1179 | } |
1180 | if (alp->volattr & ATTR_VOL_MOUNTPOINT) { |
1181 | varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntonname) + 1, 4); |
1182 | } |
1183 | if (alp->volattr & ATTR_VOL_NAME) { |
1184 | vs.f_vol_name[MAXPATHLEN - 1] = '\0'; /* Ensure nul-termination */ |
1185 | varsize += roundup(strlen(vs.f_vol_name) + 1, 4); |
1186 | } |
1187 | if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) { |
1188 | varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4); |
1189 | } |
1190 | if (alp->volattr & ATTR_VOL_FSTYPENAME) { |
1191 | mount_lock_spin(mnt); |
1192 | fstypename = vfs_getfstypenameref_locked(mp: mnt, lenp: &fstypenamelen); |
1193 | mount_unlock(mnt); |
1194 | varsize += roundup(fstypenamelen + 1, 4); |
1195 | } |
1196 | |
1197 | /* |
1198 | * Allocate a target buffer for attribute results. |
1199 | * Note that since we won't ever copy out more than the caller requested, |
1200 | * we never need to allocate more than they offer. |
1201 | */ |
1202 | ab.allocated = fixedsize + varsize; |
1203 | if (((size_t)ab.allocated) > ATTR_MAX_BUFFER) { |
1204 | error = ENOMEM; |
1205 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)" , ab.allocated, ATTR_MAX_BUFFER); |
1206 | goto out; |
1207 | } |
1208 | |
1209 | if (return_valid && |
1210 | (ab.allocated < (ssize_t)(sizeof(uint32_t) + sizeof(attribute_set_t))) && |
1211 | !(options & FSOPT_REPORT_FULLSIZE)) { |
1212 | uint32_t num_bytes_valid = sizeof(uint32_t); |
1213 | /* |
1214 | * Not enough to return anything and we don't have to report |
1215 | * how much space is needed. Get out now. |
1216 | * N.B. - We have only been called after having verified that |
1217 | * attributeBuffer is at least sizeof(uint32_t); |
1218 | */ |
1219 | if (UIO_SEG_IS_USER_SPACE(segflg)) { |
1220 | error = copyout(&num_bytes_valid, |
1221 | CAST_USER_ADDR_T(attributeBuffer), num_bytes_valid); |
1222 | } else { |
1223 | bcopy(src: &num_bytes_valid, dst: (void *)attributeBuffer, |
1224 | n: (size_t)num_bytes_valid); |
1225 | } |
1226 | goto out; |
1227 | } |
1228 | |
1229 | ab.base = kalloc_data(ab.allocated, Z_ZERO | Z_WAITOK); |
1230 | if (ab.base == NULL) { |
1231 | error = ENOMEM; |
1232 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer" , ab.allocated); |
1233 | goto out; |
1234 | } |
1235 | |
1236 | /* |
1237 | * Pack results into the destination buffer. |
1238 | */ |
1239 | ab.fixedcursor = ab.base + sizeof(uint32_t); |
1240 | if (return_valid) { |
1241 | ab.fixedcursor += sizeof(attribute_set_t); |
1242 | } |
1243 | |
1244 | bzero(s: &ab.actual, n: sizeof(ab.actual)); |
1245 | |
1246 | ab.varcursor = ab.base + fixedsize; |
1247 | ab.needed = fixedsize + varsize; |
1248 | |
1249 | /* common attributes **************************************************/ |
1250 | if (alp->commonattr & ATTR_CMN_ERROR) { |
1251 | ATTR_PACK4(ab, 0); |
1252 | ab.actual.commonattr |= ATTR_CMN_ERROR; |
1253 | } |
1254 | if (alp->commonattr & ATTR_CMN_NAME) { |
1255 | attrlist_pack_string(ab: &ab, source: cnp, count: cnl); |
1256 | ab.actual.commonattr |= ATTR_CMN_NAME; |
1257 | } |
1258 | if (alp->commonattr & ATTR_CMN_DEVID) { |
1259 | ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]); |
1260 | ab.actual.commonattr |= ATTR_CMN_DEVID; |
1261 | } |
1262 | if (alp->commonattr & ATTR_CMN_FSID) { |
1263 | ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid); |
1264 | ab.actual.commonattr |= ATTR_CMN_FSID; |
1265 | } |
1266 | if (alp->commonattr & ATTR_CMN_OBJTYPE) { |
1267 | if (!return_valid || pack_invalid) { |
1268 | ATTR_PACK4(ab, 0); |
1269 | } |
1270 | } |
1271 | if (alp->commonattr & ATTR_CMN_OBJTAG) { |
1272 | ATTR_PACK4(ab, vp->v_tag); |
1273 | ab.actual.commonattr |= ATTR_CMN_OBJTAG; |
1274 | } |
1275 | if (alp->commonattr & ATTR_CMN_OBJID) { |
1276 | if (!return_valid || pack_invalid) { |
1277 | fsobj_id_t f = {0, 0}; |
1278 | ATTR_PACK8(ab, f); |
1279 | } |
1280 | } |
1281 | if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) { |
1282 | if (!return_valid || pack_invalid) { |
1283 | fsobj_id_t f = {0, 0}; |
1284 | ATTR_PACK8(ab, f); |
1285 | } |
1286 | } |
1287 | if (alp->commonattr & ATTR_CMN_PAROBJID) { |
1288 | if (!return_valid || pack_invalid) { |
1289 | fsobj_id_t f = {0, 0}; |
1290 | ATTR_PACK8(ab, f); |
1291 | } |
1292 | } |
1293 | /* note that this returns the encoding for the volume name, not the node name */ |
1294 | if (alp->commonattr & ATTR_CMN_SCRIPT) { |
1295 | OS_ANALYZER_SUPPRESS("80178956" ) ATTR_PACK4(ab, va.va_encoding); |
1296 | ab.actual.commonattr |= ATTR_CMN_SCRIPT; |
1297 | } |
1298 | if (alp->commonattr & ATTR_CMN_CRTIME) { |
1299 | ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit); |
1300 | ab.actual.commonattr |= ATTR_CMN_CRTIME; |
1301 | } |
1302 | if (alp->commonattr & ATTR_CMN_MODTIME) { |
1303 | ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit); |
1304 | ab.actual.commonattr |= ATTR_CMN_MODTIME; |
1305 | } |
1306 | if (alp->commonattr & ATTR_CMN_CHGTIME) { |
1307 | if (!return_valid || pack_invalid) { |
1308 | ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit); |
1309 | } |
1310 | } |
1311 | if (alp->commonattr & ATTR_CMN_ACCTIME) { |
1312 | ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit); |
1313 | ab.actual.commonattr |= ATTR_CMN_ACCTIME; |
1314 | } |
1315 | if (alp->commonattr & ATTR_CMN_BKUPTIME) { |
1316 | ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit); |
1317 | ab.actual.commonattr |= ATTR_CMN_BKUPTIME; |
1318 | } |
1319 | if (alp->commonattr & ATTR_CMN_FNDRINFO) { |
1320 | char f[32]; |
1321 | /* |
1322 | * This attribute isn't really Finder Info, at least for HFS. |
1323 | */ |
1324 | if (vp->v_tag == VT_HFS) { |
1325 | #define HFS_GET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00004) |
1326 | error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, data: (caddr_t)&f, fflag: 0, ctx); |
1327 | if (error == 0) { |
1328 | attrlist_pack_fixed(ab: &ab, source: f, count: sizeof(f)); |
1329 | ab.actual.commonattr |= ATTR_CMN_FNDRINFO; |
1330 | } else if (!return_valid) { |
1331 | goto out; |
1332 | } |
1333 | } else if (!return_valid || pack_invalid) { |
1334 | /* XXX we could at least pass out the volume UUID here */ |
1335 | bzero(s: &f, n: sizeof(f)); |
1336 | attrlist_pack_fixed(ab: &ab, source: f, count: sizeof(f)); |
1337 | } |
1338 | } |
1339 | if (alp->commonattr & ATTR_CMN_OWNERID) { |
1340 | OS_ANALYZER_SUPPRESS("80178956" ) ATTR_PACK4(ab, va.va_uid); |
1341 | ab.actual.commonattr |= ATTR_CMN_OWNERID; |
1342 | } |
1343 | if (alp->commonattr & ATTR_CMN_GRPID) { |
1344 | OS_ANALYZER_SUPPRESS("80178956" ) ATTR_PACK4(ab, va.va_gid); |
1345 | ab.actual.commonattr |= ATTR_CMN_GRPID; |
1346 | } |
1347 | if (alp->commonattr & ATTR_CMN_ACCESSMASK) { |
1348 | OS_ANALYZER_SUPPRESS("80178956" ) ATTR_PACK_CAST(&ab, uint32_t, va.va_mode); |
1349 | ab.actual.commonattr |= ATTR_CMN_ACCESSMASK; |
1350 | } |
1351 | if (alp->commonattr & ATTR_CMN_FLAGS) { |
1352 | OS_ANALYZER_SUPPRESS("80178956" ) ATTR_PACK4(ab, va.va_flags); |
1353 | ab.actual.commonattr |= ATTR_CMN_FLAGS; |
1354 | } |
1355 | if (alp->commonattr & ATTR_CMN_USERACCESS) { /* XXX this is expensive and also duplicate work */ |
1356 | uint32_t perms = 0; |
1357 | if (vnode_isdir(vp)) { |
1358 | if (vnode_authorize(vp, NULL, |
1359 | KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0) { |
1360 | perms |= W_OK; |
1361 | } |
1362 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0) { |
1363 | perms |= R_OK; |
1364 | } |
1365 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0) { |
1366 | perms |= X_OK; |
1367 | } |
1368 | } else { |
1369 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0) { |
1370 | perms |= W_OK; |
1371 | } |
1372 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0) { |
1373 | perms |= R_OK; |
1374 | } |
1375 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0) { |
1376 | perms |= X_OK; |
1377 | } |
1378 | } |
1379 | #if CONFIG_MACF |
1380 | /* |
1381 | * Rather than MAC preceding DAC, in this case we want |
1382 | * the smallest set of permissions granted by both MAC & DAC |
1383 | * checks. We won't add back any permissions. |
1384 | */ |
1385 | if (perms & W_OK) { |
1386 | if (mac_vnode_check_access(ctx, vp, W_OK) != 0) { |
1387 | perms &= ~W_OK; |
1388 | } |
1389 | } |
1390 | if (perms & R_OK) { |
1391 | if (mac_vnode_check_access(ctx, vp, R_OK) != 0) { |
1392 | perms &= ~R_OK; |
1393 | } |
1394 | } |
1395 | if (perms & X_OK) { |
1396 | if (mac_vnode_check_access(ctx, vp, X_OK) != 0) { |
1397 | perms &= ~X_OK; |
1398 | } |
1399 | } |
1400 | #endif /* MAC */ |
1401 | KAUTH_DEBUG("ATTRLIST - returning user access %x" , perms); |
1402 | ATTR_PACK4(ab, perms); |
1403 | ab.actual.commonattr |= ATTR_CMN_USERACCESS; |
1404 | } |
1405 | /* |
1406 | * The following common volume attributes are only |
1407 | * packed when the pack_invalid mode is enabled. |
1408 | */ |
1409 | if (pack_invalid) { |
1410 | uint64_t fid = 0; |
1411 | |
1412 | if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) { |
1413 | attrlist_pack_variable(ab: &ab, NULL, count: 0); |
1414 | } |
1415 | if (alp->commonattr & ATTR_CMN_UUID) { |
1416 | ATTR_PACK(&ab, kauth_null_guid); |
1417 | } |
1418 | if (alp->commonattr & ATTR_CMN_GRPUUID) { |
1419 | ATTR_PACK(&ab, kauth_null_guid); |
1420 | } |
1421 | if (alp->commonattr & ATTR_CMN_FILEID) { |
1422 | ATTR_PACK8(ab, fid); |
1423 | } |
1424 | if (alp->commonattr & ATTR_CMN_PARENTID) { |
1425 | ATTR_PACK8(ab, fid); |
1426 | } |
1427 | } |
1428 | |
1429 | /* volume attributes **************************************************/ |
1430 | |
1431 | if (alp->volattr & ATTR_VOL_FSTYPE) { |
1432 | ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt)); |
1433 | ab.actual.volattr |= ATTR_VOL_FSTYPE; |
1434 | } |
1435 | if (alp->volattr & ATTR_VOL_SIGNATURE) { |
1436 | ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature); |
1437 | ab.actual.volattr |= ATTR_VOL_SIGNATURE; |
1438 | } |
1439 | if (alp->volattr & ATTR_VOL_SIZE) { |
1440 | ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks); |
1441 | ab.actual.volattr |= ATTR_VOL_SIZE; |
1442 | } |
1443 | if (alp->volattr & ATTR_VOL_SPACEFREE) { |
1444 | ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree); |
1445 | ab.actual.volattr |= ATTR_VOL_SPACEFREE; |
1446 | } |
1447 | if (alp->volattr & ATTR_VOL_SPACEAVAIL) { |
1448 | ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail); |
1449 | ab.actual.volattr |= ATTR_VOL_SPACEAVAIL; |
1450 | } |
1451 | if (alp->volattr & ATTR_VOL_SPACEUSED) { |
1452 | ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bused); |
1453 | ab.actual.volattr |= ATTR_VOL_SPACEUSED; |
1454 | } |
1455 | if (alp->volattr & ATTR_VOL_MINALLOCATION) { |
1456 | ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); |
1457 | ab.actual.volattr |= ATTR_VOL_MINALLOCATION; |
1458 | } |
1459 | if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP) { |
1460 | ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); /* not strictly true */ |
1461 | ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP; |
1462 | } |
1463 | if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) { |
1464 | ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize); |
1465 | ab.actual.volattr |= ATTR_VOL_IOBLOCKSIZE; |
1466 | } |
1467 | if (alp->volattr & ATTR_VOL_OBJCOUNT) { |
1468 | ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount); |
1469 | ab.actual.volattr |= ATTR_VOL_OBJCOUNT; |
1470 | } |
1471 | if (alp->volattr & ATTR_VOL_FILECOUNT) { |
1472 | ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount); |
1473 | ab.actual.volattr |= ATTR_VOL_FILECOUNT; |
1474 | } |
1475 | if (alp->volattr & ATTR_VOL_DIRCOUNT) { |
1476 | ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount); |
1477 | ab.actual.volattr |= ATTR_VOL_DIRCOUNT; |
1478 | } |
1479 | if (alp->volattr & ATTR_VOL_MAXOBJCOUNT) { |
1480 | ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount); |
1481 | ab.actual.volattr |= ATTR_VOL_MAXOBJCOUNT; |
1482 | } |
1483 | if (alp->volattr & ATTR_VOL_MOUNTPOINT) { |
1484 | attrlist_pack_string(ab: &ab, source: mnt->mnt_vfsstat.f_mntonname, count: 0); |
1485 | ab.actual.volattr |= ATTR_VOL_MOUNTPOINT; |
1486 | } |
1487 | if (alp->volattr & ATTR_VOL_NAME) { |
1488 | attrlist_pack_string(ab: &ab, source: vs.f_vol_name, count: 0); |
1489 | ab.actual.volattr |= ATTR_VOL_NAME; |
1490 | } |
1491 | if (alp->volattr & ATTR_VOL_MOUNTFLAGS) { |
1492 | ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag); |
1493 | ab.actual.volattr |= ATTR_VOL_MOUNTFLAGS; |
1494 | } |
1495 | if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) { |
1496 | attrlist_pack_string(ab: &ab, source: mnt->mnt_vfsstat.f_mntfromname, count: 0); |
1497 | ab.actual.volattr |= ATTR_VOL_MOUNTEDDEVICE; |
1498 | } |
1499 | if (alp->volattr & ATTR_VOL_ENCODINGSUSED) { |
1500 | if (!return_valid || pack_invalid) { |
1501 | ATTR_PACK_CAST(&ab, uint64_t, ~0LL); /* return all encodings */ |
1502 | } |
1503 | } |
1504 | if (alp->volattr & ATTR_VOL_CAPABILITIES) { |
1505 | /* fix up volume capabilities */ |
1506 | if (vfs_extendedsecurity(mnt)) { |
1507 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY; |
1508 | } else { |
1509 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY; |
1510 | } |
1511 | vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY; |
1512 | |
1513 | /* |
1514 | * if the filesystem doesn't mark either VOL_CAP_FMT_NO_IMMUTABLE_FILES |
1515 | * or VOL_CAP_FMT_NO_PERMISSIONS as valid, assume they're not supported |
1516 | */ |
1517 | if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_IMMUTABLE_FILES)) { |
1518 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_IMMUTABLE_FILES; |
1519 | vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_IMMUTABLE_FILES; |
1520 | } |
1521 | |
1522 | if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_PERMISSIONS)) { |
1523 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_PERMISSIONS; |
1524 | vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_PERMISSIONS; |
1525 | } |
1526 | |
1527 | /* |
1528 | * ATTR_CMN_USERACCESS attribute was previously set by file-system drivers, thus volume capabilitiy |
1529 | * VOL_CAP_INT_USERACCESS was conditionally enabled. ATTR_CMN_USERACCESS is now set inside VFS, |
1530 | * regardless of underlying volume type thus we always set VOL_CAP_INT_USERACCESS. |
1531 | */ |
1532 | vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_USERACCESS; |
1533 | vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_USERACCESS; |
1534 | |
1535 | ATTR_PACK(&ab, vs.f_capabilities); |
1536 | ab.actual.volattr |= ATTR_VOL_CAPABILITIES; |
1537 | } |
1538 | if (alp->volattr & ATTR_VOL_UUID) { |
1539 | ATTR_PACK(&ab, vs.f_uuid); |
1540 | ab.actual.volattr |= ATTR_VOL_UUID; |
1541 | } |
1542 | if (alp->volattr & ATTR_VOL_QUOTA_SIZE) { |
1543 | ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_quota); |
1544 | ab.actual.volattr |= ATTR_VOL_QUOTA_SIZE; |
1545 | } |
1546 | if (alp->volattr & ATTR_VOL_RESERVED_SIZE) { |
1547 | ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_reserved); |
1548 | ab.actual.volattr |= ATTR_VOL_RESERVED_SIZE; |
1549 | } |
1550 | if (alp->volattr & ATTR_VOL_ATTRIBUTES) { |
1551 | /* fix up volume attribute information */ |
1552 | |
1553 | vs.f_attributes.validattr.commonattr |= VFS_DFLT_ATTR_CMN; |
1554 | vs.f_attributes.validattr.volattr |= VFS_DFLT_ATTR_VOL; |
1555 | vs.f_attributes.validattr.dirattr |= VFS_DFLT_ATTR_DIR; |
1556 | vs.f_attributes.validattr.fileattr |= VFS_DFLT_ATTR_FILE; |
1557 | |
1558 | if (vfs_extendedsecurity(mnt)) { |
1559 | vs.f_attributes.validattr.commonattr |= (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID); |
1560 | } else { |
1561 | vs.f_attributes.validattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID); |
1562 | vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID); |
1563 | } |
1564 | ATTR_PACK(&ab, vs.f_attributes); |
1565 | ab.actual.volattr |= ATTR_VOL_ATTRIBUTES; |
1566 | } |
1567 | if (alp->volattr & ATTR_VOL_FSTYPENAME) { |
1568 | size_t curlen; |
1569 | |
1570 | /* Verify that the reference didn't change. */ |
1571 | assert(fstypename != NULL); |
1572 | mount_lock_spin(mnt); |
1573 | if (vfs_getfstypenameref_locked(mp: mnt, lenp: &curlen) == fstypename && |
1574 | fstypenamelen == curlen) { |
1575 | attrlist_pack_string(ab: &ab, source: fstypename, count: 0); |
1576 | ab.actual.volattr |= ATTR_VOL_FSTYPENAME; |
1577 | mount_unlock(mnt); |
1578 | } else { |
1579 | mount_unlock(mnt); |
1580 | error = ERESTART; |
1581 | goto out; |
1582 | } |
1583 | } |
1584 | if (alp->volattr & ATTR_VOL_FSSUBTYPE) { |
1585 | ATTR_PACK(&ab, vs.f_fssubtype); |
1586 | ab.actual.volattr |= ATTR_VOL_FSSUBTYPE; |
1587 | } |
1588 | |
1589 | /* diagnostic */ |
1590 | if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize) { |
1591 | panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x" , |
1592 | fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr); |
1593 | } |
1594 | if (!return_valid && ab.varcursor != (ab.base + ab.needed)) { |
1595 | panic("packed variable field size mismatch; used %ld but expected %ld" , (long) (ab.varcursor - ab.base), ab.needed); |
1596 | } |
1597 | |
1598 | /* |
1599 | * In the compatible case, we report the smaller of the required and returned sizes. |
1600 | * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size |
1601 | * of the result buffer, even if we copied less out. The caller knows how big a buffer |
1602 | * they gave us, so they can always check for truncation themselves. |
1603 | */ |
1604 | *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? (uint32_t)ab.needed : (uint32_t)lmin(a: bufferSize, b: ab.needed); |
1605 | |
1606 | /* Return attribute set output if requested. */ |
1607 | if (return_valid && |
1608 | (ab.allocated >= (ssize_t)(sizeof(uint32_t) + sizeof(ab.actual)))) { |
1609 | ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS; |
1610 | if (pack_invalid) { |
1611 | /* Only report the attributes that are valid */ |
1612 | ab.actual.commonattr &= ab.valid.commonattr; |
1613 | ab.actual.volattr &= ab.valid.volattr; |
1614 | } |
1615 | bcopy(src: &ab.actual, dst: ab.base + sizeof(uint32_t), n: sizeof(ab.actual)); |
1616 | } |
1617 | |
1618 | if (UIO_SEG_IS_USER_SPACE(segflg)) { |
1619 | error = copyout(ab.base, CAST_USER_ADDR_T(attributeBuffer), |
1620 | ulmin((uint32_t)bufferSize, (uint32_t)ab.needed)); |
1621 | } else { |
1622 | bcopy(src: ab.base, dst: (void *)attributeBuffer, n: (size_t)ulmin(a: (uint32_t)bufferSize, b: (uint32_t)ab.needed)); |
1623 | } |
1624 | |
1625 | out: |
1626 | if (vs.f_vol_name != NULL) { |
1627 | zfree(ZV_NAMEI, vs.f_vol_name); |
1628 | } |
1629 | if (release_str) { |
1630 | vnode_putname(name: cnp); |
1631 | } |
1632 | kfree_data(ab.base, ab.allocated); |
1633 | VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d" , error); |
1634 | |
1635 | if (root_vp != NULL) { |
1636 | vnode_put(vp: root_vp); |
1637 | } |
1638 | return error; |
1639 | } |
1640 | |
1641 | /* |
1642 | * Pack ATTR_COMMON attributes into a user buffer. |
1643 | * alp is a pointer to the bitmap of attributes required. |
1644 | * abp is the state of the attribute filling operation. |
1645 | * The attribute data (along with some other fields that are required |
1646 | * are in ad. |
1647 | */ |
1648 | static errno_t |
1649 | attr_pack_common(vfs_context_t ctx, mount_t mp, vnode_t vp, struct attrlist *alp, |
1650 | struct _attrlist_buf *abp, struct vnode_attr *vap, int proc_is64, |
1651 | const char *cnp, ssize_t cnl, const char *fullpathptr, |
1652 | ssize_t fullpathlen, int return_valid, int pack_invalid, int vtype, |
1653 | int is_bulk) |
1654 | { |
1655 | uint32_t perms = 0; |
1656 | int error = 0; |
1657 | |
1658 | if ((alp->commonattr & ATTR_CMN_ERROR) && |
1659 | (!return_valid || pack_invalid)) { |
1660 | ATTR_PACK4((*abp), 0); |
1661 | abp->actual.commonattr |= ATTR_CMN_ERROR; |
1662 | } |
1663 | if (alp->commonattr & ATTR_CMN_NAME) { |
1664 | attrlist_pack_string(ab: abp, source: cnp, count: cnl); |
1665 | abp->actual.commonattr |= ATTR_CMN_NAME; |
1666 | } |
1667 | if (alp->commonattr & ATTR_CMN_DEVID) { |
1668 | if (mp) { /* caller needs real devid */ |
1669 | ATTR_PACK4((*abp), |
1670 | mp->mnt_vfsstat.f_fsid.val[0]); |
1671 | abp->actual.commonattr |= ATTR_CMN_DEVID; |
1672 | } else if (VATTR_IS_ACTIVE(vap, va_fsid) && VATTR_IS_SUPPORTED(vap, va_fsid)) { |
1673 | ATTR_PACK4((*abp), vap->va_fsid); |
1674 | abp->actual.commonattr |= ATTR_CMN_DEVID; |
1675 | } else if (vp) { |
1676 | ATTR_PACK4((*abp), |
1677 | vp->v_mount->mnt_vfsstat.f_fsid.val[0]); |
1678 | abp->actual.commonattr |= ATTR_CMN_DEVID; |
1679 | } else if (VATTR_IS_SUPPORTED(vap, va_devid)) { |
1680 | ATTR_PACK4((*abp), vap->va_devid); |
1681 | abp->actual.commonattr |= ATTR_CMN_DEVID; |
1682 | } else if (!return_valid || pack_invalid) { |
1683 | ATTR_PACK4((*abp), 0); |
1684 | } |
1685 | } |
1686 | if (alp->commonattr & ATTR_CMN_FSID) { |
1687 | if (mp) { /* caller needs real fsid */ |
1688 | ATTR_PACK8((*abp), |
1689 | mp->mnt_vfsstat.f_fsid); |
1690 | abp->actual.commonattr |= ATTR_CMN_FSID; |
1691 | } else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) { |
1692 | ATTR_PACK8((*abp), vap->va_fsid64); |
1693 | abp->actual.commonattr |= ATTR_CMN_FSID; |
1694 | } else if (vp) { |
1695 | ATTR_PACK8((*abp), |
1696 | vp->v_mount->mnt_vfsstat.f_fsid); |
1697 | abp->actual.commonattr |= ATTR_CMN_FSID; |
1698 | } else if (!return_valid || pack_invalid) { |
1699 | fsid_t fsid = {{0}}; |
1700 | ATTR_PACK8((*abp), fsid); |
1701 | } |
1702 | } |
1703 | if (alp->commonattr & ATTR_CMN_OBJTYPE) { |
1704 | if (vp) { |
1705 | ATTR_PACK4((*abp), vtype); |
1706 | abp->actual.commonattr |= ATTR_CMN_OBJTYPE; |
1707 | } else if (VATTR_IS_SUPPORTED(vap, va_objtype)) { |
1708 | ATTR_PACK4((*abp), vap->va_objtype); |
1709 | abp->actual.commonattr |= ATTR_CMN_OBJTYPE; |
1710 | } else if (!return_valid || pack_invalid) { |
1711 | ATTR_PACK4((*abp), 0); |
1712 | } |
1713 | } |
1714 | if (alp->commonattr & ATTR_CMN_OBJTAG) { |
1715 | if (vp) { |
1716 | ATTR_PACK4((*abp), vp->v_tag); |
1717 | abp->actual.commonattr |= ATTR_CMN_OBJTAG; |
1718 | } else if (VATTR_IS_SUPPORTED(vap, va_objtag)) { |
1719 | ATTR_PACK4((*abp), vap->va_objtag); |
1720 | abp->actual.commonattr |= ATTR_CMN_OBJTAG; |
1721 | } else if (!return_valid || pack_invalid) { |
1722 | ATTR_PACK4((*abp), 0); |
1723 | } |
1724 | } |
1725 | if (alp->commonattr & ATTR_CMN_OBJID) { |
1726 | /* |
1727 | * Carbon can't deal with us reporting the target ID |
1728 | * for links. So we ask the filesystem to give us the |
1729 | * source ID as well, and if it gives us one, we use |
1730 | * it instead. |
1731 | */ |
1732 | if (vap->va_vaflags & VA_64BITOBJIDS) { |
1733 | if (VATTR_IS_SUPPORTED(vap, va_linkid)) { |
1734 | ATTR_PACK8((*abp), vap->va_linkid); |
1735 | } else { |
1736 | ATTR_PACK8((*abp), vap->va_fileid); |
1737 | } |
1738 | } else { |
1739 | fsobj_id_t f; |
1740 | if (VATTR_IS_SUPPORTED(vap, va_linkid)) { |
1741 | f.fid_objno = (uint32_t)vap->va_linkid; |
1742 | } else { |
1743 | f.fid_objno = (uint32_t)vap->va_fileid; |
1744 | } |
1745 | f.fid_generation = 0; |
1746 | ATTR_PACK8((*abp), f); |
1747 | } |
1748 | abp->actual.commonattr |= ATTR_CMN_OBJID; |
1749 | } |
1750 | if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) { |
1751 | /* |
1752 | * Carbon can't deal with us reporting the target ID |
1753 | * for links. So we ask the filesystem to give us the |
1754 | * source ID as well, and if it gives us one, we use |
1755 | * it instead. |
1756 | */ |
1757 | if (vap->va_vaflags & VA_64BITOBJIDS) { |
1758 | if (VATTR_IS_SUPPORTED(vap, va_linkid)) { |
1759 | ATTR_PACK8((*abp), vap->va_linkid); |
1760 | } else { |
1761 | ATTR_PACK8((*abp), vap->va_fileid); |
1762 | } |
1763 | } else { |
1764 | fsobj_id_t f; |
1765 | if (VATTR_IS_SUPPORTED(vap, va_linkid)) { |
1766 | f.fid_objno = (uint32_t)vap->va_linkid; |
1767 | } else { |
1768 | f.fid_objno = (uint32_t)vap->va_fileid; |
1769 | } |
1770 | f.fid_generation = 0; |
1771 | ATTR_PACK8((*abp), f); |
1772 | } |
1773 | abp->actual.commonattr |= ATTR_CMN_OBJPERMANENTID; |
1774 | } |
1775 | if (alp->commonattr & ATTR_CMN_PAROBJID) { |
1776 | if (vap->va_vaflags & VA_64BITOBJIDS) { |
1777 | ATTR_PACK8((*abp), vap->va_parentid); |
1778 | } else { |
1779 | fsobj_id_t f; |
1780 | f.fid_objno = (uint32_t)vap->va_parentid; |
1781 | f.fid_generation = 0; |
1782 | ATTR_PACK8((*abp), f); |
1783 | } |
1784 | abp->actual.commonattr |= ATTR_CMN_PAROBJID; |
1785 | } |
1786 | if (alp->commonattr & ATTR_CMN_SCRIPT) { |
1787 | if (VATTR_IS_SUPPORTED(vap, va_encoding)) { |
1788 | ATTR_PACK4((*abp), vap->va_encoding); |
1789 | abp->actual.commonattr |= ATTR_CMN_SCRIPT; |
1790 | } else if (!return_valid || pack_invalid) { |
1791 | ATTR_PACK4((*abp), 0x7e); |
1792 | } |
1793 | } |
1794 | if (alp->commonattr & ATTR_CMN_CRTIME) { |
1795 | ATTR_PACK_TIME((*abp), vap->va_create_time, proc_is64); |
1796 | abp->actual.commonattr |= ATTR_CMN_CRTIME; |
1797 | } |
1798 | if (alp->commonattr & ATTR_CMN_MODTIME) { |
1799 | ATTR_PACK_TIME((*abp), vap->va_modify_time, proc_is64); |
1800 | abp->actual.commonattr |= ATTR_CMN_MODTIME; |
1801 | } |
1802 | if (alp->commonattr & ATTR_CMN_CHGTIME) { |
1803 | ATTR_PACK_TIME((*abp), vap->va_change_time, proc_is64); |
1804 | abp->actual.commonattr |= ATTR_CMN_CHGTIME; |
1805 | } |
1806 | if (alp->commonattr & ATTR_CMN_ACCTIME) { |
1807 | ATTR_PACK_TIME((*abp), vap->va_access_time, proc_is64); |
1808 | abp->actual.commonattr |= ATTR_CMN_ACCTIME; |
1809 | } |
1810 | if (alp->commonattr & ATTR_CMN_BKUPTIME) { |
1811 | ATTR_PACK_TIME((*abp), vap->va_backup_time, proc_is64); |
1812 | abp->actual.commonattr |= ATTR_CMN_BKUPTIME; |
1813 | } |
1814 | /* |
1815 | * They are requesting user access, we should obtain this before getting |
1816 | * the finder info. For some network file systems this is a performance |
1817 | * improvement. |
1818 | */ |
1819 | if (alp->commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */ |
1820 | if (vp && !is_bulk) { |
1821 | if (vtype == VDIR) { |
1822 | if (vnode_authorize(vp, NULL, |
1823 | KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | |
1824 | KAUTH_VNODE_ADD_SUBDIRECTORY | |
1825 | KAUTH_VNODE_DELETE_CHILD, ctx) == 0) { |
1826 | perms |= W_OK; |
1827 | } |
1828 | |
1829 | if (vnode_authorize(vp, NULL, |
1830 | KAUTH_VNODE_ACCESS | |
1831 | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0) { |
1832 | perms |= R_OK; |
1833 | } |
1834 | |
1835 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | |
1836 | KAUTH_VNODE_SEARCH, ctx) == 0) { |
1837 | perms |= X_OK; |
1838 | } |
1839 | } else { |
1840 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | |
1841 | KAUTH_VNODE_WRITE_DATA, ctx) == 0) { |
1842 | perms |= W_OK; |
1843 | } |
1844 | |
1845 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0) { |
1846 | perms |= R_OK; |
1847 | } |
1848 | if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0) { |
1849 | perms |= X_OK; |
1850 | } |
1851 | } |
1852 | } else if (is_bulk && |
1853 | VATTR_IS_SUPPORTED(vap, va_user_access)) { |
1854 | perms = vap->va_user_access; |
1855 | } |
1856 | } |
1857 | if (alp->commonattr & ATTR_CMN_FNDRINFO) { |
1858 | size_t fisize = 32; |
1859 | |
1860 | error = 0; |
1861 | if (vp && !is_bulk) { |
1862 | uio_t auio; |
1863 | UIO_STACKBUF(uio_buf, 1); |
1864 | |
1865 | if ((auio = uio_createwithbuffer(a_iovcount: 1, a_offset: 0, a_spacetype: UIO_SYSSPACE, |
1866 | a_iodirection: UIO_READ, a_buf_p: uio_buf, a_buffer_size: sizeof(uio_buf))) == NULL) { |
1867 | error = ENOMEM; |
1868 | goto out; |
1869 | } |
1870 | uio_addiov(a_uio: auio, CAST_USER_ADDR_T(abp->fixedcursor), |
1871 | a_length: fisize); |
1872 | /* fisize may be reset to 0 after this call */ |
1873 | error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio, |
1874 | &fisize, XATTR_NOSECURITY, ctx); |
1875 | uio_free(a_uio: auio); |
1876 | |
1877 | /* |
1878 | * Default to zeros if its not available, |
1879 | * unless ATTR_CMN_RETURNED_ATTRS was requested. |
1880 | */ |
1881 | if (error && |
1882 | (!return_valid || pack_invalid) && |
1883 | ((error == ENOATTR) || (error == ENOENT) || |
1884 | (error == ENOTSUP) || (error == EPERM))) { |
1885 | VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes" ); |
1886 | bzero(s: abp->fixedcursor, n: 32); |
1887 | error = 0; |
1888 | } |
1889 | |
1890 | if (error == 0) { |
1891 | abp->fixedcursor += 32; |
1892 | abp->actual.commonattr |= ATTR_CMN_FNDRINFO; |
1893 | } else if (!return_valid) { |
1894 | goto out; |
1895 | } else { |
1896 | /* |
1897 | * If we can inform the caller that we can't |
1898 | * return this attribute, reset error and |
1899 | * continue with the rest of the attributes. |
1900 | */ |
1901 | error = 0; |
1902 | } |
1903 | } else if (VATTR_IS_SUPPORTED(vap, va_finderinfo)) { |
1904 | bcopy(src: &vap->va_finderinfo[0], dst: abp->fixedcursor, n: fisize); |
1905 | abp->fixedcursor += fisize; |
1906 | abp->actual.commonattr |= ATTR_CMN_FNDRINFO; |
1907 | } else if (!return_valid || pack_invalid) { |
1908 | bzero(s: abp->fixedcursor, n: fisize); |
1909 | abp->fixedcursor += fisize; |
1910 | } |
1911 | } |
1912 | if (alp->commonattr & ATTR_CMN_OWNERID) { |
1913 | ATTR_PACK4((*abp), vap->va_uid); |
1914 | abp->actual.commonattr |= ATTR_CMN_OWNERID; |
1915 | } |
1916 | if (alp->commonattr & ATTR_CMN_GRPID) { |
1917 | ATTR_PACK4((*abp), vap->va_gid); |
1918 | abp->actual.commonattr |= ATTR_CMN_GRPID; |
1919 | } |
1920 | if (alp->commonattr & ATTR_CMN_ACCESSMASK) { |
1921 | ATTR_PACK4((*abp), vap->va_mode); |
1922 | abp->actual.commonattr |= ATTR_CMN_ACCESSMASK; |
1923 | } |
1924 | if (alp->commonattr & ATTR_CMN_FLAGS) { |
1925 | ATTR_PACK4((*abp), vap->va_flags); |
1926 | abp->actual.commonattr |= ATTR_CMN_FLAGS; |
1927 | } |
1928 | if (alp->commonattr & ATTR_CMN_GEN_COUNT) { |
1929 | if (VATTR_IS_SUPPORTED(vap, va_write_gencount)) { |
1930 | ATTR_PACK4((*abp), vap->va_write_gencount); |
1931 | abp->actual.commonattr |= ATTR_CMN_GEN_COUNT; |
1932 | } else if (!return_valid || pack_invalid) { |
1933 | ATTR_PACK4((*abp), 0); |
1934 | } |
1935 | } |
1936 | |
1937 | if (alp->commonattr & ATTR_CMN_DOCUMENT_ID) { |
1938 | if (VATTR_IS_SUPPORTED(vap, va_document_id)) { |
1939 | ATTR_PACK4((*abp), vap->va_document_id); |
1940 | abp->actual.commonattr |= ATTR_CMN_DOCUMENT_ID; |
1941 | } else if (!return_valid || pack_invalid) { |
1942 | ATTR_PACK4((*abp), 0); |
1943 | } |
1944 | } |
1945 | /* We already obtain the user access, so just fill in the buffer here */ |
1946 | if (alp->commonattr & ATTR_CMN_USERACCESS) { |
1947 | #if CONFIG_MACF |
1948 | if (!is_bulk && vp) { |
1949 | /* |
1950 | * Rather than MAC preceding DAC, in this case we want |
1951 | * the smallest set of permissions granted by both MAC & |
1952 | * DAC checks. We won't add back any permissions. |
1953 | */ |
1954 | if (perms & W_OK) { |
1955 | if (mac_vnode_check_access(ctx, vp, W_OK) != 0) { |
1956 | perms &= ~W_OK; |
1957 | } |
1958 | } |
1959 | if (perms & R_OK) { |
1960 | if (mac_vnode_check_access(ctx, vp, R_OK) != 0) { |
1961 | perms &= ~R_OK; |
1962 | } |
1963 | } |
1964 | if (perms & X_OK) { |
1965 | if (mac_vnode_check_access(ctx, vp, X_OK) != 0) { |
1966 | perms &= ~X_OK; |
1967 | } |
1968 | } |
1969 | } |
1970 | #endif /* MAC */ |
1971 | VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d" , perms); |
1972 | if (!is_bulk && vp) { |
1973 | ATTR_PACK4((*abp), perms); |
1974 | abp->actual.commonattr |= ATTR_CMN_USERACCESS; |
1975 | } else if (is_bulk && VATTR_IS_SUPPORTED(vap, va_user_access)) { |
1976 | ATTR_PACK4((*abp), perms); |
1977 | abp->actual.commonattr |= ATTR_CMN_USERACCESS; |
1978 | } else if (!return_valid || pack_invalid) { |
1979 | ATTR_PACK4((*abp), 0); |
1980 | } |
1981 | } |
1982 | if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) { |
1983 | if (VATTR_IS_SUPPORTED(vap, va_acl) && (vap->va_acl != NULL)) { |
1984 | struct kauth_filesec fsec; |
1985 | /* |
1986 | * We want to return a kauth_filesec (for now), but all we have is a kauth_acl. |
1987 | */ |
1988 | fsec.fsec_magic = KAUTH_FILESEC_MAGIC; |
1989 | fsec.fsec_owner = kauth_null_guid; |
1990 | fsec.fsec_group = kauth_null_guid; |
1991 | attrlist_pack_variable2(ab: abp, source: &fsec, __offsetof(struct kauth_filesec, fsec_acl), ext: vap->va_acl, KAUTH_ACL_COPYSIZE(vap->va_acl)); |
1992 | abp->actual.commonattr |= ATTR_CMN_EXTENDED_SECURITY; |
1993 | } else if (!return_valid || pack_invalid) { |
1994 | attrlist_pack_variable(ab: abp, NULL, count: 0); |
1995 | } |
1996 | } |
1997 | if (alp->commonattr & ATTR_CMN_UUID) { |
1998 | if (VATTR_IS_SUPPORTED(vap, va_uuuid)) { |
1999 | ATTR_PACK(abp, vap->va_uuuid); |
2000 | abp->actual.commonattr |= ATTR_CMN_UUID; |
2001 | } else if (!return_valid || pack_invalid) { |
2002 | ATTR_PACK(abp, kauth_null_guid); |
2003 | } |
2004 | } |
2005 | if (alp->commonattr & ATTR_CMN_GRPUUID) { |
2006 | if (VATTR_IS_SUPPORTED(vap, va_guuid)) { |
2007 | ATTR_PACK(abp, vap->va_guuid); |
2008 | abp->actual.commonattr |= ATTR_CMN_GRPUUID; |
2009 | } else if (!return_valid || pack_invalid) { |
2010 | ATTR_PACK(abp, kauth_null_guid); |
2011 | } |
2012 | } |
2013 | if (alp->commonattr & ATTR_CMN_FILEID) { |
2014 | ATTR_PACK8((*abp), vap->va_fileid); |
2015 | abp->actual.commonattr |= ATTR_CMN_FILEID; |
2016 | } |
2017 | if (alp->commonattr & ATTR_CMN_PARENTID) { |
2018 | ATTR_PACK8((*abp), vap->va_parentid); |
2019 | abp->actual.commonattr |= ATTR_CMN_PARENTID; |
2020 | } |
2021 | |
2022 | if (alp->commonattr & ATTR_CMN_FULLPATH) { |
2023 | if (vp) { |
2024 | attrlist_pack_string(ab: abp, source: fullpathptr, count: fullpathlen); |
2025 | abp->actual.commonattr |= ATTR_CMN_FULLPATH; |
2026 | } |
2027 | } |
2028 | |
2029 | if (alp->commonattr & ATTR_CMN_ADDEDTIME) { |
2030 | if (VATTR_IS_SUPPORTED(vap, va_addedtime)) { |
2031 | ATTR_PACK_TIME((*abp), vap->va_addedtime, proc_is64); |
2032 | abp->actual.commonattr |= ATTR_CMN_ADDEDTIME; |
2033 | } else if (!return_valid || pack_invalid) { |
2034 | struct timespec zerotime = {.tv_sec = 0, .tv_nsec = 0}; |
2035 | |
2036 | ATTR_PACK_TIME((*abp), zerotime, proc_is64); |
2037 | } |
2038 | } |
2039 | if (alp->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) { |
2040 | if (VATTR_IS_SUPPORTED(vap, va_dataprotect_class)) { |
2041 | ATTR_PACK4((*abp), vap->va_dataprotect_class); |
2042 | abp->actual.commonattr |= ATTR_CMN_DATA_PROTECT_FLAGS; |
2043 | } else if (!return_valid || pack_invalid) { |
2044 | ATTR_PACK4((*abp), 0); |
2045 | } |
2046 | } |
2047 | out: |
2048 | return error; |
2049 | } |
2050 | |
2051 | static errno_t |
2052 | attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp, |
2053 | struct vnode_attr *vap, int return_valid, int pack_invalid) |
2054 | { |
2055 | if (alp->dirattr & ATTR_DIR_LINKCOUNT) { /* full count of entries */ |
2056 | ATTR_PACK4((*abp), (uint32_t)vap->va_dirlinkcount); |
2057 | abp->actual.dirattr |= ATTR_DIR_LINKCOUNT; |
2058 | } |
2059 | if (alp->dirattr & ATTR_DIR_ENTRYCOUNT) { |
2060 | ATTR_PACK4((*abp), (uint32_t)vap->va_nchildren); |
2061 | abp->actual.dirattr |= ATTR_DIR_ENTRYCOUNT; |
2062 | } |
2063 | if (alp->dirattr & ATTR_DIR_MOUNTSTATUS) { |
2064 | uint32_t mntstat; |
2065 | |
2066 | if (vp) { |
2067 | /* |
2068 | * The vnode that is passed down may either be a |
2069 | * top level vnode of a mount stack or a mounted |
2070 | * on vnode. In either case, the directory should |
2071 | * be reported as a mount point. |
2072 | */ |
2073 | if ((vp->v_flag & VROOT) || vnode_mountedhere(vp)) { |
2074 | mntstat = DIR_MNTSTATUS_MNTPOINT; |
2075 | } else { |
2076 | mntstat = 0; |
2077 | } |
2078 | #if CONFIG_TRIGGERS |
2079 | /* |
2080 | * Report back on active vnode triggers |
2081 | * that can directly trigger a mount |
2082 | */ |
2083 | if (vp->v_resolve && |
2084 | !(vp->v_resolve->vr_flags & VNT_NO_DIRECT_MOUNT)) { |
2085 | mntstat |= DIR_MNTSTATUS_TRIGGER; |
2086 | } |
2087 | #endif |
2088 | } else { |
2089 | mntstat = 0; |
2090 | } |
2091 | |
2092 | ATTR_PACK4((*abp), mntstat); |
2093 | abp->actual.dirattr |= ATTR_DIR_MOUNTSTATUS; |
2094 | } |
2095 | if (alp->dirattr & ATTR_DIR_ALLOCSIZE) { |
2096 | if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) { |
2097 | ATTR_PACK8((*abp), vap->va_data_alloc); |
2098 | abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE; |
2099 | } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) { |
2100 | ATTR_PACK8((*abp), vap->va_total_alloc); |
2101 | abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE; |
2102 | } else if (!return_valid || pack_invalid) { |
2103 | uint64_t zero_val = 0; |
2104 | ATTR_PACK8((*abp), zero_val); |
2105 | } |
2106 | } |
2107 | if (alp->dirattr & ATTR_DIR_IOBLOCKSIZE) { |
2108 | if (VATTR_IS_SUPPORTED(vap, va_iosize)) { |
2109 | ATTR_PACK4((*abp), vap->va_iosize); |
2110 | abp->actual.dirattr |= ATTR_DIR_IOBLOCKSIZE; |
2111 | } else if (!return_valid || pack_invalid) { |
2112 | ATTR_PACK4((*abp), 0); |
2113 | } |
2114 | } |
2115 | /* |
2116 | * If the filesystem does not support datalength |
2117 | * or dataallocsize, then we infer that totalsize and |
2118 | * totalalloc are substitutes. |
2119 | */ |
2120 | if (alp->dirattr & ATTR_DIR_DATALENGTH) { |
2121 | if (VATTR_IS_SUPPORTED(vap, va_data_size)) { |
2122 | ATTR_PACK8((*abp), vap->va_data_size); |
2123 | abp->actual.dirattr |= ATTR_DIR_DATALENGTH; |
2124 | } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) { |
2125 | ATTR_PACK8((*abp), vap->va_total_size); |
2126 | abp->actual.dirattr |= ATTR_DIR_DATALENGTH; |
2127 | } else if (!return_valid || pack_invalid) { |
2128 | uint64_t zero_val = 0; |
2129 | ATTR_PACK8((*abp), zero_val); |
2130 | } |
2131 | } |
2132 | |
2133 | return 0; |
2134 | } |
2135 | |
2136 | /* |
2137 | * The is_bulk parameter differentiates whether the function is called from |
2138 | * getattrlist or getattrlistbulk. When coming in from getattrlistbulk, |
2139 | * the corresponding va_* values are expected to be the values filled and no |
2140 | * attempt is made to retrieve them by calling back into the filesystem. |
2141 | */ |
2142 | static errno_t |
2143 | attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp, |
2144 | struct _attrlist_buf *abp, struct vnode_attr *vap, int return_valid, |
2145 | int pack_invalid, int is_bulk) |
2146 | { |
2147 | size_t rsize = 0; |
2148 | uint64_t rlength = 0; |
2149 | uint64_t ralloc = 0; |
2150 | int error = 0; |
2151 | |
2152 | /* |
2153 | * Pre-fetch the rsrc attributes now so we only get them once. |
2154 | * Fetch the resource fork size/allocation via xattr interface |
2155 | */ |
2156 | if (vp && !is_bulk && |
2157 | (alp->fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE | |
2158 | ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE))) { |
2159 | error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, |
2160 | &rsize, XATTR_NOSECURITY, ctx); |
2161 | if (error) { |
2162 | if ((error == ENOENT) || (error == ENOATTR) || |
2163 | (error == ENOTSUP) || (error == EPERM) || |
2164 | (error == EACCES)) { |
2165 | rsize = 0; |
2166 | error = 0; |
2167 | } else { |
2168 | goto out; |
2169 | } |
2170 | } |
2171 | rlength = rsize; |
2172 | |
2173 | if (alp->fileattr & (ATTR_FILE_RSRCALLOCSIZE | |
2174 | ATTR_FILE_ALLOCSIZE)) { |
2175 | uint32_t blksize; |
2176 | |
2177 | blksize = vp->v_mount->mnt_vfsstat.f_bsize; |
2178 | |
2179 | if (blksize == 0) { |
2180 | blksize = 512; |
2181 | } |
2182 | ralloc = roundup(rsize, blksize); |
2183 | } |
2184 | } |
2185 | |
2186 | if (alp->fileattr & ATTR_FILE_LINKCOUNT) { |
2187 | ATTR_PACK4((*abp), (uint32_t)vap->va_nlink); |
2188 | abp->actual.fileattr |= ATTR_FILE_LINKCOUNT; |
2189 | } |
2190 | /* |
2191 | * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes: |
2192 | * We infer that if the filesystem does not support va_data_size or va_data_alloc |
2193 | * it must not know about alternate forks. So when we need to gather |
2194 | * the total size or total alloc, it's OK to substitute the total size for |
2195 | * the data size below. This is because it is likely a flat filesystem and we must |
2196 | * be using AD files to store the rsrc fork and EAs. |
2197 | * |
2198 | * Additionally, note that getattrlist is barred from being called on |
2199 | * resource fork paths. (Search for CN_ALLOWRSRCFORK). So if the filesystem does |
2200 | * support va_data_size, it is guaranteed to represent the data fork's size. This |
2201 | * is an important distinction to make because when we call vnode_getattr on |
2202 | * an HFS resource fork vnode, to get the size, it will vend out the resource |
2203 | * fork's size (it only gets the size of the passed-in vnode). |
2204 | */ |
2205 | if (alp->fileattr & ATTR_FILE_TOTALSIZE) { |
2206 | if (!is_bulk) { |
2207 | uint64_t totalsize = rlength; |
2208 | |
2209 | if (VATTR_IS_SUPPORTED(vap, va_data_size)) { |
2210 | totalsize += vap->va_data_size; |
2211 | } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) { |
2212 | totalsize += vap->va_total_size; |
2213 | } |
2214 | |
2215 | ATTR_PACK8((*abp), totalsize); |
2216 | abp->actual.fileattr |= ATTR_FILE_TOTALSIZE; |
2217 | } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) { |
2218 | ATTR_PACK8((*abp), vap->va_total_size); |
2219 | abp->actual.fileattr |= ATTR_FILE_TOTALSIZE; |
2220 | } else if (!return_valid || pack_invalid) { |
2221 | uint64_t zero_val = 0; |
2222 | |
2223 | ATTR_PACK8((*abp), zero_val); |
2224 | } |
2225 | } |
2226 | if (alp->fileattr & ATTR_FILE_ALLOCSIZE) { |
2227 | if (!is_bulk) { |
2228 | uint64_t totalalloc = ralloc; |
2229 | |
2230 | /* |
2231 | * If data_alloc is supported, then it must represent the |
2232 | * data fork size. |
2233 | */ |
2234 | if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) { |
2235 | totalalloc += vap->va_data_alloc; |
2236 | } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) { |
2237 | totalalloc += vap->va_total_alloc; |
2238 | } |
2239 | |
2240 | ATTR_PACK8((*abp), totalalloc); |
2241 | abp->actual.fileattr |= ATTR_FILE_ALLOCSIZE; |
2242 | } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) { |
2243 | ATTR_PACK8((*abp), vap->va_total_alloc); |
2244 | abp->actual.fileattr |= ATTR_FILE_ALLOCSIZE; |
2245 | } else if (!return_valid || pack_invalid) { |
2246 | uint64_t zero_val = 0; |
2247 | |
2248 | ATTR_PACK8((*abp), zero_val); |
2249 | } |
2250 | } |
2251 | if (alp->fileattr & ATTR_FILE_IOBLOCKSIZE) { |
2252 | if (VATTR_IS_SUPPORTED(vap, va_iosize)) { |
2253 | ATTR_PACK4((*abp), vap->va_iosize); |
2254 | abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE; |
2255 | } else if (!return_valid || pack_invalid) { |
2256 | ATTR_PACK4((*abp), 0); |
2257 | } |
2258 | } |
2259 | if (alp->fileattr & ATTR_FILE_CLUMPSIZE) { |
2260 | if (!return_valid || pack_invalid) { |
2261 | ATTR_PACK4((*abp), 0); /* this value is deprecated */ |
2262 | abp->actual.fileattr |= ATTR_FILE_CLUMPSIZE; |
2263 | } |
2264 | } |
2265 | if (alp->fileattr & ATTR_FILE_DEVTYPE) { |
2266 | if (vp && (vp->v_type == VCHR || vp->v_type == VBLK)) { |
2267 | uint32_t dev; |
2268 | |
2269 | if (vp->v_specinfo != NULL) { |
2270 | dev = vp->v_specinfo->si_rdev; |
2271 | } else if (VATTR_IS_SUPPORTED(vap, va_rdev)) { |
2272 | dev = vap->va_rdev; |
2273 | } else { |
2274 | dev = 0; |
2275 | } |
2276 | ATTR_PACK4((*abp), dev); |
2277 | abp->actual.fileattr |= ATTR_FILE_DEVTYPE; |
2278 | } else if (vp) { |
2279 | ATTR_PACK4((*abp), 0); |
2280 | abp->actual.fileattr |= ATTR_FILE_DEVTYPE; |
2281 | } else if (VATTR_IS_SUPPORTED(vap, va_rdev)) { |
2282 | ATTR_PACK4((*abp), vap->va_rdev); |
2283 | abp->actual.fileattr |= ATTR_FILE_DEVTYPE; |
2284 | } else if (!return_valid || pack_invalid) { |
2285 | ATTR_PACK4((*abp), 0); |
2286 | } |
2287 | } |
2288 | /* |
2289 | * If the filesystem does not support datalength |
2290 | * or dataallocsize, then we infer that totalsize and |
2291 | * totalalloc are substitutes. |
2292 | */ |
2293 | if (alp->fileattr & ATTR_FILE_DATALENGTH) { |
2294 | if (VATTR_IS_SUPPORTED(vap, va_data_size)) { |
2295 | ATTR_PACK8((*abp), vap->va_data_size); |
2296 | abp->actual.fileattr |= ATTR_FILE_DATALENGTH; |
2297 | } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) { |
2298 | ATTR_PACK8((*abp), vap->va_total_size); |
2299 | abp->actual.fileattr |= ATTR_FILE_DATALENGTH; |
2300 | } else if (!return_valid || pack_invalid) { |
2301 | uint64_t zero_val = 0; |
2302 | ATTR_PACK8((*abp), zero_val); |
2303 | } |
2304 | } |
2305 | if (alp->fileattr & ATTR_FILE_DATAALLOCSIZE) { |
2306 | if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) { |
2307 | ATTR_PACK8((*abp), vap->va_data_alloc); |
2308 | abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE; |
2309 | } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) { |
2310 | ATTR_PACK8((*abp), vap->va_total_alloc); |
2311 | abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE; |
2312 | } else if (!return_valid || pack_invalid) { |
2313 | uint64_t zero_val = 0; |
2314 | ATTR_PACK8((*abp), zero_val); |
2315 | } |
2316 | } |
2317 | /* already got the resource fork size/allocation above */ |
2318 | if (alp->fileattr & ATTR_FILE_RSRCLENGTH) { |
2319 | if (!is_bulk) { |
2320 | ATTR_PACK8((*abp), rlength); |
2321 | abp->actual.fileattr |= ATTR_FILE_RSRCLENGTH; |
2322 | } else if (VATTR_IS_SUPPORTED(vap, va_rsrc_length)) { |
2323 | ATTR_PACK8((*abp), vap->va_rsrc_length); |
2324 | abp->actual.fileattr |= ATTR_FILE_RSRCLENGTH; |
2325 | } else if (!return_valid || pack_invalid) { |
2326 | uint64_t zero_val = 0; |
2327 | |
2328 | ATTR_PACK8((*abp), zero_val); |
2329 | } |
2330 | } |
2331 | if (alp->fileattr & ATTR_FILE_RSRCALLOCSIZE) { |
2332 | if (!is_bulk) { |
2333 | ATTR_PACK8((*abp), ralloc); |
2334 | abp->actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE; |
2335 | } else if (VATTR_IS_SUPPORTED(vap, va_rsrc_alloc)) { |
2336 | ATTR_PACK8((*abp), vap->va_rsrc_alloc); |
2337 | abp->actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE; |
2338 | } else if (!return_valid || pack_invalid) { |
2339 | uint64_t zero_val = 0; |
2340 | |
2341 | ATTR_PACK8((*abp), zero_val); |
2342 | } |
2343 | } |
2344 | out: |
2345 | return error; |
2346 | } |
2347 | |
2348 | /* |
2349 | * Pack FORKATTR attributes into a user buffer. |
2350 | * alp is a pointer to the bitmap of attributes required. |
2351 | * abp is the state of the attribute filling operation. |
2352 | * The attribute data (along with some other fields that are required |
2353 | * are in ad. |
2354 | */ |
2355 | static errno_t |
2356 | attr_pack_common_extended(mount_t mp, struct vnode *vp, struct attrlist *alp, |
2357 | struct _attrlist_buf *abp, const char *relpathptr, ssize_t relpathlen, |
2358 | const char *REALpathptr, ssize_t REALpathlen, |
2359 | struct vnode_attr *vap, int return_valid, int pack_invalid) |
2360 | { |
2361 | if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) { |
2362 | attrlist_pack_string(ab: abp, source: relpathptr, count: relpathlen); |
2363 | abp->actual.forkattr |= ATTR_CMNEXT_RELPATH; |
2364 | } |
2365 | |
2366 | if (alp->forkattr & ATTR_CMNEXT_PRIVATESIZE) { |
2367 | if (VATTR_IS_SUPPORTED(vap, va_private_size)) { |
2368 | ATTR_PACK8((*abp), vap->va_private_size); |
2369 | abp->actual.forkattr |= ATTR_CMNEXT_PRIVATESIZE; |
2370 | } else if (!return_valid || pack_invalid) { |
2371 | uint64_t zero_val = 0; |
2372 | ATTR_PACK8((*abp), zero_val); |
2373 | } |
2374 | } |
2375 | |
2376 | if (alp->forkattr & ATTR_CMNEXT_LINKID) { |
2377 | uint64_t linkid; |
2378 | |
2379 | if (VATTR_IS_SUPPORTED(vap, va_linkid)) { |
2380 | linkid = vap->va_linkid; |
2381 | } else { |
2382 | linkid = vap->va_fileid; |
2383 | } |
2384 | |
2385 | ATTR_PACK8((*abp), linkid); |
2386 | abp->actual.forkattr |= ATTR_CMNEXT_LINKID; |
2387 | } |
2388 | |
2389 | if (vp && (alp->forkattr & ATTR_CMNEXT_NOFIRMLINKPATH)) { |
2390 | attrlist_pack_string(ab: abp, source: REALpathptr, count: REALpathlen); |
2391 | abp->actual.forkattr |= ATTR_CMNEXT_NOFIRMLINKPATH; |
2392 | } |
2393 | |
2394 | if (alp->forkattr & ATTR_CMNEXT_REALDEVID) { |
2395 | if (mp) { |
2396 | ATTR_PACK4((*abp), |
2397 | mp->mnt_vfsstat.f_fsid.val[0]); |
2398 | abp->actual.forkattr |= ATTR_CMNEXT_REALDEVID; |
2399 | } else if (vp) { |
2400 | ATTR_PACK4((*abp), |
2401 | vp->v_mount->mnt_vfsstat.f_fsid.val[0]); |
2402 | abp->actual.forkattr |= ATTR_CMNEXT_REALDEVID; |
2403 | } else if (VATTR_IS_SUPPORTED(vap, va_fsid)) { |
2404 | ATTR_PACK4((*abp), vap->va_fsid); |
2405 | abp->actual.forkattr |= ATTR_CMN_DEVID; |
2406 | } else if (!return_valid || pack_invalid) { |
2407 | ATTR_PACK4((*abp), 0); |
2408 | } |
2409 | } |
2410 | |
2411 | if (alp->forkattr & ATTR_CMNEXT_REALFSID) { |
2412 | if (mp) { |
2413 | ATTR_PACK8((*abp), |
2414 | mp->mnt_vfsstat.f_fsid); |
2415 | abp->actual.forkattr |= ATTR_CMNEXT_REALFSID; |
2416 | } else if (vp) { |
2417 | ATTR_PACK8((*abp), |
2418 | vp->v_mount->mnt_vfsstat.f_fsid); |
2419 | abp->actual.forkattr |= ATTR_CMNEXT_REALFSID; |
2420 | } else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) { |
2421 | ATTR_PACK8((*abp), vap->va_fsid64); |
2422 | abp->actual.forkattr |= ATTR_CMN_FSID; |
2423 | } else if (!return_valid || pack_invalid) { |
2424 | fsid_t fsid = {{0}}; |
2425 | |
2426 | ATTR_PACK8((*abp), fsid); |
2427 | } |
2428 | } |
2429 | |
2430 | if (alp->forkattr & ATTR_CMNEXT_CLONEID) { |
2431 | if (VATTR_IS_SUPPORTED(vap, va_clone_id)) { |
2432 | ATTR_PACK8((*abp), vap->va_clone_id); |
2433 | abp->actual.forkattr |= ATTR_CMNEXT_CLONEID; |
2434 | } else if (!return_valid || pack_invalid) { |
2435 | uint64_t zero_val = 0; |
2436 | ATTR_PACK8((*abp), zero_val); |
2437 | } |
2438 | } |
2439 | |
2440 | if (alp->forkattr & ATTR_CMNEXT_EXT_FLAGS) { |
2441 | if (VATTR_IS_SUPPORTED(vap, va_extflags)) { |
2442 | ATTR_PACK8((*abp), vap->va_extflags); |
2443 | abp->actual.forkattr |= ATTR_CMNEXT_EXT_FLAGS; |
2444 | } else if (!return_valid || pack_invalid) { |
2445 | uint64_t zero_val = 0; |
2446 | ATTR_PACK8((*abp), zero_val); |
2447 | } |
2448 | } |
2449 | |
2450 | if (alp->forkattr & ATTR_CMNEXT_RECURSIVE_GENCOUNT) { |
2451 | if (VATTR_IS_SUPPORTED(vap, va_recursive_gencount)) { |
2452 | ATTR_PACK8((*abp), vap->va_recursive_gencount); |
2453 | abp->actual.forkattr |= ATTR_CMNEXT_RECURSIVE_GENCOUNT; |
2454 | } else if (!return_valid || pack_invalid) { |
2455 | uint64_t zero_val = 0; |
2456 | ATTR_PACK8((*abp), zero_val); |
2457 | } |
2458 | } |
2459 | |
2460 | if (alp->forkattr & ATTR_CMNEXT_ATTRIBUTION_TAG) { |
2461 | if (VATTR_IS_SUPPORTED(vap, va_attribution_tag)) { |
2462 | ATTR_PACK8((*abp), vap->va_attribution_tag); |
2463 | abp->actual.forkattr |= ATTR_CMNEXT_ATTRIBUTION_TAG; |
2464 | } else if (!return_valid || pack_invalid) { |
2465 | uint64_t zero_val = 0; |
2466 | ATTR_PACK8((*abp), zero_val); |
2467 | } |
2468 | } |
2469 | |
2470 | if (alp->forkattr & ATTR_CMNEXT_CLONE_REFCNT) { |
2471 | if (VATTR_IS_SUPPORTED(vap, va_clone_refcnt)) { |
2472 | ATTR_PACK4((*abp), vap->va_clone_refcnt); |
2473 | abp->actual.forkattr |= ATTR_CMNEXT_CLONE_REFCNT; |
2474 | } else if (!return_valid || pack_invalid) { |
2475 | uint32_t zero_val = 0; |
2476 | ATTR_PACK4((*abp), zero_val); |
2477 | } |
2478 | } |
2479 | |
2480 | return 0; |
2481 | } |
2482 | |
2483 | static void |
2484 | vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap, |
2485 | int return_valid, int is_bulk, |
2486 | #if !CONFIG_FIRMLINKS |
2487 | __unused |
2488 | #endif |
2489 | int is_realdev, vfs_context_t ctx) |
2490 | { |
2491 | /* |
2492 | * There are a couple of special cases. |
2493 | * If we are after object IDs, we can make do with va_fileid. |
2494 | */ |
2495 | if ((alp->commonattr & |
2496 | (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) && |
2497 | !VATTR_IS_SUPPORTED(vap, va_linkid)) { |
2498 | /* forget we wanted this */ |
2499 | VATTR_CLEAR_ACTIVE(vap, va_linkid); |
2500 | } |
2501 | |
2502 | /* |
2503 | * A filesystem may not support va_fsid64. If it is not available, then we'll |
2504 | * synthesize it from the mount. |
2505 | */ |
2506 | if ((alp->commonattr & ATTR_CMN_FSID) && !VATTR_IS_SUPPORTED(vap, va_fsid64)) { |
2507 | VATTR_CLEAR_ACTIVE(vap, va_fsid64); |
2508 | } |
2509 | |
2510 | /* Same for fsid */ |
2511 | if ((alp->commonattr & ATTR_CMN_FSID) && !VATTR_IS_SUPPORTED(vap, va_fsid)) { |
2512 | VATTR_CLEAR_ACTIVE(vap, va_fsid); |
2513 | } |
2514 | |
2515 | /* We request the fsid64 for the devid */ |
2516 | if ((alp->commonattr & ATTR_CMN_DEVID) && !VATTR_IS_SUPPORTED(vap, va_fsid)) { |
2517 | VATTR_CLEAR_ACTIVE(vap, va_fsid); |
2518 | } |
2519 | |
2520 | |
2521 | /* |
2522 | * Many filesystems don't know their parent object id. |
2523 | * If necessary, attempt to derive it from the vnode. |
2524 | */ |
2525 | if ((alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) && vp) { |
2526 | vnode_t dvp; |
2527 | |
2528 | #if CONFIG_FIRMLINKS |
2529 | /* If this is a firmlink target, we get the fileid of the firmlink parent. */ |
2530 | if (!is_realdev && (vp->v_flag & VFMLINKTARGET) && ((dvp = vp->v_fmlink) != NULL) && (vnode_get(dvp) == 0)) { |
2531 | struct vnode_attr lva; |
2532 | |
2533 | VATTR_INIT(&lva); |
2534 | VATTR_WANTED(&lva, va_parentid); |
2535 | VATTR_WANTED(&lva, va_fsid); |
2536 | if (vnode_getattr(vp: dvp, vap: &lva, ctx) == 0 && |
2537 | VATTR_IS_SUPPORTED(&lva, va_parentid) && |
2538 | VATTR_IS_SUPPORTED(&lva, va_fsid) && |
2539 | (lva.va_fsid == (uint32_t)vp->v_mount->mnt_vfsstat.f_fsid.val[0])) { |
2540 | vap->va_parentid = lva.va_parentid; |
2541 | VATTR_SET_SUPPORTED(vap, va_parentid); |
2542 | } |
2543 | vnode_put(vp: dvp); |
2544 | } else |
2545 | #endif /* CONFIG_FIRMLINKS */ |
2546 | if (!VATTR_IS_SUPPORTED(vap, va_parentid) && !is_bulk) { |
2547 | if ((dvp = vnode_getparent(vp)) != NULLVP) { |
2548 | struct vnode_attr lva; |
2549 | |
2550 | VATTR_INIT(&lva); |
2551 | VATTR_WANTED(&lva, va_fileid); |
2552 | if (vnode_getattr(vp: dvp, vap: &lva, ctx) == 0 && |
2553 | VATTR_IS_SUPPORTED(vap, va_fileid)) { |
2554 | vap->va_parentid = lva.va_fileid; |
2555 | VATTR_SET_SUPPORTED(vap, va_parentid); |
2556 | } |
2557 | vnode_put(vp: dvp); |
2558 | } |
2559 | } |
2560 | } |
2561 | |
2562 | /* |
2563 | * And we can report datasize/alloc from total. |
2564 | */ |
2565 | if ((alp->fileattr & ATTR_FILE_DATALENGTH) && |
2566 | !VATTR_IS_SUPPORTED(vap, va_data_size)) { |
2567 | VATTR_CLEAR_ACTIVE(vap, va_data_size); |
2568 | } |
2569 | |
2570 | if ((alp->fileattr & ATTR_FILE_DATAALLOCSIZE) && |
2571 | !VATTR_IS_SUPPORTED(vap, va_data_alloc)) { |
2572 | VATTR_CLEAR_ACTIVE(vap, va_data_alloc); |
2573 | } |
2574 | |
2575 | /* |
2576 | * If we don't have an encoding, go with UTF-8 |
2577 | */ |
2578 | if ((alp->commonattr & ATTR_CMN_SCRIPT) && |
2579 | !VATTR_IS_SUPPORTED(vap, va_encoding) && !return_valid) { |
2580 | VATTR_RETURN(vap, va_encoding, |
2581 | 0x7e /* kTextEncodingMacUnicode */); |
2582 | } |
2583 | |
2584 | /* |
2585 | * If we don't have a name, we'll get one from the vnode or |
2586 | * mount point. |
2587 | */ |
2588 | if ((alp->commonattr & ATTR_CMN_NAME) && |
2589 | !VATTR_IS_SUPPORTED(vap, va_name)) { |
2590 | VATTR_CLEAR_ACTIVE(vap, va_name); |
2591 | } |
2592 | |
2593 | /* If va_dirlinkcount isn't supported use a default of 1. */ |
2594 | if ((alp->dirattr & ATTR_DIR_LINKCOUNT) && |
2595 | !VATTR_IS_SUPPORTED(vap, va_dirlinkcount)) { |
2596 | VATTR_RETURN(vap, va_dirlinkcount, 1); |
2597 | } |
2598 | } |
2599 | |
2600 | struct _attrlist_paths { |
2601 | char *fullpathptr; |
2602 | ssize_t *fullpathlenp; |
2603 | char *relpathptr; |
2604 | ssize_t *relpathlenp; |
2605 | char *REALpathptr; |
2606 | ssize_t *REALpathlenp; |
2607 | }; |
2608 | |
2609 | static errno_t |
2610 | calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap, |
2611 | ssize_t *varsizep, struct _attrlist_paths *pathsp, const char **vnamep, |
2612 | const char **cnpp, ssize_t *cnlp) |
2613 | { |
2614 | int error = 0; |
2615 | |
2616 | *varsizep = 0; /* length count */ |
2617 | /* We may need to fix up the name attribute if requested */ |
2618 | if (alp->commonattr & ATTR_CMN_NAME) { |
2619 | if (VATTR_IS_SUPPORTED(vap, va_name)) { |
2620 | vap->va_name[MAXPATHLEN - 1] = '\0'; /* Ensure nul-termination */ |
2621 | *cnpp = vap->va_name; |
2622 | *cnlp = strlen(s: *cnpp); |
2623 | } else if (vp) { |
2624 | /* Filesystem did not support getting the name */ |
2625 | if (vnode_isvroot(vp)) { |
2626 | if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 && |
2627 | vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') { |
2628 | /* special case for boot volume. Use root name when it's |
2629 | * available (which is the volume name) or just the mount on |
2630 | * name of "/". we must do this for binary compatibility with |
2631 | * pre Tiger code. returning nothing for the boot volume name |
2632 | * breaks installers - 3961058 |
2633 | */ |
2634 | *cnpp = *vnamep = vnode_getname(vp); |
2635 | if (*cnpp == NULL) { |
2636 | /* just use "/" as name */ |
2637 | *cnpp = &vp->v_mount->mnt_vfsstat.f_mntonname[0]; |
2638 | } |
2639 | *cnlp = strlen(s: *cnpp); |
2640 | } else { |
2641 | getattrlist_findnamecomp(mn: vp->v_mount->mnt_vfsstat.f_mntonname, np: cnpp, nl: cnlp); |
2642 | } |
2643 | } else { |
2644 | *cnpp = *vnamep = vnode_getname(vp); |
2645 | *cnlp = 0; |
2646 | if (*cnpp != NULL) { |
2647 | *cnlp = strlen(s: *cnpp); |
2648 | } |
2649 | } |
2650 | } else { |
2651 | *cnlp = 0; |
2652 | } |
2653 | *varsizep += roundup(*cnlp + 1, 4); |
2654 | } |
2655 | |
2656 | /* |
2657 | * Compute the full path to this vnode, if necessary. This attribute is almost certainly |
2658 | * not supported by any filesystem, so build the path to this vnode at this time. |
2659 | */ |
2660 | if (vp && (alp->commonattr & ATTR_CMN_FULLPATH)) { |
2661 | int len = MAXPATHLEN; |
2662 | int err; |
2663 | |
2664 | /* call build_path making sure NOT to use the cache-only behavior */ |
2665 | err = build_path(first_vp: vp, buff: pathsp->fullpathptr, buflen: len, outlen: &len, flags: 0, ctx: vfs_context_current()); |
2666 | if (err) { |
2667 | error = err; |
2668 | goto out; |
2669 | } |
2670 | if (pathsp->fullpathptr) { |
2671 | *(pathsp->fullpathlenp) = strlen(s: pathsp->fullpathptr); |
2672 | } else { |
2673 | *(pathsp->fullpathlenp) = 0; |
2674 | } |
2675 | *varsizep += roundup(((*(pathsp->fullpathlenp)) + 1), 4); |
2676 | } |
2677 | |
2678 | /* |
2679 | * Compute this vnode's volume relative path. |
2680 | */ |
2681 | if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) { |
2682 | int len; |
2683 | int err; |
2684 | |
2685 | /* call build_path making sure NOT to use the cache-only behavior */ |
2686 | err = build_path(first_vp: vp, buff: pathsp->relpathptr, MAXPATHLEN, outlen: &len, BUILDPATH_VOLUME_RELATIVE, ctx: vfs_context_current()); |
2687 | if (err) { |
2688 | error = err; |
2689 | goto out; |
2690 | } |
2691 | |
2692 | //`len' includes trailing null |
2693 | *(pathsp->relpathlenp) = len - 1; |
2694 | *varsizep += roundup(len, 4); |
2695 | } |
2696 | |
2697 | /* |
2698 | * Compute this vnode's real (firmlink free) path. |
2699 | */ |
2700 | if (vp && (alp->forkattr & ATTR_CMNEXT_NOFIRMLINKPATH)) { |
2701 | int len; |
2702 | int err; |
2703 | |
2704 | /* call build_path making sure NOT to use the cache-only behavior */ |
2705 | err = build_path(first_vp: vp, buff: pathsp->REALpathptr, MAXPATHLEN, outlen: &len, BUILDPATH_NO_FIRMLINK, ctx: vfs_context_current()); |
2706 | if (err) { |
2707 | error = err; |
2708 | goto out; |
2709 | } |
2710 | |
2711 | //`len' includes trailing null |
2712 | *(pathsp->REALpathlenp) = len - 1; |
2713 | *varsizep += roundup(len, 4); |
2714 | } |
2715 | |
2716 | /* |
2717 | * We have a kauth_acl_t but we will be returning a kauth_filesec_t. |
2718 | * |
2719 | * XXX This needs to change at some point; since the blob is opaque in |
2720 | * user-space this is OK. |
2721 | */ |
2722 | if ((alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) && |
2723 | VATTR_IS_SUPPORTED(vap, va_acl) && |
2724 | (vap->va_acl != NULL)) { |
2725 | /* |
2726 | * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against |
2727 | * KAUTH_FILESEC_NOACL ourselves |
2728 | */ |
2729 | if (vap->va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) { |
2730 | *varsizep += roundup((KAUTH_FILESEC_SIZE(0)), 4); |
2731 | } else { |
2732 | *varsizep += roundup((KAUTH_FILESEC_SIZE(vap->va_acl->acl_entrycount)), 4); |
2733 | } |
2734 | } |
2735 | |
2736 | out: |
2737 | return error; |
2738 | } |
2739 | |
2740 | static errno_t |
2741 | vfs_attr_pack_internal(mount_t mp, vnode_t vp, uio_t auio, struct attrlist *alp, |
2742 | uint64_t options, struct vnode_attr *vap, __unused void *fndesc, |
2743 | vfs_context_t ctx, int is_bulk, enum vtype vtype, ssize_t fixedsize) |
2744 | { |
2745 | struct _attrlist_buf ab; |
2746 | struct _attrlist_paths apaths = {.fullpathptr = NULL, .fullpathlenp = NULL, |
2747 | .relpathptr = NULL, .relpathlenp = NULL, |
2748 | .REALpathptr = NULL, .REALpathlenp = NULL}; |
2749 | ssize_t buf_size; |
2750 | size_t copy_size; |
2751 | ssize_t varsize; |
2752 | const char *vname = NULL; |
2753 | const char *cnp; |
2754 | ssize_t cnl; |
2755 | char *fullpathptr; |
2756 | ssize_t fullpathlen; |
2757 | char *relpathptr; |
2758 | ssize_t relpathlen; |
2759 | char *REALpathptr; |
2760 | ssize_t REALpathlen; |
2761 | int error; |
2762 | int proc_is64; |
2763 | int return_valid; |
2764 | int pack_invalid; |
2765 | int is_realdev; |
2766 | int alloc_local_buf; |
2767 | const int use_fork = options & FSOPT_ATTR_CMN_EXTENDED; |
2768 | |
2769 | proc_is64 = proc_is64bit(vfs_context_proc(ctx)); |
2770 | ab.base = NULL; |
2771 | cnp = "unknown" ; |
2772 | cnl = 0; |
2773 | fullpathptr = NULL; |
2774 | fullpathlen = 0; |
2775 | relpathptr = NULL; |
2776 | relpathlen = 0; |
2777 | REALpathptr = NULL; |
2778 | REALpathlen = 0; |
2779 | error = 0; |
2780 | alloc_local_buf = 0; |
2781 | |
2782 | buf_size = (ssize_t)uio_resid(a_uio: auio); |
2783 | if ((buf_size <= 0) || (uio_iovcnt(a_uio: auio) > 1)) { |
2784 | return EINVAL; |
2785 | } |
2786 | |
2787 | copy_size = 0; |
2788 | /* Check for special packing semantics */ |
2789 | return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0; |
2790 | pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0; |
2791 | is_realdev = options & FSOPT_RETURN_REALDEV ? 1 : 0; |
2792 | |
2793 | if (pack_invalid) { |
2794 | /* Generate a valid mask for post processing */ |
2795 | bcopy(src: &(alp->commonattr), dst: &ab.valid, n: sizeof(attribute_set_t)); |
2796 | } |
2797 | |
2798 | /* did we ask for something the filesystem doesn't support? */ |
2799 | if (vap->va_active && |
2800 | (!VATTR_ALL_SUPPORTED(vap) |
2801 | #if CONFIG_FIRMLINKS |
2802 | /* For firmlink targets we have to overide what the FS returned for parentid */ |
2803 | || |
2804 | (!is_realdev && vp && (vp->v_flag & VFMLINKTARGET) && vp->v_fmlink && |
2805 | (vp->v_fmlink->v_type == VDIR) && |
2806 | (alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID))) |
2807 | #endif |
2808 | )) { |
2809 | // this disables the selectors that were not supported by the filesystem |
2810 | vattr_get_alt_data(vp, alp, vap, return_valid, is_bulk, is_realdev, |
2811 | ctx); |
2812 | |
2813 | /* check again */ |
2814 | if (!VATTR_ALL_SUPPORTED(vap)) { |
2815 | if (return_valid && pack_invalid) { |
2816 | /* Fix up valid mask for post processing */ |
2817 | getattrlist_fixupattrs(asp: &ab.valid, vap, use_fork); |
2818 | |
2819 | /* Force packing of everything asked for */ |
2820 | vap->va_supported = vap->va_active; |
2821 | } else if (return_valid) { |
2822 | /* Adjust the requested attributes */ |
2823 | getattrlist_fixupattrs( |
2824 | asp: (attribute_set_t *)&(alp->commonattr), vap, use_fork); |
2825 | } else { |
2826 | error = EINVAL; |
2827 | } |
2828 | } |
2829 | |
2830 | if (error) { |
2831 | goto out; |
2832 | } |
2833 | } |
2834 | |
2835 | //if a path is requested, allocate a temporary buffer to build it |
2836 | if (vp && (alp->commonattr & (ATTR_CMN_FULLPATH))) { |
2837 | fullpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO); |
2838 | apaths.fullpathptr = fullpathptr; |
2839 | apaths.fullpathlenp = &fullpathlen; |
2840 | } |
2841 | |
2842 | // only interpret fork attributes if they're used as new common attributes |
2843 | if (vp && use_fork) { |
2844 | if (alp->forkattr & (ATTR_CMNEXT_RELPATH)) { |
2845 | relpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO); |
2846 | apaths.relpathptr = relpathptr; |
2847 | apaths.relpathlenp = &relpathlen; |
2848 | } |
2849 | |
2850 | if (alp->forkattr & (ATTR_CMNEXT_NOFIRMLINKPATH)) { |
2851 | REALpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO); |
2852 | apaths.REALpathptr = REALpathptr; |
2853 | apaths.REALpathlenp = &REALpathlen; |
2854 | } |
2855 | } |
2856 | |
2857 | /* |
2858 | * Compute variable-space requirements. |
2859 | */ |
2860 | error = calc_varsize(vp, alp, vap, varsizep: &varsize, pathsp: &apaths, vnamep: &vname, cnpp: &cnp, cnlp: &cnl); |
2861 | if (error) { |
2862 | goto out; |
2863 | } |
2864 | |
2865 | /* |
2866 | * Allocate a target buffer for attribute results. |
2867 | * |
2868 | * Note that we won't ever copy out more than the caller requested, even though |
2869 | * we might have to allocate more than they offer so that the diagnostic checks |
2870 | * don't result in a panic if the caller's buffer is too small. |
2871 | */ |
2872 | ab.allocated = fixedsize + varsize; |
2873 | /* Cast 'allocated' to an unsigned to verify allocation size */ |
2874 | if (((size_t)ab.allocated) > ATTR_MAX_BUFFER) { |
2875 | error = ENOMEM; |
2876 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)" , ab.allocated, ATTR_MAX_BUFFER); |
2877 | goto out; |
2878 | } |
2879 | |
2880 | /* |
2881 | * Special handling for bulk calls, align to 8 (and only if enough |
2882 | * space left. |
2883 | */ |
2884 | if (is_bulk) { |
2885 | if (buf_size < ab.allocated) { |
2886 | goto out; |
2887 | } else { |
2888 | ssize_t newlen; |
2889 | |
2890 | newlen = (ab.allocated + 7) & ~0x07; |
2891 | /* Align only if enough space for alignment */ |
2892 | if (newlen <= buf_size) { |
2893 | ab.allocated = newlen; |
2894 | } |
2895 | } |
2896 | } |
2897 | |
2898 | /* |
2899 | * See if we can reuse buffer passed in i.e. it is a kernel buffer |
2900 | * and big enough. |
2901 | */ |
2902 | if (uio_isuserspace(a_uio: auio) || (buf_size < ab.allocated)) { |
2903 | ab.base = kalloc_data(ab.allocated, Z_ZERO | Z_WAITOK); |
2904 | alloc_local_buf = 1; |
2905 | } else { |
2906 | /* |
2907 | * In case this is a kernel buffer and sufficiently |
2908 | * big, this function will try to use that buffer |
2909 | * instead of allocating another buffer and bcopy'ing |
2910 | * into it. |
2911 | * |
2912 | * The calculation below figures out where to start |
2913 | * writing in the buffer and once all the data has been |
2914 | * filled in, uio_resid is updated to reflect the usage |
2915 | * of the buffer. |
2916 | * |
2917 | * uio_offset cannot be used here to determine the |
2918 | * starting location as uio_offset could be set to a |
2919 | * value which has nothing to do the location |
2920 | * in the buffer. |
2921 | */ |
2922 | ab.base = (char *)uio_curriovbase(a_uio: auio) + |
2923 | ((ssize_t)uio_curriovlen(a_uio: auio) - buf_size); |
2924 | bzero(s: ab.base, n: ab.allocated); |
2925 | } |
2926 | |
2927 | if (ab.base == NULL) { |
2928 | error = ENOMEM; |
2929 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer" , ab.allocated); |
2930 | goto out; |
2931 | } |
2932 | |
2933 | |
2934 | /* set the S_IFMT bits for the mode */ |
2935 | if (alp->commonattr & ATTR_CMN_ACCESSMASK) { |
2936 | if (vp) { |
2937 | switch (vp->v_type) { |
2938 | case VREG: |
2939 | vap->va_mode |= S_IFREG; |
2940 | break; |
2941 | case VDIR: |
2942 | vap->va_mode |= S_IFDIR; |
2943 | break; |
2944 | case VBLK: |
2945 | vap->va_mode |= S_IFBLK; |
2946 | break; |
2947 | case VCHR: |
2948 | vap->va_mode |= S_IFCHR; |
2949 | break; |
2950 | case VLNK: |
2951 | vap->va_mode |= S_IFLNK; |
2952 | break; |
2953 | case VSOCK: |
2954 | vap->va_mode |= S_IFSOCK; |
2955 | break; |
2956 | case VFIFO: |
2957 | vap->va_mode |= S_IFIFO; |
2958 | break; |
2959 | default: |
2960 | error = EBADF; |
2961 | goto out; |
2962 | } |
2963 | } |
2964 | } |
2965 | |
2966 | /* |
2967 | * Pack results into the destination buffer. |
2968 | */ |
2969 | ab.fixedcursor = ab.base + sizeof(uint32_t); |
2970 | if (return_valid) { |
2971 | ab.fixedcursor += sizeof(attribute_set_t); |
2972 | bzero(s: &ab.actual, n: sizeof(ab.actual)); |
2973 | } |
2974 | ab.varcursor = ab.base + fixedsize; |
2975 | ab.needed = ab.allocated; |
2976 | |
2977 | /* common attributes ************************************************/ |
2978 | error = attr_pack_common(ctx, mp: (options & FSOPT_RETURN_REALDEV ? mp : NULL), |
2979 | vp, alp, abp: &ab, vap, proc_is64, cnp, cnl, fullpathptr, fullpathlen, |
2980 | return_valid, pack_invalid, vtype, is_bulk); |
2981 | |
2982 | /* directory attributes *********************************************/ |
2983 | if (!error && alp->dirattr && (vtype == VDIR)) { |
2984 | error = attr_pack_dir(vp, alp, abp: &ab, vap, return_valid, pack_invalid); |
2985 | } |
2986 | |
2987 | /* file attributes **************************************************/ |
2988 | if (!error && alp->fileattr && (vtype != VDIR)) { |
2989 | error = attr_pack_file(ctx, vp, alp, abp: &ab, vap, return_valid, |
2990 | pack_invalid, is_bulk); |
2991 | } |
2992 | |
2993 | /* common extended attributes *****************************************/ |
2994 | if (!error && use_fork) { |
2995 | error = attr_pack_common_extended(mp, vp, alp, abp: &ab, relpathptr, relpathlen, |
2996 | REALpathptr, REALpathlen, vap, return_valid, pack_invalid); |
2997 | } |
2998 | |
2999 | if (error) { |
3000 | goto out; |
3001 | } |
3002 | |
3003 | /* diagnostic */ |
3004 | if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize) { |
3005 | panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x" , |
3006 | fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr); |
3007 | } |
3008 | if (!return_valid && ab.varcursor != (ab.base + ab.needed)) { |
3009 | panic("packed variable field size mismatch; used %ld but expected %ld" , (long) (ab.varcursor - ab.base), ab.needed); |
3010 | } |
3011 | |
3012 | /* |
3013 | * In the compatible case, we report the smaller of the required and returned sizes. |
3014 | * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size |
3015 | * of the result buffer, even if we copied less out. The caller knows how big a buffer |
3016 | * they gave us, so they can always check for truncation themselves. |
3017 | */ |
3018 | *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? (uint32_t)ab.needed : (uint32_t)lmin(a: ab.allocated, b: ab.needed); |
3019 | |
3020 | /* Return attribute set output if requested. */ |
3021 | if (return_valid) { |
3022 | ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS; |
3023 | if (pack_invalid) { |
3024 | /* Only report the attributes that are valid */ |
3025 | ab.actual.commonattr &= ab.valid.commonattr; |
3026 | ab.actual.dirattr &= ab.valid.dirattr; |
3027 | ab.actual.fileattr &= ab.valid.fileattr; |
3028 | } |
3029 | bcopy(src: &ab.actual, dst: ab.base + sizeof(uint32_t), n: sizeof(ab.actual)); |
3030 | } |
3031 | |
3032 | copy_size = lmin(a: buf_size, b: ab.allocated); |
3033 | |
3034 | /* Only actually copyout as much out as the user buffer can hold */ |
3035 | if (alloc_local_buf) { |
3036 | error = uiomove(cp: ab.base, n: (int)copy_size, uio: auio); |
3037 | } else { |
3038 | off_t orig_offset = uio_offset(a_uio: auio); |
3039 | |
3040 | /* |
3041 | * The buffer in the uio struct was used directly |
3042 | * (i.e. it was a kernel buffer and big enough |
3043 | * to hold the data required) in order to avoid |
3044 | * un-needed allocation and copies. |
3045 | * |
3046 | * At this point, update the resid value to what it |
3047 | * would be if this was the result of a uiomove. The |
3048 | * offset is also incremented, though it may not |
3049 | * mean anything to the caller but that is what |
3050 | * uiomove does as well. |
3051 | */ |
3052 | uio_setresid(a_uio: auio, a_value: buf_size - copy_size); |
3053 | uio_setoffset(a_uio: auio, a_offset: orig_offset + (off_t)copy_size); |
3054 | } |
3055 | |
3056 | out: |
3057 | if (vname) { |
3058 | vnode_putname(name: vname); |
3059 | } |
3060 | if (fullpathptr) { |
3061 | zfree(ZV_NAMEI, fullpathptr); |
3062 | } |
3063 | if (relpathptr) { |
3064 | zfree(ZV_NAMEI, relpathptr); |
3065 | } |
3066 | if (REALpathptr) { |
3067 | zfree(ZV_NAMEI, REALpathptr); |
3068 | } |
3069 | if (alloc_local_buf) { |
3070 | kfree_data(ab.base, ab.allocated); |
3071 | } |
3072 | return error; |
3073 | } |
3074 | |
3075 | errno_t |
3076 | vfs_attr_pack_ext(mount_t mp, vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options, |
3077 | struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx) |
3078 | { |
3079 | int error; |
3080 | ssize_t fixedsize; |
3081 | uint64_t orig_active; |
3082 | struct attrlist orig_al; |
3083 | enum vtype v_type; |
3084 | uid_t ouid = vap->va_uid; |
3085 | gid_t ogid = vap->va_gid; |
3086 | |
3087 | if (vp) { |
3088 | v_type = vnode_vtype(vp); |
3089 | } else { |
3090 | v_type = vap->va_objtype; |
3091 | } |
3092 | |
3093 | orig_al = *alp; |
3094 | orig_active = vap->va_active; |
3095 | vap->va_active = 0; |
3096 | |
3097 | error = getattrlist_setupvattr_all(alp, vap, obj_type: v_type, fixedsize: &fixedsize, |
3098 | is_64bit: proc_is64bit(vfs_context_proc(ctx)), use_fork: options & FSOPT_ATTR_CMN_EXTENDED); |
3099 | |
3100 | if (error) { |
3101 | VFS_DEBUG(ctx, vp, |
3102 | "ATTRLIST - ERROR: setup for request failed" ); |
3103 | goto out; |
3104 | } |
3105 | |
3106 | if (mp) { |
3107 | vnode_attr_handle_uid_and_gid(vap, mp, ctx); |
3108 | } |
3109 | |
3110 | error = vfs_attr_pack_internal(mp, vp, auio: uio, alp, |
3111 | options: options | FSOPT_REPORT_FULLSIZE, vap, NULL, ctx, is_bulk: 1, vtype: v_type, |
3112 | fixedsize); |
3113 | |
3114 | if (mp) { |
3115 | vap->va_uid = ouid; |
3116 | vap->va_gid = ogid; |
3117 | } |
3118 | VATTR_CLEAR_SUPPORTED_ALL(vap); |
3119 | vap->va_active = orig_active; |
3120 | *alp = orig_al; |
3121 | out: |
3122 | return error; |
3123 | } |
3124 | |
3125 | errno_t |
3126 | vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options, |
3127 | struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx) |
3128 | { |
3129 | return vfs_attr_pack_ext(NULL, vp, uio, alp, options, vap, fndesc, ctx); |
3130 | } |
3131 | |
3132 | /* |
3133 | * Obtain attribute information about a filesystem object. |
3134 | * |
3135 | * Note: The alt_name parameter can be used by the caller to pass in the vnode |
3136 | * name obtained from some authoritative source (eg. readdir vnop); where |
3137 | * filesystems' getattr vnops do not support ATTR_CMN_NAME, the alt_name will be |
3138 | * used as the ATTR_CMN_NAME attribute returned in vnode_attr.va_name. |
3139 | * |
3140 | */ |
3141 | static int |
3142 | getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, |
3143 | user_addr_t attributeBuffer, size_t bufferSize, uint64_t options, |
3144 | enum uio_seg segflg, char* authoritative_name, struct ucred *file_cred) |
3145 | { |
3146 | struct vnode_attr *va; |
3147 | kauth_action_t action; |
3148 | ssize_t fixedsize; |
3149 | char *va_name; |
3150 | int proc_is64; |
3151 | int error; |
3152 | int return_valid; |
3153 | int pack_invalid; |
3154 | int vtype = 0; |
3155 | uio_t auio; |
3156 | UIO_STACKBUF(uio_buf, 1); |
3157 | // must be true for fork attributes to be used as new common attributes |
3158 | const int use_fork = (options & FSOPT_ATTR_CMN_EXTENDED) != 0; |
3159 | |
3160 | if (bufferSize < sizeof(uint32_t)) { |
3161 | return ERANGE; |
3162 | } |
3163 | |
3164 | proc_is64 = proc_is64bit(vfs_context_proc(ctx)); |
3165 | |
3166 | if (segflg == UIO_USERSPACE) { |
3167 | if (proc_is64) { |
3168 | segflg = UIO_USERSPACE64; |
3169 | } else { |
3170 | segflg = UIO_USERSPACE32; |
3171 | } |
3172 | } |
3173 | auio = uio_createwithbuffer(a_iovcount: 1, a_offset: 0, a_spacetype: segflg, a_iodirection: UIO_READ, |
3174 | a_buf_p: &uio_buf[0], a_buffer_size: sizeof(uio_buf)); |
3175 | uio_addiov(a_uio: auio, a_baseaddr: attributeBuffer, a_length: bufferSize); |
3176 | |
3177 | va = kalloc_type(struct vnode_attr, Z_WAITOK); |
3178 | VATTR_INIT(va); |
3179 | va_name = NULL; |
3180 | |
3181 | if (alp->bitmapcount != ATTR_BIT_MAP_COUNT) { |
3182 | error = EINVAL; |
3183 | goto out; |
3184 | } |
3185 | |
3186 | VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'" , |
3187 | vp, vfs_context_proc(ctx)->p_comm, alp->commonattr, alp->volattr, alp->fileattr, alp->dirattr, alp->forkattr, |
3188 | (options & FSOPT_NOFOLLOW) ? "no" :"" , vp->v_name); |
3189 | |
3190 | #if CONFIG_MACF |
3191 | error = mac_vnode_check_getattrlist(ctx, vp, alist: alp, options); |
3192 | if (error) { |
3193 | goto out; |
3194 | } |
3195 | #endif /* MAC */ |
3196 | |
3197 | /* |
3198 | * It is legal to request volume or file attributes, but not both. |
3199 | * |
3200 | * 26903449 fork attributes can also be requested, but only if they're |
3201 | * interpreted as new, common attributes |
3202 | */ |
3203 | if (alp->volattr) { |
3204 | if (alp->fileattr || alp->dirattr || (alp->forkattr && !use_fork)) { |
3205 | error = EINVAL; |
3206 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory attributes" ); |
3207 | goto out; |
3208 | } |
3209 | /* handle volume attribute request */ |
3210 | error = getvolattrlist(ctx, vp, alp, attributeBuffer, |
3211 | bufferSize, options, segflg, is_64bit: proc_is64); |
3212 | goto out; |
3213 | } |
3214 | |
3215 | /* |
3216 | * ATTR_CMN_GEN_COUNT and ATTR_CMN_DOCUMENT_ID reuse the bits |
3217 | * originally allocated to ATTR_CMN_NAMEDATTRCOUNT and |
3218 | * ATTR_CMN_NAMEDATTRLIST. |
3219 | */ |
3220 | if ((alp->commonattr & (ATTR_CMN_GEN_COUNT | ATTR_CMN_DOCUMENT_ID)) && |
3221 | !(options & FSOPT_ATTR_CMN_EXTENDED)) { |
3222 | error = EINVAL; |
3223 | goto out; |
3224 | } |
3225 | |
3226 | /* common extended attributes require FSOPT_ATTR_CMN_EXTENDED option */ |
3227 | if (!(use_fork) && (alp->forkattr & ATTR_CMNEXT_VALIDMASK)) { |
3228 | error = EINVAL; |
3229 | goto out; |
3230 | } |
3231 | |
3232 | /* FSOPT_ATTR_CMN_EXTENDED requires forkattrs are not referenced */ |
3233 | if ((options & FSOPT_ATTR_CMN_EXTENDED) && (alp->forkattr & (ATTR_FORK_VALIDMASK))) { |
3234 | error = EINVAL; |
3235 | goto out; |
3236 | } |
3237 | |
3238 | /* Check for special packing semantics */ |
3239 | return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0; |
3240 | pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0; |
3241 | if (pack_invalid) { |
3242 | /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */ |
3243 | if (!return_valid || (alp->forkattr && !use_fork)) { |
3244 | error = EINVAL; |
3245 | goto out; |
3246 | } |
3247 | /* Keep invalid attrs from being uninitialized */ |
3248 | bzero(s: va, n: sizeof(*va)); |
3249 | } |
3250 | |
3251 | /* Pick up the vnode type. If the FS is bad and changes vnode types on us, we |
3252 | * will have a valid snapshot that we can work from here. |
3253 | */ |
3254 | vtype = vp->v_type; |
3255 | |
3256 | /* |
3257 | * Set up the vnode_attr structure and authorise. |
3258 | */ |
3259 | if ((error = getattrlist_setupvattr(alp, vap: va, sizep: &fixedsize, actionp: &action, is_64bit: proc_is64, isdir: (vtype == VDIR), use_fork)) != 0) { |
3260 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed" ); |
3261 | goto out; |
3262 | } |
3263 | if ((error = vnode_authorize(vp, NULL, action, ctx)) != 0) { |
3264 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorisation failed/denied" ); |
3265 | goto out; |
3266 | } |
3267 | |
3268 | |
3269 | if (va->va_active != 0) { |
3270 | uint64_t va_active = va->va_active; |
3271 | |
3272 | /* |
3273 | * If we're going to ask for va_name, allocate a buffer to point it at |
3274 | */ |
3275 | if (VATTR_IS_ACTIVE(va, va_name)) { |
3276 | va_name = zalloc(view: ZV_NAMEI); |
3277 | /* |
3278 | * If we have an authoritative_name, prefer that name. |
3279 | * |
3280 | * N.B. Since authoritative_name implies this is coming from getattrlistbulk, |
3281 | * we know the name is authoritative. For /dev/fd, we want to use the file |
3282 | * descriptor as the name not the underlying name of the associate vnode in a |
3283 | * particular file system. |
3284 | */ |
3285 | if (authoritative_name) { |
3286 | /* Don't ask the file system */ |
3287 | VATTR_CLEAR_ACTIVE(va, va_name); |
3288 | strlcpy(dst: va_name, src: authoritative_name, MAXPATHLEN); |
3289 | } |
3290 | } |
3291 | |
3292 | va->va_name = authoritative_name ? NULL : va_name; |
3293 | |
3294 | if (options & FSOPT_RETURN_REALDEV) { |
3295 | va->va_vaflags |= VA_REALFSID; |
3296 | } |
3297 | |
3298 | /* |
3299 | * Call the filesystem. |
3300 | */ |
3301 | if ((error = vnode_getattr(vp, vap: va, ctx)) != 0) { |
3302 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d" , error); |
3303 | goto out; |
3304 | } |
3305 | #if CONFIG_MACF |
3306 | /* |
3307 | * Give MAC polices a chance to reject or filter the |
3308 | * attributes returned by the filesystem. Note that MAC |
3309 | * policies are consulted *after* calling the filesystem |
3310 | * because filesystems can return more attributes than |
3311 | * were requested so policies wouldn't be authoritative |
3312 | * is consulted beforehand. This also gives policies an |
3313 | * opportunity to change the values of attributes |
3314 | * retrieved. |
3315 | */ |
3316 | error = mac_vnode_check_getattr(ctx, file_cred, vp, va); |
3317 | if (error) { |
3318 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d" , error); |
3319 | goto out; |
3320 | } |
3321 | #else |
3322 | (void)file_cred; |
3323 | #endif |
3324 | |
3325 | /* |
3326 | * It we ask for the name, i.e., vname is non null and |
3327 | * we have an authoritative name, then reset va_name is |
3328 | * active and if needed set va_name is supported. |
3329 | * |
3330 | * A (buggy) filesystem may change fields which belong |
3331 | * to us. We try to deal with that here as well. |
3332 | */ |
3333 | va->va_active = va_active; |
3334 | if (authoritative_name && va_name) { |
3335 | VATTR_SET_ACTIVE(va, va_name); |
3336 | if (!(VATTR_IS_SUPPORTED(va, va_name))) { |
3337 | VATTR_SET_SUPPORTED(va, va_name); |
3338 | } |
3339 | } |
3340 | va->va_name = va_name; |
3341 | } |
3342 | |
3343 | error = vfs_attr_pack_internal(mp: vp->v_mount, vp, auio, alp, options, vap: va, NULL, ctx, |
3344 | is_bulk: 0, vtype, fixedsize); |
3345 | |
3346 | out: |
3347 | if (va_name) { |
3348 | zfree(ZV_NAMEI, va_name); |
3349 | } |
3350 | if (VATTR_IS_SUPPORTED(va, va_acl) && (va->va_acl != NULL)) { |
3351 | kauth_acl_free(fsp: va->va_acl); |
3352 | } |
3353 | kfree_type(struct vnode_attr, va); |
3354 | |
3355 | VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d" , error); |
3356 | return error; |
3357 | } |
3358 | |
3359 | int |
3360 | fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval) |
3361 | { |
3362 | vfs_context_t ctx; |
3363 | vnode_t vp; |
3364 | int error; |
3365 | struct attrlist al; |
3366 | struct fileproc *fp; |
3367 | |
3368 | ctx = vfs_context_current(); |
3369 | vp = NULL; |
3370 | fp = NULL; |
3371 | error = 0; |
3372 | |
3373 | if ((error = fp_get_ftype(p, fd: uap->fd, ftype: DTYPE_VNODE, EINVAL, fpp: &fp)) != 0) { |
3374 | return error; |
3375 | } |
3376 | vp = (struct vnode *)fp_get_data(fp); |
3377 | |
3378 | if ((error = vnode_getwithref(vp)) != 0) { |
3379 | goto out; |
3380 | } |
3381 | |
3382 | /* |
3383 | * Fetch the attribute request. |
3384 | */ |
3385 | error = copyin(uap->alist, &al, sizeof(al)); |
3386 | if (error) { |
3387 | goto out_vnode_put; |
3388 | } |
3389 | |
3390 | /* Default to using the vnode's name. */ |
3391 | error = getattrlist_internal(ctx, vp, alp: &al, attributeBuffer: uap->attributeBuffer, |
3392 | bufferSize: uap->bufferSize, options: uap->options, |
3393 | segflg: (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : \ |
3394 | UIO_USERSPACE32), NULL, |
3395 | file_cred: fp->fp_glob->fg_cred); |
3396 | |
3397 | out_vnode_put: |
3398 | vnode_put(vp); |
3399 | out: |
3400 | fp_drop(p, fd: uap->fd, fp, locked: 0); |
3401 | |
3402 | return error; |
3403 | } |
3404 | |
3405 | static int |
3406 | getattrlistat_internal(vfs_context_t ctx, user_addr_t path, |
3407 | struct attrlist *alp, user_addr_t attributeBuffer, size_t bufferSize, |
3408 | uint64_t options, enum uio_seg segflg, enum uio_seg pathsegflg, int fd) |
3409 | { |
3410 | struct nameidata nd; |
3411 | vnode_t vp; |
3412 | int32_t nameiflags; |
3413 | int error; |
3414 | |
3415 | nameiflags = 0; |
3416 | /* |
3417 | * Look up the file. |
3418 | */ |
3419 | if (!(options & (FSOPT_NOFOLLOW | FSOPT_NOFOLLOW_ANY))) { |
3420 | nameiflags |= FOLLOW; |
3421 | } |
3422 | |
3423 | nameiflags |= AUDITVNPATH1; |
3424 | NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, pathsegflg, |
3425 | path, ctx); |
3426 | if (options & FSOPT_NOFOLLOW_ANY) { |
3427 | nd.ni_flag |= NAMEI_NOFOLLOW_ANY; |
3428 | } |
3429 | |
3430 | error = nameiat(ndp: &nd, dirfd: fd); |
3431 | |
3432 | if (error) { |
3433 | return error; |
3434 | } |
3435 | |
3436 | vp = nd.ni_vp; |
3437 | |
3438 | error = getattrlist_internal(ctx, vp, alp, attributeBuffer, |
3439 | bufferSize, options, segflg, NULL, NOCRED); |
3440 | |
3441 | /* Retain the namei reference until the getattrlist completes. */ |
3442 | nameidone(&nd); |
3443 | vnode_put(vp); |
3444 | return error; |
3445 | } |
3446 | |
3447 | int |
3448 | getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval) |
3449 | { |
3450 | enum uio_seg segflg; |
3451 | struct attrlist al; |
3452 | int error; |
3453 | |
3454 | segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; |
3455 | |
3456 | /* |
3457 | * Fetch the attribute request. |
3458 | */ |
3459 | error = copyin(uap->alist, &al, sizeof(al)); |
3460 | if (error) { |
3461 | return error; |
3462 | } |
3463 | |
3464 | return getattrlistat_internal(ctx: vfs_context_current(), |
3465 | CAST_USER_ADDR_T(uap->path), alp: &al, |
3466 | CAST_USER_ADDR_T(uap->attributeBuffer), bufferSize: uap->bufferSize, |
3467 | options: (uint64_t)uap->options, segflg, pathsegflg: segflg, AT_FDCWD); |
3468 | } |
3469 | |
3470 | int |
3471 | getattrlistat(proc_t p, struct getattrlistat_args *uap, __unused int32_t *retval) |
3472 | { |
3473 | enum uio_seg segflg; |
3474 | struct attrlist al; |
3475 | int error; |
3476 | |
3477 | segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; |
3478 | |
3479 | /* |
3480 | * Fetch the attribute request. |
3481 | */ |
3482 | error = copyin(uap->alist, &al, sizeof(al)); |
3483 | if (error) { |
3484 | return error; |
3485 | } |
3486 | |
3487 | return getattrlistat_internal(ctx: vfs_context_current(), |
3488 | CAST_USER_ADDR_T(uap->path), alp: &al, |
3489 | CAST_USER_ADDR_T(uap->attributeBuffer), bufferSize: uap->bufferSize, |
3490 | options: (uint64_t)uap->options, segflg, pathsegflg: segflg, fd: uap->fd); |
3491 | } |
3492 | |
3493 | /* |
3494 | * This refills the per-fd direntries cache by issuing a VNOP_READDIR. |
3495 | * It attempts to try and find a size the filesystem responds to, so |
3496 | * it first tries 1 direntry sized buffer and going from 1 to 2 to 4 |
3497 | * direntry sized buffers to readdir. If the filesystem does not respond |
3498 | * to 4 * direntry it returns the error by the filesystem (if any) and sets |
3499 | * EOF. |
3500 | * |
3501 | * This function also tries again if the last "refill" returned an EOF |
3502 | * to try and get any additional entries if they were added after the last |
3503 | * refill. |
3504 | */ |
3505 | static int |
3506 | refill_fd_direntries(vfs_context_t ctx, vnode_t dvp, struct fd_vn_data *fvd, |
3507 | int *eofflagp) |
3508 | { |
3509 | uio_t rdir_uio; |
3510 | UIO_STACKBUF(uio_buf, 1); |
3511 | size_t rdirbufsiz; |
3512 | size_t rdirbufused; |
3513 | int eofflag; |
3514 | int nentries; |
3515 | int error; |
3516 | |
3517 | /* |
3518 | * If the last readdir returned EOF, don't try again. |
3519 | */ |
3520 | if (fvd->fv_eofflag) { |
3521 | *eofflagp = 1; |
3522 | if (fvd->fv_buf) { |
3523 | kfree_data(fvd->fv_buf, fvd->fv_bufallocsiz); |
3524 | fvd->fv_buf = NULL; |
3525 | } |
3526 | return 0; |
3527 | } |
3528 | |
3529 | error = 0; |
3530 | |
3531 | /* |
3532 | * If there is a cached allocation size of the dirbuf that should be |
3533 | * allocated, use that. Otherwise start with a allocation size of |
3534 | * FV_DIRBUF_START_SIZ. This start size may need to be increased if the |
3535 | * filesystem doesn't respond to the initial size. |
3536 | */ |
3537 | |
3538 | if (fvd->fv_offset && fvd->fv_bufallocsiz) { |
3539 | rdirbufsiz = fvd->fv_bufallocsiz; |
3540 | } else { |
3541 | rdirbufsiz = FV_DIRBUF_START_SIZ; |
3542 | } |
3543 | |
3544 | *eofflagp = 0; |
3545 | |
3546 | rdir_uio = uio_createwithbuffer(a_iovcount: 1, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ, |
3547 | a_buf_p: &uio_buf[0], a_buffer_size: sizeof(uio_buf)); |
3548 | |
3549 | retry_alloc: |
3550 | /* |
3551 | * Don't explicitly zero out this buffer since this is |
3552 | * not copied out to user space. |
3553 | */ |
3554 | if (!fvd->fv_buf) { |
3555 | fvd->fv_buf = kalloc_data(rdirbufsiz, Z_WAITOK); |
3556 | fvd->fv_bufallocsiz = rdirbufsiz; |
3557 | fvd->fv_bufdone = 0; |
3558 | } |
3559 | |
3560 | uio_reset(a_uio: rdir_uio, a_offset: fvd->fv_eoff, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ); |
3561 | uio_addiov(a_uio: rdir_uio, CAST_USER_ADDR_T(fvd->fv_buf), a_length: rdirbufsiz); |
3562 | |
3563 | /* |
3564 | * Some filesystems do not set nentries or eofflag... |
3565 | */ |
3566 | eofflag = 0; |
3567 | nentries = 0; |
3568 | error = vnode_readdir64(vp: dvp, uio: rdir_uio, VNODE_READDIR_EXTENDED, |
3569 | eofflag: &eofflag, numdirent: &nentries, ctxp: ctx); |
3570 | |
3571 | rdirbufused = rdirbufsiz - (size_t)uio_resid(a_uio: rdir_uio); |
3572 | |
3573 | if (!error && (rdirbufused > 0) && (rdirbufused <= rdirbufsiz)) { |
3574 | /* Save offsets */ |
3575 | fvd->fv_soff = fvd->fv_eoff; |
3576 | fvd->fv_eoff = uio_offset(a_uio: rdir_uio); |
3577 | /* Save eofflag state but don't return EOF for this time.*/ |
3578 | fvd->fv_eofflag = eofflag; |
3579 | eofflag = 0; |
3580 | /* Reset buffer parameters */ |
3581 | fvd->fv_bufsiz = rdirbufused; |
3582 | fvd->fv_bufdone = 0; |
3583 | bzero(s: fvd->fv_buf + rdirbufused, n: rdirbufsiz - rdirbufused); |
3584 | } else if (!eofflag && (rdirbufsiz < FV_DIRBUF_MAX_SIZ)) { |
3585 | /* |
3586 | * Some Filesystems have higher requirements for the |
3587 | * smallest buffer size they will respond to for a |
3588 | * directory listing. Start (relatively) small but increase |
3589 | * it upto FV_DIRBUF_MAX_SIZ. Most should be good with |
3590 | * 1*direntry. Cache the size found so that this does not need |
3591 | * need to be done every time. This also means that an error |
3592 | * from VNOP_READDIR is ignored until at least FV_DIRBUF_MAX_SIZ |
3593 | * has been attempted. |
3594 | */ |
3595 | kfree_data(fvd->fv_buf, fvd->fv_bufallocsiz); |
3596 | rdirbufsiz = 2 * rdirbufsiz; |
3597 | fvd->fv_bufallocsiz = 0; |
3598 | goto retry_alloc; |
3599 | } else if (!error) { |
3600 | /* |
3601 | * The Filesystem did not set eofflag but also did not |
3602 | * return any entries (or an error). It is presumed that |
3603 | * EOF has been reached. |
3604 | */ |
3605 | fvd->fv_eofflag = eofflag = 1; |
3606 | } |
3607 | |
3608 | /* |
3609 | * If the filesystem returned an error and it had previously returned |
3610 | * EOF, ignore the error and set EOF. |
3611 | */ |
3612 | if (error && fvd->fv_eofflag) { |
3613 | eofflag = 1; |
3614 | error = 0; |
3615 | } |
3616 | |
3617 | /* |
3618 | * If either the directory has either hit EOF or an error, now is a good |
3619 | * time to free up directory entry buffer. |
3620 | */ |
3621 | if ((error || eofflag) && fvd->fv_buf) { |
3622 | kfree_data(fvd->fv_buf, fvd->fv_bufallocsiz); |
3623 | if (error) { |
3624 | fvd->fv_bufallocsiz = 0; |
3625 | } |
3626 | } |
3627 | |
3628 | *eofflagp = eofflag; |
3629 | |
3630 | return error; |
3631 | } |
3632 | |
3633 | /* |
3634 | * gets the current direntry. To advance to the next direntry this has to be |
3635 | * paired with a direntry_done. |
3636 | * |
3637 | * Since directories have restrictions on where directory enumeration |
3638 | * can restart from, entries are first read into* a per fd diectory entry |
3639 | * "cache" and entries provided from that cache. |
3640 | */ |
3641 | static int |
3642 | get_direntry(vfs_context_t ctx, vnode_t dvp, struct fd_vn_data *fvd, |
3643 | int *eofflagp, struct direntry **dpp) |
3644 | { |
3645 | int eofflag; |
3646 | int error; |
3647 | |
3648 | *eofflagp = 0; |
3649 | *dpp = NULL; |
3650 | error = 0; |
3651 | if (!fvd->fv_bufsiz) { |
3652 | error = refill_fd_direntries(ctx, dvp, fvd, eofflagp: &eofflag); |
3653 | if (error) { |
3654 | return error; |
3655 | } |
3656 | if (eofflag) { |
3657 | *eofflagp = eofflag; |
3658 | return error; |
3659 | } |
3660 | } |
3661 | |
3662 | *dpp = (struct direntry *)(fvd->fv_buf + fvd->fv_bufdone); |
3663 | return error; |
3664 | } |
3665 | |
3666 | /* |
3667 | * Advances to the next direntry. |
3668 | */ |
3669 | static void |
3670 | direntry_done(struct fd_vn_data *fvd) |
3671 | { |
3672 | struct direntry *dp; |
3673 | |
3674 | dp = (struct direntry *)(fvd->fv_buf + fvd->fv_bufdone); |
3675 | if (dp->d_reclen) { |
3676 | fvd->fv_bufdone += dp->d_reclen; |
3677 | if (fvd->fv_bufdone > fvd->fv_bufsiz) { |
3678 | fvd->fv_bufdone = fvd->fv_bufsiz; |
3679 | } |
3680 | } else { |
3681 | fvd->fv_bufdone = fvd->fv_bufsiz; |
3682 | } |
3683 | |
3684 | /* |
3685 | * If we're at the end the fd direntries cache, reset the |
3686 | * cache trackers. |
3687 | */ |
3688 | if (fvd->fv_bufdone == fvd->fv_bufsiz) { |
3689 | fvd->fv_bufdone = 0; |
3690 | fvd->fv_bufsiz = 0; |
3691 | } |
3692 | } |
3693 | |
3694 | /* |
3695 | * A stripped down version of getattrlist_internal to fill in only select |
3696 | * attributes in case of an error from getattrlist_internal. |
3697 | * |
3698 | * It always returns at least ATTR_BULK_REQUIRED i.e. the name (but may also |
3699 | * return some other attributes which can be obtained from the vnode). |
3700 | * |
3701 | * It does not change the value of the passed in attrlist. |
3702 | * |
3703 | * The objective of this function is to fill in an "error entry", i.e. |
3704 | * an entry with ATTR_CMN_RETURNED_ATTRS & ATTR_CMN_NAME. If the caller |
3705 | * has also asked for ATTR_CMN_ERROR, it is filled in as well. |
3706 | * |
3707 | * Input |
3708 | * vp - vnode pointer |
3709 | * alp - pointer to attrlist struct. |
3710 | * options - options passed to getattrlistbulk(2) |
3711 | * kern_attr_buf - Kernel buffer to fill data (assumes offset 0 in |
3712 | * buffer) |
3713 | * kern_attr_buf_siz - Size of buffer. |
3714 | * needs_error_attr - Whether the caller asked for ATTR_CMN_ERROR |
3715 | * error_attr - This value is used to fill ATTR_CMN_ERROR (if the user |
3716 | * has requested it in the attribute list. |
3717 | * namebuf - This is used to fill in the name. |
3718 | * ctx - vfs context of caller. |
3719 | */ |
3720 | static void |
3721 | get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options, |
3722 | user_addr_t kern_attr_buf, size_t kern_attr_buf_siz, int error_attr, |
3723 | caddr_t namebuf, vfs_context_t ctx) |
3724 | { |
3725 | size_t fsiz, vsiz; |
3726 | struct _attrlist_buf ab; |
3727 | size_t namelen; |
3728 | kauth_action_t action; |
3729 | struct attrlist al; |
3730 | int needs_error_attr = (alp->commonattr & ATTR_CMN_ERROR); |
3731 | |
3732 | /* |
3733 | * To calculate fixed size required, in the FSOPT_PACK_INVAL_ATTRS case, |
3734 | * the fixedsize should include space for all the attributes asked by |
3735 | * the user. Only ATTR_BULK_REQUIRED (and ATTR_CMN_ERROR) will be filled |
3736 | * and will be valid. All other attributes are zeroed out later. |
3737 | * |
3738 | * ATTR_CMN_RETURNED_ATTRS, ATTR_CMN_ERROR and ATTR_CMN_NAME |
3739 | * (the only valid ones being returned from here) happen to be |
3740 | * the first three attributes by order as well. |
3741 | */ |
3742 | al = *alp; |
3743 | if (!(options & FSOPT_PACK_INVAL_ATTRS)) { |
3744 | /* |
3745 | * In this case the fixedsize only needs to be only for the |
3746 | * attributes being actually returned. |
3747 | */ |
3748 | al.commonattr = ATTR_BULK_REQUIRED; |
3749 | if (needs_error_attr) { |
3750 | al.commonattr |= ATTR_CMN_ERROR; |
3751 | } |
3752 | al.fileattr = 0; |
3753 | al.dirattr = 0; |
3754 | } |
3755 | |
3756 | /* |
3757 | * Passing NULL for the vnode_attr pointer is valid for |
3758 | * getattrlist_setupvattr. All that is required is the size. |
3759 | */ |
3760 | fsiz = 0; |
3761 | (void)getattrlist_setupvattr(alp: &al, NULL, sizep: (ssize_t *)&fsiz, |
3762 | actionp: &action, is_64bit: proc_is64bit(vfs_context_proc(ctx)), |
3763 | isdir: (vnode_vtype(vp) == VDIR), use_fork: (options & FSOPT_ATTR_CMN_EXTENDED)); |
3764 | |
3765 | namelen = strlen(s: namebuf); |
3766 | vsiz = namelen + 1; |
3767 | vsiz = ((vsiz + 3) & ~0x03); |
3768 | |
3769 | bzero(s: &ab, n: sizeof(ab)); |
3770 | ab.base = (char *)kern_attr_buf; |
3771 | ab.needed = fsiz + vsiz; |
3772 | |
3773 | /* Fill in the size needed */ |
3774 | *((uint32_t *)ab.base) = (uint32_t)ab.needed; |
3775 | if (ab.needed > (ssize_t)kern_attr_buf_siz) { |
3776 | goto out; |
3777 | } |
3778 | |
3779 | /* |
3780 | * Setup to pack results into the destination buffer. |
3781 | */ |
3782 | ab.fixedcursor = ab.base + sizeof(uint32_t); |
3783 | /* |
3784 | * Zero out buffer, ab.fixedbuffer starts after the first uint32_t |
3785 | * which gives the length. This ensures everything that we don't |
3786 | * fill in explicitly later is zeroed out correctly. |
3787 | */ |
3788 | bzero(s: ab.fixedcursor, n: fsiz); |
3789 | /* |
3790 | * variable size data should start after all the fixed |
3791 | * size data. |
3792 | */ |
3793 | ab.varcursor = ab.base + fsiz; |
3794 | /* |
3795 | * Initialise the value for ATTR_CMN_RETURNED_ATTRS and leave space |
3796 | * Leave space for filling in its value here at the end. |
3797 | */ |
3798 | bzero(s: &ab.actual, n: sizeof(ab.actual)); |
3799 | ab.fixedcursor += sizeof(attribute_set_t); |
3800 | |
3801 | ab.allocated = ab.needed; |
3802 | |
3803 | /* Fill ATTR_CMN_ERROR (if asked for) */ |
3804 | if (needs_error_attr) { |
3805 | ATTR_PACK4(ab, error_attr); |
3806 | ab.actual.commonattr |= ATTR_CMN_ERROR; |
3807 | } |
3808 | |
3809 | /* |
3810 | * Fill ATTR_CMN_NAME, The attrrefrence is packed at this location |
3811 | * but the actual string itself is packed after fixedsize which set |
3812 | * to different lengths based on whether FSOPT_PACK_INVAL_ATTRS |
3813 | * was passed. |
3814 | */ |
3815 | attrlist_pack_string(ab: &ab, source: namebuf, count: namelen); |
3816 | |
3817 | /* |
3818 | * Now Fill in ATTR_CMN_RETURNED_ATTR. This copies to a |
3819 | * location after the count i.e. before ATTR_CMN_ERROR and |
3820 | * ATTR_CMN_NAME. |
3821 | */ |
3822 | ab.actual.commonattr |= ATTR_CMN_NAME | ATTR_CMN_RETURNED_ATTRS; |
3823 | bcopy(src: &ab.actual, dst: ab.base + sizeof(uint32_t), n: sizeof(ab.actual)); |
3824 | out: |
3825 | return; |
3826 | } |
3827 | |
3828 | /* |
3829 | * This is the buffer size required to return at least 1 entry. We need space |
3830 | * for the length, for ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_NAME. Assuming the |
3831 | * smallest filename of a single byte we get |
3832 | */ |
3833 | |
3834 | #define MIN_BUF_SIZE_REQUIRED (sizeof(uint32_t) + sizeof(attribute_set_t) +\ |
3835 | sizeof(attrreference_t)) |
3836 | |
3837 | /* |
3838 | * Read directory entries and get attributes filled in for each directory |
3839 | */ |
3840 | static int |
3841 | readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio, |
3842 | struct attrlist *alp, uint64_t options, int *count, int *eofflagp, |
3843 | vfs_context_t ctx) |
3844 | { |
3845 | caddr_t kern_attr_buf; |
3846 | size_t kern_attr_buf_siz; |
3847 | caddr_t max_path_name_buf = NULL; |
3848 | int error = 0; |
3849 | |
3850 | *count = 0; |
3851 | *eofflagp = 0; |
3852 | |
3853 | if (uio_iovcnt(a_uio: auio) > 1) { |
3854 | return EINVAL; |
3855 | } |
3856 | |
3857 | /* |
3858 | * We fill in a kernel buffer for the attributes and uiomove each |
3859 | * entry's attributes (as returned by getattrlist_internal) |
3860 | */ |
3861 | kern_attr_buf_siz = uio_resid(a_uio: auio); |
3862 | if (kern_attr_buf_siz > ATTR_MAX_BUFFER) { |
3863 | kern_attr_buf_siz = ATTR_MAX_BUFFER; |
3864 | } else if (kern_attr_buf_siz == 0) { |
3865 | /* Nothing to do */ |
3866 | return error; |
3867 | } |
3868 | |
3869 | kern_attr_buf = kalloc_data(kern_attr_buf_siz, Z_WAITOK); |
3870 | |
3871 | while (uio_resid(a_uio: auio) > (user_ssize_t)MIN_BUF_SIZE_REQUIRED) { |
3872 | struct direntry *dp; |
3873 | user_addr_t name_buffer; |
3874 | struct nameidata nd; |
3875 | vnode_t vp; |
3876 | struct attrlist al; |
3877 | size_t entlen; |
3878 | size_t bytes_left; |
3879 | size_t pad_bytes; |
3880 | ssize_t new_resid; |
3881 | |
3882 | /* |
3883 | * get_direntry returns the current direntry and does not |
3884 | * advance. A move to the next direntry only happens if |
3885 | * direntry_done is called. |
3886 | */ |
3887 | error = get_direntry(ctx, dvp, fvd, eofflagp, dpp: &dp); |
3888 | if (error || (*eofflagp) || !dp) { |
3889 | break; |
3890 | } |
3891 | |
3892 | /* |
3893 | * skip "." and ".." (and a bunch of other invalid conditions.) |
3894 | */ |
3895 | if (!dp->d_reclen || dp->d_ino == 0 || dp->d_namlen == 0 || |
3896 | (dp->d_namlen == 1 && dp->d_name[0] == '.') || |
3897 | (dp->d_namlen == 2 && dp->d_name[0] == '.' && |
3898 | dp->d_name[1] == '.')) { |
3899 | direntry_done(fvd); |
3900 | continue; |
3901 | } |
3902 | |
3903 | /* |
3904 | * try to deal with not-null terminated filenames. |
3905 | */ |
3906 | if (dp->d_name[dp->d_namlen] != '\0') { |
3907 | if (!max_path_name_buf) { |
3908 | max_path_name_buf = zalloc_flags(ZV_NAMEI, Z_WAITOK); |
3909 | } |
3910 | bcopy(src: dp->d_name, dst: max_path_name_buf, n: dp->d_namlen); |
3911 | max_path_name_buf[dp->d_namlen] = '\0'; |
3912 | name_buffer = CAST_USER_ADDR_T(max_path_name_buf); |
3913 | } else { |
3914 | name_buffer = CAST_USER_ADDR_T(&(dp->d_name)); |
3915 | } |
3916 | |
3917 | /* |
3918 | * We have an iocount on the directory already. |
3919 | * |
3920 | * Note that we supply NOCROSSMOUNT to the namei call as we attempt to acquire |
3921 | * a vnode for this particular entry. This is because the native call will |
3922 | * (likely) attempt to emit attributes based on its own metadata in order to avoid |
3923 | * creating vnodes where posssible. If the native call is not going to walk |
3924 | * up the vnode mounted-on chain in order to find the top-most mount point, then we |
3925 | * should not either in this emulated readdir+getattrlist() approach. We |
3926 | * will be responsible for setting DIR_MNTSTATUS_MNTPOINT on that directory that |
3927 | * contains a mount point. |
3928 | */ |
3929 | NDINIT(&nd, LOOKUP, OP_GETATTR, (AUDITVNPATH1 | USEDVP | NOCROSSMOUNT), |
3930 | UIO_SYSSPACE, CAST_USER_ADDR_T(name_buffer), ctx); |
3931 | |
3932 | nd.ni_dvp = dvp; |
3933 | error = namei(ndp: &nd); |
3934 | |
3935 | if (error) { |
3936 | direntry_done(fvd); |
3937 | error = 0; |
3938 | continue; |
3939 | } |
3940 | |
3941 | vp = nd.ni_vp; |
3942 | |
3943 | /* |
3944 | * getattrlist_internal can change the values of the |
3945 | * the required attribute list. Copy the current values |
3946 | * and use that one instead. |
3947 | */ |
3948 | al = *alp; |
3949 | |
3950 | error = getattrlist_internal(ctx, vp, alp: &al, |
3951 | CAST_USER_ADDR_T(kern_attr_buf), bufferSize: kern_attr_buf_siz, |
3952 | options: options | FSOPT_REPORT_FULLSIZE, segflg: UIO_SYSSPACE, |
3953 | CAST_DOWN_EXPLICIT(char *, name_buffer), |
3954 | NOCRED); |
3955 | |
3956 | nameidone(&nd); |
3957 | |
3958 | if (error) { |
3959 | get_error_attributes(vp, alp, options, |
3960 | CAST_USER_ADDR_T(kern_attr_buf), |
3961 | kern_attr_buf_siz, error_attr: error, namebuf: (caddr_t)name_buffer, |
3962 | ctx); |
3963 | error = 0; |
3964 | } |
3965 | |
3966 | /* Done with vnode now */ |
3967 | vnode_put(vp); |
3968 | |
3969 | /* |
3970 | * Because FSOPT_REPORT_FULLSIZE was set, the first 4 bytes |
3971 | * of the buffer returned by getattrlist contains the size |
3972 | * (even if the provided buffer isn't sufficiently big). Use |
3973 | * that to check if we've run out of buffer space. |
3974 | * |
3975 | * resid is a signed type, and the size of the buffer etc |
3976 | * are unsigned types. It is theoretically possible for |
3977 | * resid to be < 0 and in which case we would be assigning |
3978 | * an out of bounds value to bytes_left (which is unsigned) |
3979 | * uiomove takes care to not ever set resid to < 0, so it |
3980 | * is safe to do this here. |
3981 | */ |
3982 | bytes_left = (size_t)((user_size_t)uio_resid(a_uio: auio)); |
3983 | entlen = (size_t)(*((uint32_t *)(kern_attr_buf))); |
3984 | if (!entlen || (entlen > bytes_left)) { |
3985 | break; |
3986 | } |
3987 | |
3988 | /* |
3989 | * Will the pad bytes fit as well ? If they can't be, still use |
3990 | * this entry but this will be the last entry returned. |
3991 | */ |
3992 | pad_bytes = ((entlen + 7) & ~0x07) - entlen; |
3993 | new_resid = 0; |
3994 | if (pad_bytes && (entlen + pad_bytes <= bytes_left)) { |
3995 | /* |
3996 | * While entlen can never be > ATTR_MAX_BUFFER, |
3997 | * (entlen + pad_bytes) can be, handle that and |
3998 | * zero out the pad bytes. N.B. - Only zero |
3999 | * out information in the kernel buffer that is |
4000 | * going to be uiomove'ed out. |
4001 | */ |
4002 | if (entlen + pad_bytes <= kern_attr_buf_siz) { |
4003 | /* This is the normal case. */ |
4004 | bzero(s: kern_attr_buf + entlen, n: pad_bytes); |
4005 | } else { |
4006 | bzero(s: kern_attr_buf + entlen, |
4007 | n: kern_attr_buf_siz - entlen); |
4008 | /* |
4009 | * Pad bytes left over, change the resid value |
4010 | * manually. We only got in here because |
4011 | * bytes_left >= entlen + pad_bytes so |
4012 | * new_resid (which is a signed type) is |
4013 | * always positive. |
4014 | */ |
4015 | new_resid = (ssize_t)(bytes_left - |
4016 | (entlen + pad_bytes)); |
4017 | } |
4018 | entlen += pad_bytes; |
4019 | } |
4020 | *((uint32_t *)kern_attr_buf) = (uint32_t)entlen; |
4021 | error = uiomove(cp: kern_attr_buf, n: min(a: (int)entlen, b: (int)kern_attr_buf_siz), |
4022 | uio: auio); |
4023 | |
4024 | if (error) { |
4025 | break; |
4026 | } |
4027 | |
4028 | if (new_resid) { |
4029 | uio_setresid(a_uio: auio, a_value: (user_ssize_t)new_resid); |
4030 | } |
4031 | |
4032 | /* |
4033 | * At this point, the directory entry has been consumed, proceed |
4034 | * to the next one. |
4035 | */ |
4036 | (*count)++; |
4037 | direntry_done(fvd); |
4038 | } |
4039 | |
4040 | if (max_path_name_buf) { |
4041 | zfree(ZV_NAMEI, max_path_name_buf); |
4042 | } |
4043 | |
4044 | /* |
4045 | * At this point, kern_attr_buf is always allocated |
4046 | */ |
4047 | kfree_data(kern_attr_buf, kern_attr_buf_siz); |
4048 | |
4049 | /* |
4050 | * Always set the offset to the last succesful offset |
4051 | * returned by VNOP_READDIR. |
4052 | */ |
4053 | uio_setoffset(a_uio: auio, a_offset: fvd->fv_eoff); |
4054 | |
4055 | return error; |
4056 | } |
4057 | |
4058 | /* common attributes that only require KAUTH_VNODE_LIST_DIRECTORY */ |
4059 | #define LIST_DIR_ATTRS (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE | \ |
4060 | ATTR_CMN_FILEID | ATTR_CMN_RETURNED_ATTRS | \ |
4061 | ATTR_CMN_ERROR) |
4062 | |
4063 | /* |
4064 | * int getattrlistbulk(int dirfd, struct attrlist *alist, void *attributeBuffer, |
4065 | * size_t bufferSize, uint64_t options) |
4066 | * |
4067 | * Gets directory entries alongwith their attributes in the same way |
4068 | * getattrlist does for a single file system object. |
4069 | * |
4070 | * On non error returns, retval will hold the count of entries returned. |
4071 | */ |
4072 | int |
4073 | getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval) |
4074 | { |
4075 | struct attrlist al; |
4076 | vnode_t dvp = NULLVP; |
4077 | struct fileproc *fp; |
4078 | struct fd_vn_data *fvdata; |
4079 | vfs_context_t ctx; |
4080 | uthread_t ut; |
4081 | enum uio_seg segflg; |
4082 | int count; |
4083 | uio_t auio = NULL; |
4084 | UIO_STACKBUF(uio_buf, 1); |
4085 | kauth_action_t action; |
4086 | int eofflag; |
4087 | uint64_t options; |
4088 | int error; |
4089 | |
4090 | *retval = 0; |
4091 | |
4092 | error = fp_getfvp(p, fd: uap->dirfd, resultfp: &fp, resultvp: &dvp); |
4093 | if (error) { |
4094 | return error; |
4095 | } |
4096 | |
4097 | count = 0; |
4098 | fvdata = NULL; |
4099 | eofflag = 0; |
4100 | ctx = vfs_context_current(); |
4101 | ut = current_uthread(); |
4102 | segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; |
4103 | |
4104 | if ((fp->fp_glob->fg_flag & FREAD) == 0) { |
4105 | /* |
4106 | * AUDIT_ARG(vnpath_withref, dvp, ARG_VNODE1); |
4107 | */ |
4108 | error = EBADF; |
4109 | dvp = NULLVP; |
4110 | goto out; |
4111 | } |
4112 | |
4113 | if ((error = vnode_getwithref(vp: dvp))) { |
4114 | dvp = NULLVP; |
4115 | goto out; |
4116 | } |
4117 | |
4118 | if (uap->options & FSOPT_LIST_SNAPSHOT) { |
4119 | vnode_t snapdvp; |
4120 | |
4121 | if (!vnode_isvroot(vp: dvp)) { |
4122 | error = EINVAL; |
4123 | goto out; |
4124 | } |
4125 | |
4126 | /* switch directory to snapshot directory */ |
4127 | error = vnode_get_snapdir(dvp, &snapdvp, ctx); |
4128 | if (error) { |
4129 | goto out; |
4130 | } |
4131 | vnode_put(vp: dvp); |
4132 | dvp = snapdvp; |
4133 | } |
4134 | |
4135 | if (dvp->v_type != VDIR) { |
4136 | error = ENOTDIR; |
4137 | goto out; |
4138 | } |
4139 | |
4140 | #if CONFIG_MACF |
4141 | error = mac_file_check_change_offset(cred: vfs_context_ucred(ctx), |
4142 | fg: fp->fp_glob); |
4143 | if (error) { |
4144 | goto out; |
4145 | } |
4146 | #endif |
4147 | /* |
4148 | * XXX : Audit Support |
4149 | * AUDIT_ARG(vnpath, dvp, ARG_VNODE1); |
4150 | */ |
4151 | |
4152 | options = uap->options | FSOPT_ATTR_CMN_EXTENDED; |
4153 | |
4154 | if ((error = copyin(CAST_USER_ADDR_T(uap->alist), &al, |
4155 | sizeof(struct attrlist)))) { |
4156 | goto out; |
4157 | } |
4158 | |
4159 | if (al.volattr || |
4160 | ((al.commonattr & ATTR_BULK_REQUIRED) != ATTR_BULK_REQUIRED)) { |
4161 | error = EINVAL; |
4162 | goto out; |
4163 | } |
4164 | |
4165 | #if CONFIG_MACF |
4166 | error = mac_vnode_check_readdir(ctx, vp: dvp); |
4167 | if (error != 0) { |
4168 | goto out; |
4169 | } |
4170 | #endif /* MAC */ |
4171 | |
4172 | /* |
4173 | * Requested attributes that are available in the direntry struct, with the addition |
4174 | * of ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_ERROR, can be let past with just LIST_DIRECTORY. |
4175 | * Any other requested attributes require SEARCH as well. |
4176 | */ |
4177 | action = KAUTH_VNODE_LIST_DIRECTORY; |
4178 | if ((al.commonattr & ~LIST_DIR_ATTRS) || al.fileattr || al.dirattr) { |
4179 | action |= KAUTH_VNODE_SEARCH; |
4180 | } |
4181 | |
4182 | error = vnode_authorize(vp: dvp, NULL, action, ctx); |
4183 | if (error) { |
4184 | goto out; |
4185 | } |
4186 | |
4187 | fvdata = (struct fd_vn_data *)fp->fp_glob->fg_vn_data; |
4188 | if (!fvdata) { |
4189 | panic("Directory expected to have fg_vn_data" ); |
4190 | } |
4191 | |
4192 | FV_LOCK(fvdata); |
4193 | |
4194 | /* |
4195 | * getattrlistbulk(2) maintains its offset in fv_offset. However |
4196 | * if the offset in the file glob is set (or reset) to 0, the directory |
4197 | * traversal needs to be restarted (Any existing state in the |
4198 | * directory buffer is removed as well). |
4199 | */ |
4200 | if (!fp->fp_glob->fg_offset) { |
4201 | fvdata->fv_offset = 0; |
4202 | kfree_data(fvdata->fv_buf, fvdata->fv_bufallocsiz); |
4203 | fvdata->fv_bufsiz = 0; |
4204 | fvdata->fv_bufdone = 0; |
4205 | fvdata->fv_soff = 0; |
4206 | fvdata->fv_eoff = 0; |
4207 | fvdata->fv_eofflag = 0; |
4208 | } |
4209 | |
4210 | auio = uio_createwithbuffer(a_iovcount: 1, a_offset: fvdata->fv_offset, a_spacetype: segflg, a_iodirection: UIO_READ, |
4211 | a_buf_p: &uio_buf[0], a_buffer_size: sizeof(uio_buf)); |
4212 | uio_addiov(a_uio: auio, a_baseaddr: uap->attributeBuffer, a_length: (user_size_t)uap->bufferSize); |
4213 | |
4214 | /* |
4215 | * For "expensive" operations in which the native VNOP implementations |
4216 | * end up having to do just as much (if not more) work than the default |
4217 | * implementation, fall back to the default implementation. |
4218 | * The VNOP helper functions depend on the filesystem providing the |
4219 | * object type, if the caller has not requested ATTR_CMN_OBJTYPE, fall |
4220 | * back to the default implementation. |
4221 | */ |
4222 | if ((al.commonattr & |
4223 | (ATTR_CMN_UUID | ATTR_CMN_GRPUUID | ATTR_CMN_EXTENDED_SECURITY)) || |
4224 | !(al.commonattr & ATTR_CMN_OBJTYPE)) { |
4225 | error = ENOTSUP; |
4226 | } else { |
4227 | struct vnode_attr *va; |
4228 | char *va_name; |
4229 | |
4230 | if (fvdata->fv_eofflag && !fvdata->fv_buf) { |
4231 | /* |
4232 | * If the last successful VNOP_GETATTRLISTBULK or |
4233 | * VNOP_READDIR returned EOF, don't try again. |
4234 | */ |
4235 | eofflag = 1; |
4236 | count = 0; |
4237 | error = 0; |
4238 | } else { |
4239 | eofflag = 0; |
4240 | count = 0; |
4241 | |
4242 | va = kalloc_type(struct vnode_attr, Z_WAITOK); |
4243 | |
4244 | VATTR_INIT(va); |
4245 | va_name = zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO); |
4246 | va->va_name = va_name; |
4247 | |
4248 | (void)getattrlist_setupvattr_all(alp: &al, vap: va, obj_type: VNON, NULL, |
4249 | is_64bit: IS_64BIT_PROCESS(p), use_fork: (uap->options & FSOPT_ATTR_CMN_EXTENDED)); |
4250 | |
4251 | /* |
4252 | * Set UT_KERN_RAGE_VNODES to cause all vnodes created by the |
4253 | * filesystem to be rapidly aged. |
4254 | */ |
4255 | ut->uu_flag |= UT_KERN_RAGE_VNODES; |
4256 | error = VNOP_GETATTRLISTBULK(dvp, &al, va, auio, NULL, |
4257 | options, &eofflag, &count, ctx); |
4258 | ut->uu_flag &= ~UT_KERN_RAGE_VNODES; |
4259 | |
4260 | zfree(ZV_NAMEI, va_name); |
4261 | kfree_type(struct vnode_attr, va); |
4262 | |
4263 | /* |
4264 | * cache state of eofflag. |
4265 | */ |
4266 | if (!error) { |
4267 | fvdata->fv_eofflag = eofflag; |
4268 | } |
4269 | } |
4270 | } |
4271 | |
4272 | /* |
4273 | * If the Filessytem does not natively support getattrlistbulk, |
4274 | * do the default implementation. |
4275 | */ |
4276 | if (error == ENOTSUP) { |
4277 | eofflag = 0; |
4278 | count = 0; |
4279 | |
4280 | ut->uu_flag |= UT_KERN_RAGE_VNODES; |
4281 | error = readdirattr(dvp, fvd: fvdata, auio, alp: &al, options, |
4282 | count: &count, eofflagp: &eofflag, ctx); |
4283 | ut->uu_flag &= ~UT_KERN_RAGE_VNODES; |
4284 | } |
4285 | |
4286 | if (count) { |
4287 | fvdata->fv_offset = uio_offset(a_uio: auio); |
4288 | fp->fp_glob->fg_offset = fvdata->fv_offset; |
4289 | *retval = count; |
4290 | error = 0; |
4291 | } else if (!error && !eofflag) { |
4292 | /* |
4293 | * This just means the buffer was too small to fit even a |
4294 | * single entry. |
4295 | */ |
4296 | error = ERANGE; |
4297 | } |
4298 | |
4299 | FV_UNLOCK(fvdata); |
4300 | out: |
4301 | if (dvp) { |
4302 | vnode_put(vp: dvp); |
4303 | } |
4304 | |
4305 | file_drop(uap->dirfd); |
4306 | |
4307 | return error; |
4308 | } |
4309 | |
4310 | static int |
4311 | attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size) |
4312 | { |
4313 | /* make sure we have enough source data */ |
4314 | if ((*cursor) + size > end) { |
4315 | return EINVAL; |
4316 | } |
4317 | |
4318 | bcopy(src: *cursor, dst: buf, n: size); |
4319 | *cursor += size; |
4320 | return 0; |
4321 | } |
4322 | |
4323 | #define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0); |
4324 | #define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = (typeof(v))_f;} while(0) |
4325 | #define ATTR_UNPACK_TIME(v, is64) \ |
4326 | do { \ |
4327 | if (is64) { \ |
4328 | struct user64_timespec us; \ |
4329 | ATTR_UNPACK(us); \ |
4330 | v.tv_sec = (unsigned long)us.tv_sec; \ |
4331 | v.tv_nsec = (long)us.tv_nsec; \ |
4332 | } else { \ |
4333 | struct user32_timespec us; \ |
4334 | ATTR_UNPACK(us); \ |
4335 | v.tv_sec = us.tv_sec; \ |
4336 | v.tv_nsec = us.tv_nsec; \ |
4337 | } \ |
4338 | } while(0) |
4339 | |
4340 | |
4341 | /* |
4342 | * Write attributes. |
4343 | */ |
4344 | static int |
4345 | setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_context_t ctx) |
4346 | { |
4347 | struct attrlist al; |
4348 | struct vnode_attr va; |
4349 | struct attrreference ar; |
4350 | kauth_action_t action; |
4351 | char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname; |
4352 | int proc_is64, error; |
4353 | kauth_filesec_t rfsec; |
4354 | |
4355 | user_buf = NULL; |
4356 | fndrinfo = NULL; |
4357 | volname = NULL; |
4358 | error = 0; |
4359 | proc_is64 = proc_is64bit(p); |
4360 | VATTR_INIT(&va); |
4361 | |
4362 | if (uap->options & FSOPT_UTIMES_NULL) { |
4363 | va.va_vaflags |= VA_UTIMES_NULL; |
4364 | } |
4365 | |
4366 | /* |
4367 | * Fetch the attribute set and validate. |
4368 | */ |
4369 | if ((error = copyin(uap->alist, (caddr_t) &al, sizeof(al)))) { |
4370 | goto out; |
4371 | } |
4372 | if (al.bitmapcount != ATTR_BIT_MAP_COUNT) { |
4373 | error = EINVAL; |
4374 | goto out; |
4375 | } |
4376 | |
4377 | #if DEVELOPMENT || DEBUG |
4378 | /* |
4379 | * XXX VSWAP: Check for entitlements or special flag here |
4380 | * so we can restrict access appropriately. |
4381 | */ |
4382 | #else /* DEVELOPMENT || DEBUG */ |
4383 | |
4384 | if (vnode_isswap(vp) && (ctx != vfs_context_kernel())) { |
4385 | error = EPERM; |
4386 | goto out; |
4387 | } |
4388 | #endif /* DEVELOPMENT || DEBUG */ |
4389 | |
4390 | VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'" , |
4391 | vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr, |
4392 | (uap->options & FSOPT_NOFOLLOW) ? "no" :"" , vp->v_name); |
4393 | |
4394 | if (al.volattr) { |
4395 | if ((al.volattr & ~ATTR_VOL_SETMASK) || |
4396 | (al.commonattr & ~ATTR_CMN_VOLSETMASK) || |
4397 | al.fileattr || |
4398 | al.forkattr) { |
4399 | error = EINVAL; |
4400 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid volume attributes" ); |
4401 | goto out; |
4402 | } |
4403 | } else { |
4404 | if ((al.commonattr & ~ATTR_CMN_SETMASK) || |
4405 | (al.fileattr & ~ATTR_FILE_SETMASK) || |
4406 | (al.dirattr & ~ATTR_DIR_SETMASK) || |
4407 | (al.forkattr & ~ATTR_FORK_SETMASK)) { |
4408 | error = EINVAL; |
4409 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attempt to set invalid file/folder attributes" ); |
4410 | goto out; |
4411 | } |
4412 | } |
4413 | |
4414 | /* |
4415 | * If the caller's bitmaps indicate that there are no attributes to set, |
4416 | * then exit early. |
4417 | */ |
4418 | if (al.commonattr == 0 && |
4419 | (al.volattr & ~ATTR_VOL_INFO) == 0 && |
4420 | al.dirattr == 0 && |
4421 | al.fileattr == 0 && |
4422 | al.forkattr == 0) { |
4423 | error = 0; |
4424 | goto out; |
4425 | } |
4426 | |
4427 | /* |
4428 | * Make the naive assumption that the caller has supplied a reasonable buffer |
4429 | * size. We could be more careful by pulling in the fixed-size region, checking |
4430 | * the attrref structures, then pulling in the variable section. |
4431 | * We need to reconsider this for handling large ACLs, as they should probably be |
4432 | * brought directly into a buffer. Multiple copyins will make this slower though. |
4433 | * |
4434 | * We could also map the user buffer if it is larger than some sensible mimimum. |
4435 | */ |
4436 | if (uap->bufferSize > ATTR_MAX_BUFFER) { |
4437 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size %d too large" , uap->bufferSize); |
4438 | error = ENOMEM; |
4439 | goto out; |
4440 | } |
4441 | user_buf = kalloc_data(uap->bufferSize, Z_WAITOK); |
4442 | if (user_buf == NULL) { |
4443 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer" , uap->bufferSize); |
4444 | error = ENOMEM; |
4445 | goto out; |
4446 | } |
4447 | if ((error = copyin(uap->attributeBuffer, user_buf, uap->bufferSize)) != 0) { |
4448 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer copyin failed" ); |
4449 | goto out; |
4450 | } |
4451 | VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p" , uap->bufferSize, user_buf); |
4452 | |
4453 | #if CONFIG_MACF |
4454 | error = mac_vnode_check_setattrlist(ctxd: ctx, vp, alist: &al); |
4455 | if (error) { |
4456 | goto out; |
4457 | } |
4458 | #endif /* MAC */ |
4459 | |
4460 | /* |
4461 | * Unpack the argument buffer. |
4462 | */ |
4463 | cursor = user_buf; |
4464 | bufend = cursor + uap->bufferSize; |
4465 | |
4466 | /* common */ |
4467 | if (al.commonattr & ATTR_CMN_SCRIPT) { |
4468 | ATTR_UNPACK(va.va_encoding); |
4469 | VATTR_SET_ACTIVE(&va, va_encoding); |
4470 | } |
4471 | if (al.commonattr & ATTR_CMN_CRTIME) { |
4472 | ATTR_UNPACK_TIME(va.va_create_time, proc_is64); |
4473 | VATTR_SET_ACTIVE(&va, va_create_time); |
4474 | } |
4475 | if (al.commonattr & ATTR_CMN_MODTIME) { |
4476 | ATTR_UNPACK_TIME(va.va_modify_time, proc_is64); |
4477 | VATTR_SET_ACTIVE(&va, va_modify_time); |
4478 | } |
4479 | if (al.commonattr & ATTR_CMN_CHGTIME) { |
4480 | ATTR_UNPACK_TIME(va.va_change_time, proc_is64); |
4481 | al.commonattr &= ~ATTR_CMN_CHGTIME; |
4482 | /*quietly ignore change time; advisory in man page*/ |
4483 | } |
4484 | if (al.commonattr & ATTR_CMN_ACCTIME) { |
4485 | ATTR_UNPACK_TIME(va.va_access_time, proc_is64); |
4486 | VATTR_SET_ACTIVE(&va, va_access_time); |
4487 | } |
4488 | if (al.commonattr & ATTR_CMN_BKUPTIME) { |
4489 | ATTR_UNPACK_TIME(va.va_backup_time, proc_is64); |
4490 | VATTR_SET_ACTIVE(&va, va_backup_time); |
4491 | } |
4492 | if (al.commonattr & ATTR_CMN_FNDRINFO) { |
4493 | if ((cursor + 32) > bufend) { |
4494 | error = EINVAL; |
4495 | VFS_DEBUG(ctx, vp, "ATTRLIST - not enough data supplied for FINDERINFO" ); |
4496 | goto out; |
4497 | } |
4498 | fndrinfo = cursor; |
4499 | cursor += 32; |
4500 | } |
4501 | if (al.commonattr & ATTR_CMN_OWNERID) { |
4502 | ATTR_UNPACK(va.va_uid); |
4503 | VATTR_SET_ACTIVE(&va, va_uid); |
4504 | } |
4505 | if (al.commonattr & ATTR_CMN_GRPID) { |
4506 | ATTR_UNPACK(va.va_gid); |
4507 | VATTR_SET_ACTIVE(&va, va_gid); |
4508 | } |
4509 | if (al.commonattr & ATTR_CMN_ACCESSMASK) { |
4510 | ATTR_UNPACK_CAST(uint32_t, va.va_mode); |
4511 | VATTR_SET_ACTIVE(&va, va_mode); |
4512 | } |
4513 | if (al.commonattr & ATTR_CMN_FLAGS) { |
4514 | ATTR_UNPACK(va.va_flags); |
4515 | VATTR_SET_ACTIVE(&va, va_flags); |
4516 | #if CONFIG_MACF |
4517 | if ((error = mac_vnode_check_setflags(ctx, vp, flags: va.va_flags)) != 0) { |
4518 | goto out; |
4519 | } |
4520 | #endif |
4521 | } |
4522 | if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) { |
4523 | /* |
4524 | * We are (for now) passed a kauth_filesec_t, but all we want from |
4525 | * it is the ACL. |
4526 | */ |
4527 | cp = cursor; |
4528 | ATTR_UNPACK(ar); |
4529 | if (ar.attr_dataoffset < 0) { |
4530 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied" , ar.attr_dataoffset); |
4531 | error = EINVAL; |
4532 | goto out; |
4533 | } |
4534 | |
4535 | cp += ar.attr_dataoffset; |
4536 | rfsec = (kauth_filesec_t)cp; |
4537 | if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) || /* no space for acl */ |
4538 | (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) || /* bad magic */ |
4539 | (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */ |
4540 | ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */ |
4541 | error = EINVAL; |
4542 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied" , ar.attr_length); |
4543 | goto out; |
4544 | } |
4545 | |
4546 | if (rfsec->fsec_entrycount == KAUTH_FILESEC_NOACL) { |
4547 | /* deleting ACL */ |
4548 | VATTR_SET(&va, va_acl, NULL); |
4549 | } else if (rfsec->fsec_entrycount > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */ |
4550 | error = EINVAL; |
4551 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied" ); |
4552 | goto out; |
4553 | } else { |
4554 | VATTR_SET(&va, va_acl, &rfsec->fsec_acl); |
4555 | } |
4556 | } |
4557 | if (al.commonattr & ATTR_CMN_UUID) { |
4558 | ATTR_UNPACK(va.va_uuuid); |
4559 | VATTR_SET_ACTIVE(&va, va_uuuid); |
4560 | } |
4561 | if (al.commonattr & ATTR_CMN_GRPUUID) { |
4562 | ATTR_UNPACK(va.va_guuid); |
4563 | VATTR_SET_ACTIVE(&va, va_guuid); |
4564 | } |
4565 | if (al.commonattr & ATTR_CMN_ADDEDTIME) { |
4566 | ATTR_UNPACK_TIME(va.va_addedtime, proc_is64); |
4567 | VATTR_SET_ACTIVE(&va, va_addedtime); |
4568 | } |
4569 | /* Support setattrlist of data protection class */ |
4570 | if (al.commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) { |
4571 | ATTR_UNPACK(va.va_dataprotect_class); |
4572 | VATTR_SET_ACTIVE(&va, va_dataprotect_class); |
4573 | } |
4574 | |
4575 | /* volume */ |
4576 | if (al.volattr & ATTR_VOL_INFO) { |
4577 | if (al.volattr & ATTR_VOL_NAME) { |
4578 | volname = cursor; |
4579 | ATTR_UNPACK(ar); |
4580 | /* attr_length cannot be 0! */ |
4581 | if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0) || |
4582 | (ar.attr_length > uap->bufferSize) || |
4583 | (uap->bufferSize - ar.attr_length < (unsigned)ar.attr_dataoffset)) { |
4584 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) " , ar.attr_dataoffset); |
4585 | error = EINVAL; |
4586 | goto out; |
4587 | } |
4588 | |
4589 | if (volname >= bufend - ar.attr_dataoffset - ar.attr_length) { |
4590 | error = EINVAL; |
4591 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer" ); |
4592 | goto out; |
4593 | } |
4594 | volname += ar.attr_dataoffset; |
4595 | /* guarantee NUL termination */ |
4596 | volname[ar.attr_length - 1] = 0; |
4597 | } |
4598 | } |
4599 | |
4600 | /* file */ |
4601 | if (al.fileattr & ATTR_FILE_DEVTYPE) { |
4602 | /* XXX does it actually make any sense to change this? */ |
4603 | error = EINVAL; |
4604 | VFS_DEBUG(ctx, vp, "ATTRLIST - XXX device type change not implemented" ); |
4605 | goto out; |
4606 | } |
4607 | |
4608 | /* |
4609 | * Validate and authorize. |
4610 | */ |
4611 | action = 0; |
4612 | if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, vap: &va, actionp: &action, ctx)) != 0)) { |
4613 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d" , error); |
4614 | goto out; |
4615 | } |
4616 | /* |
4617 | * We can auth file Finder Info here. HFS volume FinderInfo is really boot data, |
4618 | * and will be auth'ed by the FS. |
4619 | */ |
4620 | if (fndrinfo != NULL) { |
4621 | if (al.volattr & ATTR_VOL_INFO) { |
4622 | if (vp->v_tag != VT_HFS) { |
4623 | error = EINVAL; |
4624 | goto out; |
4625 | } |
4626 | } else { |
4627 | action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES; |
4628 | } |
4629 | } |
4630 | |
4631 | if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) { |
4632 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed" ); |
4633 | goto out; |
4634 | } |
4635 | |
4636 | /* |
4637 | * When we're setting both the access mask and the finder info, then |
4638 | * check if were about to remove write access for the owner. Since |
4639 | * vnode_setattr and vn_setxattr invoke two separate vnops, we need |
4640 | * to consider their ordering. |
4641 | * |
4642 | * If were about to remove write access for the owner we'll set the |
4643 | * Finder Info here before vnode_setattr. Otherwise we'll set it |
4644 | * after vnode_setattr since it may be adding owner write access. |
4645 | */ |
4646 | if ((fndrinfo != NULL) && !(al.volattr & ATTR_VOL_INFO) && |
4647 | (al.commonattr & ATTR_CMN_ACCESSMASK) && !(va.va_mode & S_IWUSR)) { |
4648 | if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) { |
4649 | goto out; |
4650 | } |
4651 | fndrinfo = NULL; /* it was set here so skip setting below */ |
4652 | } |
4653 | |
4654 | /* |
4655 | * Write the attributes if we have any. |
4656 | */ |
4657 | if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, vap: &va, ctx)) != 0)) { |
4658 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d" , error); |
4659 | goto out; |
4660 | } |
4661 | |
4662 | #if CONFIG_MACF |
4663 | mac_vnode_notify_setattrlist(ctx, vp, alist: &al); |
4664 | if (VATTR_IS_ACTIVE(&va, va_flags)) { |
4665 | mac_vnode_notify_setflags(ctx, vp, flags: va.va_flags); |
4666 | } |
4667 | #endif |
4668 | |
4669 | /* |
4670 | * Write the Finder Info if we have any. |
4671 | */ |
4672 | if (fndrinfo != NULL) { |
4673 | if (al.volattr & ATTR_VOL_INFO) { |
4674 | if (vp->v_tag == VT_HFS) { |
4675 | #define HFS_SET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00005) |
4676 | error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, data: (caddr_t)fndrinfo, fflag: 0, ctx); |
4677 | if (error != 0) { |
4678 | goto out; |
4679 | } |
4680 | } else { |
4681 | /* XXX should never get here */ |
4682 | } |
4683 | } else if ((error = setattrlist_setfinderinfo(vp, fndrinfo, ctx)) != 0) { |
4684 | goto out; |
4685 | } |
4686 | } |
4687 | |
4688 | /* |
4689 | * Set the volume name, if we have one |
4690 | */ |
4691 | if (volname != NULL) { |
4692 | struct vfs_attr vs = {}; |
4693 | |
4694 | VFSATTR_INIT(&vs); |
4695 | |
4696 | vs.f_vol_name = volname; /* References the setattrlist buffer directly */ |
4697 | VFSATTR_WANTED(&vs, f_vol_name); |
4698 | |
4699 | #if CONFIG_MACF |
4700 | error = mac_mount_check_setattr(ctx, mp: vp->v_mount, vfa: &vs); |
4701 | if (error != 0) { |
4702 | goto out; |
4703 | } |
4704 | #endif |
4705 | |
4706 | if ((error = vfs_setattr(mp: vp->v_mount, vfa: &vs, ctx)) != 0) { |
4707 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed" ); |
4708 | goto out; |
4709 | } |
4710 | |
4711 | if (!VFSATTR_ALL_SUPPORTED(&vs)) { |
4712 | error = EINVAL; |
4713 | VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name" ); |
4714 | goto out; |
4715 | } |
4716 | } |
4717 | |
4718 | /* all done and successful */ |
4719 | |
4720 | out: |
4721 | kfree_data(user_buf, uap->bufferSize); |
4722 | VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d" , error); |
4723 | return error; |
4724 | } |
4725 | |
4726 | int |
4727 | setattrlist(proc_t p, struct setattrlist_args *uap, __unused int32_t *retval) |
4728 | { |
4729 | struct vfs_context *ctx; |
4730 | struct nameidata nd; |
4731 | vnode_t vp = NULL; |
4732 | uint32_t nameiflags; |
4733 | int error = 0; |
4734 | |
4735 | ctx = vfs_context_current(); |
4736 | |
4737 | /* |
4738 | * Look up the file. |
4739 | */ |
4740 | nameiflags = AUDITVNPATH1; |
4741 | if ((uap->options & (FSOPT_NOFOLLOW | FSOPT_NOFOLLOW_ANY)) == 0) { |
4742 | nameiflags |= FOLLOW; |
4743 | } |
4744 | #if CONFIG_FILE_LEASES |
4745 | nameiflags |= WANTPARENT; |
4746 | #endif |
4747 | NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx); |
4748 | if (uap->options & FSOPT_NOFOLLOW_ANY) { |
4749 | nd.ni_flag |= NAMEI_NOFOLLOW_ANY; |
4750 | } |
4751 | if ((error = namei(ndp: &nd)) != 0) { |
4752 | goto out; |
4753 | } |
4754 | vp = nd.ni_vp; |
4755 | #if CONFIG_FILE_LEASES |
4756 | vnode_breakdirlease(vp: nd.ni_dvp, false, O_WRONLY); |
4757 | vnode_put(vp: nd.ni_dvp); |
4758 | #endif |
4759 | nameidone(&nd); |
4760 | |
4761 | error = setattrlist_internal(vp, uap, p, ctx); |
4762 | out: |
4763 | if (vp != NULL) { |
4764 | vnode_put(vp); |
4765 | } |
4766 | return error; |
4767 | } |
4768 | |
4769 | int |
4770 | setattrlistat(proc_t p, struct setattrlistat_args *uap, __unused int32_t *retval) |
4771 | { |
4772 | struct setattrlist_args ap; |
4773 | struct vfs_context *ctx; |
4774 | struct nameidata nd; |
4775 | vnode_t vp = NULLVP; |
4776 | uint32_t nameiflags; |
4777 | int error; |
4778 | |
4779 | ctx = vfs_context_current(); |
4780 | |
4781 | AUDIT_ARG(fd, uap->fd); |
4782 | /* |
4783 | * Look up the file. |
4784 | */ |
4785 | nameiflags = AUDITVNPATH1; |
4786 | if (!(uap->options & (FSOPT_NOFOLLOW | FSOPT_NOFOLLOW_ANY))) { |
4787 | nameiflags |= FOLLOW; |
4788 | } |
4789 | #if CONFIG_FILE_LEASES |
4790 | nameiflags |= WANTPARENT; |
4791 | #endif |
4792 | NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx); |
4793 | if (uap->options & FSOPT_NOFOLLOW_ANY) { |
4794 | nd.ni_flag |= NAMEI_NOFOLLOW_ANY; |
4795 | } |
4796 | if ((error = nameiat(ndp: &nd, dirfd: uap->fd)) != 0) { |
4797 | goto out; |
4798 | } |
4799 | vp = nd.ni_vp; |
4800 | #if CONFIG_FILE_LEASES |
4801 | vnode_breakdirlease(vp: nd.ni_dvp, false, O_WRONLY); |
4802 | vnode_put(vp: nd.ni_dvp); |
4803 | #endif |
4804 | nameidone(&nd); |
4805 | |
4806 | ap.path = 0; |
4807 | ap.alist = uap->alist; |
4808 | ap.attributeBuffer = uap->attributeBuffer; |
4809 | ap.bufferSize = uap->bufferSize; |
4810 | ap.options = uap->options; |
4811 | |
4812 | error = setattrlist_internal(vp, uap: &ap, p, ctx); |
4813 | out: |
4814 | if (vp) { |
4815 | vnode_put(vp); |
4816 | } |
4817 | return error; |
4818 | } |
4819 | |
4820 | int |
4821 | fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval) |
4822 | { |
4823 | struct vfs_context *ctx; |
4824 | vnode_t vp = NULL; |
4825 | int error; |
4826 | struct setattrlist_args ap; |
4827 | |
4828 | ctx = vfs_context_current(); |
4829 | |
4830 | if ((error = file_vnode(uap->fd, &vp)) != 0) { |
4831 | return error; |
4832 | } |
4833 | |
4834 | if ((error = vnode_getwithref(vp)) != 0) { |
4835 | file_drop(uap->fd); |
4836 | return error; |
4837 | } |
4838 | |
4839 | #if CONFIG_FILE_LEASES |
4840 | vnode_breakdirlease(vp, true, O_WRONLY); |
4841 | #endif |
4842 | |
4843 | ap.path = 0; |
4844 | ap.alist = uap->alist; |
4845 | ap.attributeBuffer = uap->attributeBuffer; |
4846 | ap.bufferSize = uap->bufferSize; |
4847 | ap.options = uap->options; |
4848 | |
4849 | error = setattrlist_internal(vp, uap: &ap, p, ctx); |
4850 | file_drop(uap->fd); |
4851 | if (vp != NULL) { |
4852 | vnode_put(vp); |
4853 | } |
4854 | |
4855 | return error; |
4856 | } |
4857 | |