| 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 |  | 
|---|