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