1/* 2 * Copyright (c) 2004-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/kernel.h> 32#include <sys/malloc.h> 33#include <sys/ubc.h> 34#include <sys/utfconv.h> 35#include <sys/vnode.h> 36#include <sys/xattr.h> 37#include <sys/fcntl.h> 38#include <sys/vnode_internal.h> 39#include <sys/kauth.h> 40 41#include "hfs.h" 42#include "hfs_cnode.h" 43#include "hfs_mount.h" 44#include "hfs_format.h" 45#include "hfs_endian.h" 46#include "hfs_btreeio.h" 47#include "hfs_fsctl.h" 48 49#include "hfscommon/headers/BTreesInternal.h" 50 51#define HFS_XATTR_VERBOSE 0 52 53#define ATTRIBUTE_FILE_NODE_SIZE 8192 54 55 56/* State information for the listattr_callback callback function. */ 57struct listattr_callback_state { 58 u_int32_t fileID; 59 int result; 60 uio_t uio; 61 size_t size; 62}; 63 64#define HFS_MAXATTRIBUTESIZE (128 * 1024) 65#define HFS_MAXATTRBLKS (32 * 1024) 66 67 68/* HFS Internal Names */ 69#define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity" 70#define XATTR_XATTREXTENTS_NAME "system.xattrextents" 71 72/* Faster version if we already know this is the data fork. */ 73#define RSRC_FORK_EXISTS(CP) \ 74 (((CP)->c_attr.ca_blocks - (CP)->c_datafork->ff_data.cf_blocks) > 0) 75 76static u_int32_t emptyfinfo[8] = {0}; 77 78const char hfs_attrdatafilename[] = "Attribute Data"; 79 80static int listattr_callback(const HFSPlusAttrKey *key, const HFSPlusAttrData *data, 81 struct listattr_callback_state *state); 82 83static int remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator); 84 85static int getnodecount(struct hfsmount *hfsmp, size_t nodesize); 86 87static size_t getmaxinlineattrsize(struct vnode * attrvp); 88 89static int read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents); 90 91static int write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents); 92 93static int alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks); 94 95static void free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents); 96 97static int has_overflow_extents(HFSPlusForkData *forkdata); 98 99static int count_extent_blocks(int maxblks, HFSPlusExtentRecord extents); 100 101#if NAMEDSTREAMS 102/* 103 * Obtain the vnode for a stream. 104 */ 105int 106hfs_vnop_getnamedstream(struct vnop_getnamedstream_args* ap) 107{ 108 vnode_t vp = ap->a_vp; 109 vnode_t *svpp = ap->a_svpp; 110 struct cnode *cp; 111 int error = 0; 112 113 *svpp = NULL; 114 115 /* 116 * We only support the "com.apple.ResourceFork" stream. 117 */ 118 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 119 return (ENOATTR); 120 } 121 cp = VTOC(vp); 122 if ( !S_ISREG(cp->c_mode) ) { 123 return (EPERM); 124 } 125 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) { 126 return (error); 127 } 128 if (!RSRC_FORK_EXISTS(cp) && (ap->a_operation != NS_OPEN)) { 129 hfs_unlock(cp); 130 return (ENOATTR); 131 } 132 error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE); 133 hfs_unlock(cp); 134 135 return (error); 136} 137 138/* 139 * Create a stream. 140 */ 141int 142hfs_vnop_makenamedstream(struct vnop_makenamedstream_args* ap) 143{ 144 vnode_t vp = ap->a_vp; 145 vnode_t *svpp = ap->a_svpp; 146 struct cnode *cp; 147 int error = 0; 148 149 *svpp = NULL; 150 151 /* 152 * We only support the "com.apple.ResourceFork" stream. 153 */ 154 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 155 return (ENOATTR); 156 } 157 cp = VTOC(vp); 158 if ( !S_ISREG(cp->c_mode) ) { 159 return (EPERM); 160 } 161 if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) { 162 return (error); 163 } 164 error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE); 165 hfs_unlock(cp); 166 167 return (error); 168} 169 170/* 171 * Remove a stream. 172 */ 173int 174hfs_vnop_removenamedstream(struct vnop_removenamedstream_args* ap) 175{ 176 vnode_t svp = ap->a_svp; 177 struct cnode *scp; 178 int error = 0; 179 180 /* 181 * We only support the "com.apple.ResourceFork" stream. 182 */ 183 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 184 return (ENOATTR); 185 } 186 scp = VTOC(svp); 187 188 /* Take truncate lock before taking cnode lock. */ 189 hfs_lock_truncate(scp, TRUE); 190 if ((error = hfs_lock(scp, HFS_EXCLUSIVE_LOCK))) { 191 goto out; 192 } 193 if (VTOF(svp)->ff_size != 0) { 194 error = hfs_truncate(svp, 0, IO_NDELAY, 0, ap->a_context); 195 } 196 hfs_unlock(scp); 197out: 198 hfs_unlock_truncate(scp, TRUE); 199 return (error); 200} 201#endif 202 203 204/* 205 * Retrieve the data of an extended attribute. 206 */ 207__private_extern__ 208int 209hfs_vnop_getxattr(struct vnop_getxattr_args *ap) 210/* 211 struct vnop_getxattr_args { 212 struct vnodeop_desc *a_desc; 213 vnode_t a_vp; 214 char * a_name; 215 uio_t a_uio; 216 size_t *a_size; 217 int a_options; 218 vfs_context_t a_context; 219 }; 220*/ 221{ 222 struct vnode *vp = ap->a_vp; 223 struct cnode *cp; 224 struct hfsmount *hfsmp; 225 uio_t uio = ap->a_uio; 226 struct BTreeIterator * iterator = NULL; 227 struct filefork *btfile; 228 FSBufferDescriptor btdata; 229 HFSPlusAttrRecord * recp = NULL; 230 size_t bufsize; 231 u_int16_t datasize; 232 int lockflags; 233 int result; 234 235 cp = VTOC(vp); 236 if (vp == cp->c_vp) { 237 /* Get the Finder Info. */ 238 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 239 u_int8_t finderinfo[32]; 240 bufsize = 32; 241 242 if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) { 243 return (result); 244 } 245 /* Make a copy since we may not export all of it. */ 246 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo)); 247 hfs_unlock(cp); 248 249 /* Don't expose a symlink's private type/creator. */ 250 if (vnode_islnk(vp)) { 251 struct FndrFileInfo *fip; 252 253 fip = (struct FndrFileInfo *)&finderinfo; 254 fip->fdType = 0; 255 fip->fdCreator = 0; 256 } 257 /* If Finder Info is empty then it doesn't exist. */ 258 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) { 259 return (ENOATTR); 260 } 261 if (uio == NULL) { 262 *ap->a_size = bufsize; 263 return (0); 264 } 265 if (uio_resid(uio) < bufsize) 266 return (ERANGE); 267 268 result = uiomove((caddr_t)&finderinfo , bufsize, uio); 269 270 return (result); 271 } 272 /* Read the Resource Fork. */ 273 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 274 struct vnode *rvp = NULL; 275 276 if ( !S_ISREG(cp->c_mode) ) { 277 return (EPERM); 278 } 279 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) { 280 return (result); 281 } 282 if ( !RSRC_FORK_EXISTS(cp)) { 283 hfs_unlock(cp); 284 return (ENOATTR); 285 } 286 hfsmp = VTOHFS(vp); 287 result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE); 288 hfs_unlock(cp); 289 if (result) { 290 return (result); 291 } 292 if (uio == NULL) { 293 *ap->a_size = (size_t)VTOF(rvp)->ff_size; 294 } else { 295 result = VNOP_READ(rvp, uio, 0, ap->a_context); 296 } 297 vnode_put(rvp); 298 return (result); 299 } 300 } 301 hfsmp = VTOHFS(vp); 302 /* 303 * Standard HFS only supports native FinderInfo and Resource Forks. 304 */ 305 if (hfsmp->hfs_flags & HFS_STANDARD) { 306 return (EPERM); 307 } 308 309 if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) { 310 return (result); 311 } 312 /* Bail if we don't have any extended attributes. */ 313 if ((hfsmp->hfs_attribute_vp == NULL) || 314 (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) { 315 result = ENOATTR; 316 goto exit; 317 } 318 btfile = VTOF(hfsmp->hfs_attribute_vp); 319 320 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 321 if (iterator == NULL) { 322 result = ENOMEM; 323 goto exit; 324 } 325 bzero(iterator, sizeof(*iterator)); 326 327 bufsize = sizeof(HFSPlusAttrData) - 2; 328 if (uio) 329 bufsize += uio_resid(uio); 330 bufsize = MAX(bufsize, sizeof(HFSPlusAttrRecord)); 331 MALLOC(recp, HFSPlusAttrRecord *, bufsize, M_TEMP, M_WAITOK); 332 if (recp == NULL) { 333 result = ENOMEM; 334 goto exit; 335 } 336 btdata.bufferAddress = recp; 337 btdata.itemSize = bufsize; 338 btdata.itemCount = 1; 339 340 result = hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 341 if (result) 342 goto exit; 343 344 /* Lookup the attribute. */ 345 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 346 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 347 hfs_systemfile_unlock(hfsmp, lockflags); 348 349 if (result) { 350 if (result == btNotFound) 351 result = ENOATTR; 352 goto exit; 353 } 354 355 switch (recp->recordType) { 356 case kHFSPlusAttrInlineData: 357 /* 358 * Sanity check record size. It's not required to have any 359 * user data, so the minimum size is 2 bytes less that the 360 * size of HFSPlusAttrData (since HFSPlusAttrData struct 361 * has 2 bytes set aside for attribute data). 362 */ 363 if (datasize < (sizeof(HFSPlusAttrData) - 2)) { 364 printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n", 365 VTOC(vp)->c_fileid, ap->a_name, datasize, sizeof(HFSPlusAttrData)); 366 result = ENOATTR; 367 break; 368 } 369 *ap->a_size = recp->attrData.attrSize; 370 if (uio && recp->attrData.attrSize != 0) { 371 if (*ap->a_size > uio_resid(uio)) 372 result = ERANGE; 373 else 374 result = uiomove((caddr_t) &recp->attrData.attrData , recp->attrData.attrSize, uio); 375 } 376 break; 377 378 case kHFSPlusAttrForkData: 379 if (datasize < sizeof(HFSPlusAttrForkData)) { 380 printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n", 381 VTOC(vp)->c_fileid, ap->a_name, datasize, sizeof(HFSPlusAttrForkData)); 382 result = ENOATTR; 383 break; 384 } 385 *ap->a_size = recp->forkData.theFork.logicalSize; 386 if (uio == NULL) { 387 break; 388 } 389 if (*ap->a_size > uio_resid(uio)) { 390 result = ERANGE; 391 break; 392 } 393 /* Process overflow extents if necessary. */ 394 if (has_overflow_extents(&recp->forkData.theFork)) { 395 HFSPlusExtentDescriptor *extentbuf; 396 HFSPlusExtentDescriptor *extentptr; 397 size_t extentbufsize; 398 u_int32_t totalblocks; 399 u_int32_t blkcnt; 400 u_int32_t attrlen; 401 402 totalblocks = recp->forkData.theFork.totalBlocks; 403 /* Ignore bogus block counts. */ 404 if (totalblocks > HFS_MAXATTRBLKS) { 405 result = ERANGE; 406 break; 407 } 408 attrlen = recp->forkData.theFork.logicalSize; 409 410 /* Get a buffer to hold the worst case amount of extents. */ 411 extentbufsize = totalblocks * sizeof(HFSPlusExtentDescriptor); 412 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord)); 413 MALLOC(extentbuf, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK); 414 if (extentbuf == NULL) { 415 result = ENOMEM; 416 break; 417 } 418 bzero(extentbuf, extentbufsize); 419 extentptr = extentbuf; 420 421 /* Grab the first 8 extents. */ 422 bcopy(&recp->forkData.theFork.extents[0], extentptr, sizeof(HFSPlusExtentRecord)); 423 extentptr += kHFSPlusExtentDensity; 424 blkcnt = count_extent_blocks(totalblocks, recp->forkData.theFork.extents); 425 426 /* Now lookup the overflow extents. */ 427 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 428 while (blkcnt < totalblocks) { 429 ((HFSPlusAttrKey *)&iterator->key)->startBlock = blkcnt; 430 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 431 if (result || 432 (recp->recordType != kHFSPlusAttrExtents) || 433 (datasize < sizeof(HFSPlusAttrExtents))) { 434 printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n", 435 ap->a_name, blkcnt, totalblocks); 436 result = ENOATTR; 437 break; /* break from while */ 438 } 439 /* Grab the next 8 extents. */ 440 bcopy(&recp->overflowExtents.extents[0], extentptr, sizeof(HFSPlusExtentRecord)); 441 extentptr += kHFSPlusExtentDensity; 442 blkcnt += count_extent_blocks(totalblocks, recp->overflowExtents.extents); 443 } 444 hfs_systemfile_unlock(hfsmp, lockflags); 445 446 if (blkcnt < totalblocks) { 447 result = ENOATTR; 448 } else { 449 result = read_attr_data(hfsmp, uio, attrlen, extentbuf); 450 } 451 FREE(extentbuf, M_TEMP); 452 453 } else /* No overflow extents. */ { 454 result = read_attr_data(hfsmp, uio, recp->forkData.theFork.logicalSize, recp->forkData.theFork.extents); 455 } 456 break; 457 458 default: 459 result = ENOATTR; 460 break; 461 } 462exit: 463 hfs_unlock(cp); 464 465 if (iterator) { 466 FREE(iterator, M_TEMP); 467 } 468 if (recp) { 469 FREE(recp, M_TEMP); 470 } 471 472 return MacToVFSError(result); 473} 474 475/* 476 * Set the data of an extended attribute. 477 */ 478__private_extern__ 479int 480hfs_vnop_setxattr(struct vnop_setxattr_args *ap) 481/* 482 struct vnop_setxattr_args { 483 struct vnodeop_desc *a_desc; 484 vnode_t a_vp; 485 char * a_name; 486 uio_t a_uio; 487 int a_options; 488 vfs_context_t a_context; 489 }; 490*/ 491{ 492 struct vnode *vp = ap->a_vp; 493 struct cnode *cp = NULL; 494 struct hfsmount *hfsmp; 495 uio_t uio = ap->a_uio; 496 struct BTreeIterator * iterator = NULL; 497 struct filefork *btfile = NULL; 498 size_t attrsize; 499 FSBufferDescriptor btdata; 500 HFSPlusAttrRecord *recp = NULL; 501 HFSPlusExtentDescriptor *extentptr = NULL; 502 HFSPlusAttrRecord attrdata; /* 90 bytes */ 503 void * user_data_ptr = NULL; 504 int started_transaction = 0; 505 int lockflags = 0; 506 int exists; 507 int allocatedblks = 0; 508 int result; 509 510 if (ap->a_name == NULL || ap->a_name[0] == '\0') { 511 return (EINVAL); /* invalid name */ 512 } 513 hfsmp = VTOHFS(vp); 514 if (VNODE_IS_RSRC(vp)) { 515 return (EPERM); 516 } 517 /* Set the Finder Info. */ 518 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 519 u_int8_t finderinfo[32]; 520 struct FndrFileInfo *fip; 521 void * finderinfo_start; 522 u_int16_t fdFlags; 523 524 attrsize = sizeof(VTOC(vp)->c_finderinfo); 525 526 if (uio_resid(uio) != attrsize) { 527 return (ERANGE); 528 } 529 /* Grab the new Finder Info data. */ 530 if ((result = uiomove((caddr_t)&finderinfo , attrsize, uio))) { 531 return (result); 532 } 533 fip = (struct FndrFileInfo *)&finderinfo; 534 535 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) { 536 return (result); 537 } 538 cp = VTOC(vp); 539 540 /* Symlink's don't have an external type/creator. */ 541 if (vnode_islnk(vp)) { 542 /* Skip over type/creator fields. */ 543 finderinfo_start = &cp->c_finderinfo[8]; 544 attrsize -= 8; 545 } else { 546 finderinfo_start = &cp->c_finderinfo[0]; 547 /* 548 * Don't allow the external setting of 549 * file type to kHardLinkFileType. 550 */ 551 if (fip->fdType == SWAP_BE32(kHardLinkFileType)) { 552 hfs_unlock(cp); 553 return (EPERM); 554 } 555 } 556 557 if (bcmp(finderinfo_start, emptyfinfo, attrsize)) { 558 /* attr exists and "create" was specified. */ 559 if (ap->a_options & XATTR_CREATE) { 560 hfs_unlock(cp); 561 return (EEXIST); 562 } 563 } else /* empty */ { 564 /* attr doesn't exists and "replace" was specified. */ 565 if (ap->a_options & XATTR_REPLACE) { 566 hfs_unlock(cp); 567 return (ENOATTR); 568 } 569 } 570 /* Set the cnode's Finder Info. */ 571 if (attrsize == sizeof(cp->c_finderinfo)) 572 bcopy(&finderinfo[0], finderinfo_start, attrsize); 573 else 574 bcopy(&finderinfo[8], finderinfo_start, attrsize); 575 576 /* Updating finderInfo updates change time and modified time */ 577 cp->c_touch_chgtime = TRUE; 578 cp->c_flag |= C_MODIFIED; 579 580 /* 581 * Mirror the invisible bit to the UF_HIDDEN flag. 582 * 583 * The fdFlags for files and frFlags for folders are both 8 bytes 584 * into the userInfo (the first 16 bytes of the Finder Info). They 585 * are both 16-bit fields. 586 */ 587 fdFlags = *((u_int16_t *) &cp->c_finderinfo[8]); 588 if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) 589 cp->c_flags |= UF_HIDDEN; 590 else 591 cp->c_flags &= ~UF_HIDDEN; 592 593 result = hfs_update(vp, FALSE); 594 595 hfs_unlock(cp); 596 return (result); 597 } 598 /* Write the Resource Fork. */ 599 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 600 struct vnode *rvp = NULL; 601 602 if (!vnode_isreg(vp)) { 603 return (EPERM); 604 } 605 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) { 606 return (result); 607 } 608 cp = VTOC(vp); 609 610 if (RSRC_FORK_EXISTS(cp)) { 611 /* attr exists and "create" was specified. */ 612 if (ap->a_options & XATTR_CREATE) { 613 hfs_unlock(cp); 614 return (EEXIST); 615 } 616 } else { 617 /* attr doesn't exists and "replace" was specified. */ 618 if (ap->a_options & XATTR_REPLACE) { 619 hfs_unlock(cp); 620 return (ENOATTR); 621 } 622 } 623 result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE); 624 hfs_unlock(cp); 625 if (result) { 626 return (result); 627 } 628 /* VNOP_WRITE will update timestamps accordingly */ 629 result = VNOP_WRITE(rvp, uio, 0, ap->a_context); 630 vnode_put(rvp); 631 return (result); 632 } 633 /* 634 * Standard HFS only supports native FinderInfo and Resource Forks. 635 */ 636 if (hfsmp->hfs_flags & HFS_STANDARD) { 637 return (EPERM); 638 } 639 attrsize = uio_resid(uio); 640 641 /* Enforce an upper limit. */ 642 if (attrsize > HFS_MAXATTRIBUTESIZE) { 643 return (E2BIG); 644 } 645 646 /* 647 * Attempt to copy the users attr data before taking any locks. 648 */ 649 if (attrsize > 0 && 650 hfsmp->hfs_max_inline_attrsize != 0 && 651 attrsize < hfsmp->hfs_max_inline_attrsize) { 652 MALLOC(user_data_ptr, void *, attrsize, M_TEMP, M_WAITOK); 653 if (user_data_ptr == NULL) { 654 return (ENOMEM); 655 } 656 657 result = uiomove((caddr_t)user_data_ptr, attrsize, uio); 658 if (result) { 659 goto exit; 660 } 661 } 662 663 result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK); 664 if (result) { 665 goto exit; 666 } 667 cp = VTOC(vp); 668 669 /* Start a transaction for our changes. */ 670 if (hfs_start_transaction(hfsmp) != 0) { 671 result = EINVAL; 672 goto exit; 673 } 674 started_transaction = 1; 675 676 /* 677 * Once we started the transaction, nobody can compete 678 * with us, so make sure this file is still there. 679 */ 680 if (cp->c_flag & C_NOEXISTS) { 681 result = ENOENT; 682 goto exit; 683 } 684 685 /* 686 * If there isn't an attributes b-tree then create one. 687 */ 688 if (hfsmp->hfs_attribute_vp == NULL) { 689 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE, 690 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE)); 691 if (result) { 692 goto exit; 693 } 694 } 695 if (hfsmp->hfs_max_inline_attrsize == 0) { 696 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp); 697 } 698 699 /* Take exclusive access to the attributes b-tree. */ 700 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); 701 702 /* Build the b-tree key. */ 703 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 704 if (iterator == NULL) { 705 result = ENOMEM; 706 goto exit; 707 } 708 bzero(iterator, sizeof(*iterator)); 709 result = hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 710 if (result) { 711 goto exit; 712 } 713 714 /* Preflight for replace/create semantics. */ 715 btfile = VTOF(hfsmp->hfs_attribute_vp); 716 btdata.bufferAddress = &attrdata; 717 btdata.itemSize = sizeof(attrdata); 718 btdata.itemCount = 1; 719 exists = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL) == 0; 720 721 /* Replace requires that the attribute already exists. */ 722 if ((ap->a_options & XATTR_REPLACE) && !exists) { 723 result = ENOATTR; 724 goto exit; 725 } 726 /* Create requires that the attribute doesn't exist. */ 727 if ((ap->a_options & XATTR_CREATE) && exists) { 728 result = EEXIST; 729 goto exit; 730 } 731 /* If it won't fit inline then use extent-based attributes. */ 732 if (attrsize > hfsmp->hfs_max_inline_attrsize) { 733 size_t extentbufsize; 734 int blkcnt; 735 int extentblks; 736 u_int32_t *keystartblk; 737 int i; 738 739 /* Check if volume supports extent-based attributes */ 740 if ((hfsmp->hfs_flags & HFS_XATTR_EXTENTS) == 0) { 741 result = E2BIG; 742 goto exit; 743 } 744 745 /* Get some blocks. */ 746 blkcnt = howmany(attrsize, hfsmp->blockSize); 747 extentbufsize = blkcnt * sizeof(HFSPlusExtentDescriptor); 748 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord)); 749 MALLOC(extentptr, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK); 750 if (extentptr == NULL) { 751 result = ENOMEM; 752 goto exit; 753 } 754 bzero(extentptr, extentbufsize); 755 result = alloc_attr_blks(hfsmp, attrsize, extentbufsize, extentptr, &allocatedblks); 756 if (result) { 757 allocatedblks = 0; 758 goto exit; /* no more space */ 759 } 760 /* Copy data into the blocks. */ 761 result = write_attr_data(hfsmp, uio, attrsize, extentptr); 762 if (result) { 763 printf("hfs_setxattr: write_attr_data err (%d) %s:%s\n", 764 result, vnode_name(vp) ? vnode_name(vp) : "", ap->a_name); 765 goto exit; 766 } 767 768 /* Now remove any previous attribute. */ 769 if (exists) { 770 result = remove_attribute_records(hfsmp, iterator); 771 if (result) { 772 printf("hfs_setxattr: remove_attribute_records err (%d) %s:%s\n", 773 result, vnode_name(vp) ? vnode_name(vp) : "", ap->a_name); 774 goto exit; 775 } 776 } 777 778 /* Create attribute fork data record. */ 779 MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK); 780 if (recp == NULL) { 781 result = ENOMEM; 782 goto exit; 783 } 784 btdata.bufferAddress = recp; 785 btdata.itemCount = 1; 786 btdata.itemSize = sizeof(HFSPlusAttrForkData); 787 788 recp->recordType = kHFSPlusAttrForkData; 789 recp->forkData.reserved = 0; 790 recp->forkData.theFork.logicalSize = attrsize; 791 recp->forkData.theFork.clumpSize = 0; 792 recp->forkData.theFork.totalBlocks = blkcnt; 793 bcopy(extentptr, recp->forkData.theFork.extents, sizeof(HFSPlusExtentRecord)); 794 795 (void) hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 796 797 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize); 798 if (result) { 799#if HFS_XATTR_VERBOSE 800 printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n", 801 MacToVFSError(result), vnode_name(vp) ? vnode_name(vp) : "", ap->a_name); 802#endif 803 goto exit; 804 } 805 extentblks = count_extent_blocks(blkcnt, recp->forkData.theFork.extents); 806 blkcnt -= extentblks; 807 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock; 808 i = 0; 809 810 /* Create overflow extents as needed. */ 811 while (blkcnt > 0) { 812 /* Initialize the key and record. */ 813 *keystartblk += (u_int32_t)extentblks; 814 btdata.itemSize = sizeof(HFSPlusAttrExtents); 815 recp->recordType = kHFSPlusAttrExtents; 816 recp->overflowExtents.reserved = 0; 817 818 /* Copy the next set of extents. */ 819 i += kHFSPlusExtentDensity; 820 bcopy(&extentptr[i], recp->overflowExtents.extents, sizeof(HFSPlusExtentRecord)); 821 822 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize); 823 if (result) { 824 printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n", 825 MacToVFSError(result), vnode_name(vp) ? vnode_name(vp) : "", ap->a_name); 826 goto exit; 827 } 828 extentblks = count_extent_blocks(blkcnt, recp->overflowExtents.extents); 829 blkcnt -= extentblks; 830 } 831 } else /* Inline data */ { 832 if (exists) { 833 result = remove_attribute_records(hfsmp, iterator); 834 if (result) { 835 goto exit; 836 } 837 } 838 839 /* Calculate size of record rounded up to multiple of 2 bytes. */ 840 btdata.itemSize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0); 841 MALLOC(recp, HFSPlusAttrRecord *, btdata.itemSize, M_TEMP, M_WAITOK); 842 if (recp == NULL) { 843 result = ENOMEM; 844 goto exit; 845 } 846 recp->recordType = kHFSPlusAttrInlineData; 847 recp->attrData.reserved[0] = 0; 848 recp->attrData.reserved[1] = 0; 849 recp->attrData.attrSize = attrsize; 850 851 /* Copy in the attribute data (if any). */ 852 if (attrsize > 0) { 853 if (user_data_ptr) 854 bcopy(user_data_ptr, &recp->attrData.attrData, attrsize); 855 else 856 result = uiomove((caddr_t)&recp->attrData.attrData, attrsize, uio); 857 if (result) { 858 goto exit; 859 } 860 } 861 862 (void) hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 863 864 btdata.bufferAddress = recp; 865 btdata.itemCount = 1; 866 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize); 867 } 868exit: 869 if (btfile && started_transaction) { 870 (void) BTFlushPath(btfile); 871 } 872 if (lockflags) { 873 hfs_systemfile_unlock(hfsmp, lockflags); 874 } 875 if (result == 0) { 876 cp = VTOC(vp); 877 /* Setting an attribute only updates change time and not 878 * modified time of the file. 879 */ 880 cp->c_touch_chgtime = TRUE; 881 cp->c_attr.ca_recflags |= kHFSHasAttributesMask; 882 if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) { 883 cp->c_attr.ca_recflags |= kHFSHasSecurityMask; 884 } 885 (void) hfs_update(vp, 0); 886 } 887 if (started_transaction) { 888 if (result && allocatedblks) { 889 free_attr_blks(hfsmp, allocatedblks, extentptr); 890 } 891 hfs_end_transaction(hfsmp); 892 } 893 if (cp) { 894 hfs_unlock(cp); 895 } 896 if (result == 0) { 897 HFS_KNOTE(vp, NOTE_ATTRIB); 898 } 899 if (user_data_ptr) { 900 FREE(user_data_ptr, M_TEMP); 901 } 902 if (recp) { 903 FREE(recp, M_TEMP); 904 } 905 if (extentptr) { 906 FREE(extentptr, M_TEMP); 907 } 908 if (iterator) { 909 FREE(iterator, M_TEMP); 910 } 911 return (result == btNotFound ? ENOATTR : MacToVFSError(result)); 912} 913 914/* 915 * Remove an extended attribute. 916 */ 917__private_extern__ 918int 919hfs_vnop_removexattr(struct vnop_removexattr_args *ap) 920/* 921 struct vnop_removexattr_args { 922 struct vnodeop_desc *a_desc; 923 vnode_t a_vp; 924 char * a_name; 925 int a_options; 926 vfs_context_t a_context; 927 }; 928*/ 929{ 930 struct vnode *vp = ap->a_vp; 931 struct cnode *cp = VTOC(vp); 932 struct hfsmount *hfsmp; 933 struct BTreeIterator * iterator = NULL; 934 int lockflags; 935 int result; 936 937 if (ap->a_name == NULL || ap->a_name[0] == '\0') { 938 return (EINVAL); /* invalid name */ 939 } 940 hfsmp = VTOHFS(vp); 941 if (VNODE_IS_RSRC(vp)) { 942 return (EPERM); 943 } 944 945 /* If Resource Fork is non-empty then truncate it. */ 946 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 947 struct vnode *rvp = NULL; 948 949 if ( !vnode_isreg(vp) ) { 950 return (EPERM); 951 } 952 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) { 953 return (result); 954 } 955 if ( !RSRC_FORK_EXISTS(cp)) { 956 hfs_unlock(cp); 957 return (ENOATTR); 958 } 959 result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE); 960 hfs_unlock(cp); 961 if (result) { 962 return (result); 963 } 964 965 hfs_lock_truncate(VTOC(rvp), TRUE); 966 if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK))) { 967 hfs_unlock_truncate(cp, TRUE); 968 vnode_put(rvp); 969 return (result); 970 } 971 972 /* Start a transaction for encapsulating changes in 973 * hfs_truncate() and hfs_update() 974 */ 975 if ((result = hfs_start_transaction(hfsmp))) { 976 hfs_unlock_truncate(cp, TRUE); 977 hfs_unlock(cp); 978 vnode_put(rvp); 979 return (result); 980 } 981 982 result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context); 983 if (result == 0) { 984 cp->c_touch_chgtime = TRUE; 985 cp->c_flag |= C_MODIFIED; 986 result = hfs_update(vp, FALSE); 987 } 988 989 hfs_end_transaction(hfsmp); 990 hfs_unlock_truncate(VTOC(rvp), TRUE); 991 hfs_unlock(VTOC(rvp)); 992 993 vnode_put(rvp); 994 return (result); 995 } 996 /* Clear out the Finder Info. */ 997 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 998 void * finderinfo_start; 999 int finderinfo_size; 1000 1001 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) { 1002 return (result); 1003 } 1004 1005 /* Symlink's don't have an external type/creator. */ 1006 if (vnode_islnk(vp)) { 1007 /* Skip over type/creator fields. */ 1008 finderinfo_start = &cp->c_finderinfo[8]; 1009 finderinfo_size = sizeof(cp->c_finderinfo) - 8; 1010 } else { 1011 finderinfo_start = &cp->c_finderinfo[0]; 1012 finderinfo_size = sizeof(cp->c_finderinfo); 1013 } 1014 if (bcmp(finderinfo_start, emptyfinfo, finderinfo_size) == 0) { 1015 hfs_unlock(cp); 1016 return (ENOATTR); 1017 } 1018 1019 bzero(finderinfo_start, finderinfo_size); 1020 1021 /* Updating finderInfo updates change time and modified time */ 1022 cp->c_touch_chgtime = TRUE; 1023 cp->c_flag |= C_MODIFIED; 1024 hfs_update(vp, FALSE); 1025 1026 hfs_unlock(cp); 1027 1028 return (0); 1029 } 1030 /* 1031 * Standard HFS only supports native FinderInfo and Resource Forks. 1032 */ 1033 if (hfsmp->hfs_flags & HFS_STANDARD) { 1034 return (EPERM); 1035 } 1036 if (hfsmp->hfs_attribute_vp == NULL) { 1037 return (ENOATTR); 1038 } 1039 1040 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1041 if (iterator == NULL) { 1042 return (ENOMEM); 1043 } 1044 bzero(iterator, sizeof(*iterator)); 1045 1046 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) { 1047 return (result); 1048 } 1049 1050 result = hfs_buildattrkey(cp->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 1051 if (result) { 1052 goto exit; 1053 } 1054 1055 if (hfs_start_transaction(hfsmp) != 0) { 1056 result = EINVAL; 1057 goto exit; 1058 } 1059 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK); 1060 1061 result = remove_attribute_records(hfsmp, iterator); 1062 1063 hfs_systemfile_unlock(hfsmp, lockflags); 1064 1065 if (result == 0) { 1066 cp->c_touch_chgtime = TRUE; 1067 1068 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 1069 1070 /* If no more attributes exist, clear attribute bit */ 1071 result = file_attribute_exist(hfsmp, cp->c_fileid); 1072 if (result == 0) { 1073 cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask; 1074 } 1075 if (result == EEXIST) { 1076 result = 0; 1077 } 1078 1079 hfs_systemfile_unlock(hfsmp, lockflags); 1080 1081 /* If ACL was removed, clear security bit */ 1082 if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) { 1083 cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask; 1084 } 1085 (void) hfs_update(vp, 0); 1086 } 1087 1088 hfs_end_transaction(hfsmp); 1089exit: 1090 hfs_unlock(cp); 1091 if (result == 0) { 1092 HFS_KNOTE(vp, NOTE_ATTRIB); 1093 } 1094 FREE(iterator, M_TEMP); 1095 return MacToVFSError(result); 1096} 1097 1098/* Check if any attribute record exist for given fileID. This function 1099 * is called by hfs_vnop_removexattr to determine if it should clear the 1100 * attribute bit in the catalog record or not. 1101 * 1102 * Note - you must acquire a shared lock on the attribute btree before 1103 * calling this function. 1104 * 1105 * Output: 1106 * EEXIST - If attribute record was found 1107 * 0 - Attribute was not found 1108 * (other) - Other error (such as EIO) 1109 */ 1110int 1111file_attribute_exist(struct hfsmount *hfsmp, uint32_t fileID) 1112{ 1113 HFSPlusAttrKey *key; 1114 struct BTreeIterator * iterator = NULL; 1115 struct filefork *btfile; 1116 int result = 0; 1117 1118 // if there's no attribute b-tree we sure as heck 1119 // can't have any attributes! 1120 if (hfsmp->hfs_attribute_vp == NULL) { 1121 return false; 1122 } 1123 1124 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1125 if (iterator == NULL) { 1126 result = ENOMEM; 1127 goto out; 1128 } 1129 bzero(iterator, sizeof(*iterator)); 1130 key = (HFSPlusAttrKey *)&iterator->key; 1131 1132 result = hfs_buildattrkey(fileID, NULL, key); 1133 if (result) { 1134 goto out; 1135 } 1136 1137 btfile = VTOF(hfsmp->hfs_attribute_vp); 1138 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL); 1139 if (result && (result != btNotFound)) { 1140 goto out; 1141 } 1142 1143 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL); 1144 /* If no next record was found or fileID for next record did not match, 1145 * no more attributes exist for this fileID 1146 */ 1147 if ((result && (result == btNotFound)) || (key->fileID != fileID)) { 1148 result = 0; 1149 } else { 1150 result = EEXIST; 1151 } 1152 1153out: 1154 if (iterator) { 1155 FREE(iterator, M_TEMP); 1156 } 1157 return result; 1158} 1159 1160 1161/* 1162 * Remove all the records for a given attribute. 1163 * 1164 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr. 1165 * - A transaction must have been started. 1166 * - The Attribute b-tree file must be locked exclusive. 1167 * - The Allocation Bitmap file must be locked exclusive. 1168 * - The iterator key must be initialized. 1169 */ 1170static int 1171remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator) 1172{ 1173 struct filefork *btfile; 1174 FSBufferDescriptor btdata; 1175 HFSPlusAttrRecord attrdata; /* 90 bytes */ 1176 u_int16_t datasize; 1177 int result; 1178 1179 btfile = VTOF(hfsmp->hfs_attribute_vp); 1180 1181 btdata.bufferAddress = &attrdata; 1182 btdata.itemSize = sizeof(attrdata); 1183 btdata.itemCount = 1; 1184 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 1185 if (result) { 1186 goto exit; /* no records. */ 1187 } 1188 /* 1189 * Free the blocks from extent based attributes. 1190 * 1191 * Note that the block references (btree records) are removed 1192 * before releasing the blocks in the allocation bitmap. 1193 */ 1194 if (attrdata.recordType == kHFSPlusAttrForkData) { 1195 int totalblks; 1196 int extentblks; 1197 u_int32_t *keystartblk; 1198 1199#if HFS_XATTR_VERBOSE 1200 if (datasize < sizeof(HFSPlusAttrForkData)) { 1201 printf("remove_attribute_records: bad record size %d (expecting %d)\n", datasize, sizeof(HFSPlusAttrForkData)); 1202 } 1203#endif 1204 totalblks = attrdata.forkData.theFork.totalBlocks; 1205 1206 /* Process the first 8 extents. */ 1207 extentblks = count_extent_blocks(totalblks, attrdata.forkData.theFork.extents); 1208 if (extentblks > totalblks) 1209 panic("remove_attribute_records: corruption..."); 1210 if (BTDeleteRecord(btfile, iterator) == 0) { 1211 free_attr_blks(hfsmp, extentblks, attrdata.forkData.theFork.extents); 1212 } 1213 totalblks -= extentblks; 1214 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock; 1215 1216 /* Process any overflow extents. */ 1217 while (totalblks) { 1218 *keystartblk += (u_int32_t)extentblks; 1219 1220 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 1221 if (result || 1222 (attrdata.recordType != kHFSPlusAttrExtents) || 1223 (datasize < sizeof(HFSPlusAttrExtents))) { 1224 printf("remove_attribute_records: BTSearchRecord %d (%d), totalblks %d\n", 1225 MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks); 1226 result = ENOATTR; 1227 break; /* break from while */ 1228 } 1229 /* Process the next 8 extents. */ 1230 extentblks = count_extent_blocks(totalblks, attrdata.overflowExtents.extents); 1231 if (extentblks > totalblks) 1232 panic("remove_attribute_records: corruption..."); 1233 if (BTDeleteRecord(btfile, iterator) == 0) { 1234 free_attr_blks(hfsmp, extentblks, attrdata.overflowExtents.extents); 1235 } 1236 totalblks -= extentblks; 1237 } 1238 } else { 1239 result = BTDeleteRecord(btfile, iterator); 1240 } 1241 (void) BTFlushPath(btfile); 1242exit: 1243 return (result == btNotFound ? ENOATTR : MacToVFSError(result)); 1244} 1245 1246 1247/* 1248 * Retrieve the list of extended attribute names. 1249 */ 1250__private_extern__ 1251int 1252hfs_vnop_listxattr(struct vnop_listxattr_args *ap) 1253/* 1254 struct vnop_listxattr_args { 1255 struct vnodeop_desc *a_desc; 1256 vnode_t a_vp; 1257 uio_t a_uio; 1258 size_t *a_size; 1259 int a_options; 1260 vfs_context_t a_context; 1261*/ 1262{ 1263 struct vnode *vp = ap->a_vp; 1264 struct cnode *cp = VTOC(vp); 1265 struct hfsmount *hfsmp; 1266 uio_t uio = ap->a_uio; 1267 struct BTreeIterator * iterator = NULL; 1268 struct filefork *btfile; 1269 struct listattr_callback_state state; 1270 void * finderinfo_start; 1271 int finderinfo_size; 1272 user_addr_t user_start = 0; 1273 user_size_t user_len = 0; 1274 int lockflags; 1275 int result; 1276 1277 if (VNODE_IS_RSRC(vp)) { 1278 return (EPERM); 1279 } 1280 hfsmp = VTOHFS(vp); 1281 *ap->a_size = 0; 1282 1283 if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) { 1284 return (result); 1285 } 1286 1287 /* Don't expose a symlink's private type/creator. */ 1288 if (vnode_islnk(vp)) { 1289 /* Skip over type/creator fields. */ 1290 finderinfo_start = &cp->c_finderinfo[8]; 1291 finderinfo_size = sizeof(cp->c_finderinfo) - 8; 1292 } else { 1293 finderinfo_start = &cp->c_finderinfo[0]; 1294 finderinfo_size = sizeof(cp->c_finderinfo); 1295 } 1296 /* If Finder Info is non-empty then export it's name. */ 1297 if (bcmp(finderinfo_start, emptyfinfo, finderinfo_size) != 0) { 1298 if (uio == NULL) { 1299 *ap->a_size += sizeof(XATTR_FINDERINFO_NAME); 1300 } else if (uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) { 1301 result = ERANGE; 1302 goto exit; 1303 } else { 1304 result = uiomove(XATTR_FINDERINFO_NAME, 1305 sizeof(XATTR_FINDERINFO_NAME), uio); 1306 if (result) 1307 goto exit; 1308 } 1309 } 1310 /* If Resource Fork is non-empty then export it's name. */ 1311 if (S_ISREG(cp->c_mode) && RSRC_FORK_EXISTS(cp)) { 1312 if (uio == NULL) { 1313 *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME); 1314 } else if (uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) { 1315 result = ERANGE; 1316 goto exit; 1317 } else { 1318 result = uiomove(XATTR_RESOURCEFORK_NAME, 1319 sizeof(XATTR_RESOURCEFORK_NAME), uio); 1320 if (result) 1321 goto exit; 1322 } 1323 } 1324 /* 1325 * Standard HFS only supports native FinderInfo and Resource Forks. 1326 * Return at this point. 1327 */ 1328 if (hfsmp->hfs_flags & HFS_STANDARD) { 1329 result = 0; 1330 goto exit; 1331 } 1332 /* Bail if we don't have any extended attributes. */ 1333 if ((hfsmp->hfs_attribute_vp == NULL) || 1334 (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) { 1335 result = 0; 1336 goto exit; 1337 } 1338 btfile = VTOF(hfsmp->hfs_attribute_vp); 1339 1340 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1341 if (iterator == NULL) { 1342 result = ENOMEM; 1343 goto exit; 1344 } 1345 bzero(iterator, sizeof(*iterator)); 1346 result = hfs_buildattrkey(cp->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key); 1347 if (result) 1348 goto exit; 1349 1350 /* 1351 * Lock the user's buffer here so that we won't fault on 1352 * it in uiomove while holding the attributes b-tree lock. 1353 */ 1354 if (uio && uio_isuserspace(uio)) { 1355 user_start = uio_curriovbase(uio); 1356 user_len = uio_curriovlen(uio); 1357 1358 if ((result = vslock(user_start, user_len)) != 0) { 1359 user_start = 0; 1360 goto exit; 1361 } 1362 } 1363 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 1364 1365 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL); 1366 if (result && result != btNotFound) { 1367 hfs_systemfile_unlock(hfsmp, lockflags); 1368 goto exit; 1369 } 1370 1371 state.fileID = cp->c_fileid; 1372 state.result = 0; 1373 state.uio = uio; 1374 state.size = 0; 1375 1376 /* 1377 * Process entries starting just after iterator->key. 1378 */ 1379 result = BTIterateRecords(btfile, kBTreeNextRecord, iterator, 1380 (IterateCallBackProcPtr)listattr_callback, &state); 1381 hfs_systemfile_unlock(hfsmp, lockflags); 1382 if (uio == NULL) { 1383 *ap->a_size += state.size; 1384 } 1385 1386 if (state.result || result == btNotFound) 1387 result = state.result; 1388 1389exit: 1390 if (user_start) { 1391 vsunlock(user_start, user_len, TRUE); 1392 } 1393 FREE(iterator, M_TEMP); 1394 1395 hfs_unlock(cp); 1396 1397 return MacToVFSError(result); 1398} 1399 1400 1401/* 1402 * Callback - called for each attribute 1403 */ 1404static int 1405listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state) 1406{ 1407 char attrname[XATTR_MAXNAMELEN + 1]; 1408 size_t bytecount; 1409 int result; 1410 1411 if (state->fileID != key->fileID) { 1412 state->result = 0; 1413 return (0); /* stop */ 1414 } 1415 /* 1416 * Skip over non-primary keys 1417 */ 1418 if (key->startBlock != 0) { 1419 return (1); /* continue */ 1420 } 1421 1422 /* Convert the attribute name into UTF-8. */ 1423 result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar), 1424 (u_int8_t *)attrname, &bytecount, sizeof(attrname), '/', 0); 1425 if (result) { 1426 state->result = result; 1427 return (0); /* stop */ 1428 } 1429 bytecount++; /* account for null termination char */ 1430 1431 if (xattr_protected(attrname)) 1432 return (1); /* continue */ 1433 1434 if (state->uio == NULL) { 1435 state->size += bytecount; 1436 } else { 1437 if (bytecount > uio_resid(state->uio)) { 1438 state->result = ERANGE; 1439 return (0); /* stop */ 1440 } 1441 result = uiomove((caddr_t) attrname, bytecount, state->uio); 1442 if (result) { 1443 state->result = result; 1444 return (0); /* stop */ 1445 } 1446 } 1447 return (1); /* continue */ 1448} 1449 1450/* 1451 * Remove all the attributes from a cnode. 1452 * 1453 * This function creates/ends its own transaction so that each 1454 * attribute is deleted in its own transaction (to avoid having 1455 * a transaction grow too large). 1456 * 1457 * This function takes the necessary locks on the attribute 1458 * b-tree file and the allocation (bitmap) file. 1459 */ 1460__private_extern__ 1461int 1462hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid) 1463{ 1464 BTreeIterator *iterator; 1465 HFSPlusAttrKey *key; 1466 struct filefork *btfile; 1467 int result, lockflags; 1468 1469 if (hfsmp->hfs_attribute_vp == NULL) { 1470 return (0); 1471 } 1472 btfile = VTOF(hfsmp->hfs_attribute_vp); 1473 1474 MALLOC(iterator, BTreeIterator *, sizeof(BTreeIterator), M_TEMP, M_WAITOK); 1475 bzero(iterator, sizeof(BTreeIterator)); 1476 key = (HFSPlusAttrKey *)&iterator->key; 1477 1478 /* Loop until there are no more attributes for this file id */ 1479 for(;;) { 1480 if (hfs_start_transaction(hfsmp) != 0) { 1481 result = EINVAL; 1482 goto exit; 1483 } 1484 1485 /* Lock the attribute b-tree and the allocation (bitmap) files */ 1486 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK); 1487 1488 /* 1489 * Go to first possible attribute key/record pair 1490 */ 1491 (void) hfs_buildattrkey(fileid, NULL, key); 1492 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL); 1493 if (result || key->fileID != fileid) { 1494 hfs_systemfile_unlock(hfsmp, lockflags); 1495 hfs_end_transaction(hfsmp); 1496 goto exit; 1497 } 1498 result = remove_attribute_records(hfsmp, iterator); 1499 1500#if HFS_XATTR_VERBOSE 1501 if (result) { 1502 printf("hfs_removeallattr: unexpected err %d\n", result); 1503 } 1504#endif 1505 hfs_systemfile_unlock(hfsmp, lockflags); 1506 hfs_end_transaction(hfsmp); 1507 if (result) 1508 break; 1509 } 1510exit: 1511 FREE(iterator, M_TEMP); 1512 return (result == btNotFound ? 0: MacToVFSError(result)); 1513} 1514 1515__private_extern__ 1516void 1517hfs_xattr_init(struct hfsmount * hfsmp) 1518{ 1519 /* 1520 * If there isn't an attributes b-tree then create one. 1521 */ 1522 if (!(hfsmp->hfs_flags & HFS_STANDARD) && 1523 (hfsmp->hfs_attribute_vp == NULL) && 1524 !(hfsmp->hfs_flags & HFS_READ_ONLY)) { 1525 (void) hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE, 1526 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE)); 1527 } 1528 if (hfsmp->hfs_attribute_vp) 1529 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp); 1530} 1531 1532/* 1533 * Enable/Disable volume attributes stored as EA for root file system. 1534 * Supported attributes are - 1535 * 1. ACLs 1536 * 2. Extent-based Extended Attributes 1537 */ 1538__private_extern__ 1539int 1540hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state) 1541{ 1542 struct BTreeIterator * iterator = NULL; 1543 struct filefork *btfile; 1544 int lockflags; 1545 int result; 1546 1547 if (hfsmp->hfs_flags & HFS_STANDARD) { 1548 return (ENOTSUP); 1549 } 1550 1551 /* 1552 * If there isn't an attributes b-tree then create one. 1553 */ 1554 if (hfsmp->hfs_attribute_vp == NULL) { 1555 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE, 1556 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE)); 1557 if (result) { 1558 return (result); 1559 } 1560 } 1561 1562 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1563 if (iterator == NULL) { 1564 return (ENOMEM); 1565 } 1566 bzero(iterator, sizeof(*iterator)); 1567 1568 /* 1569 * Build a b-tree key. 1570 * We use the root's parent id (1) to hold this volume attribute. 1571 */ 1572 if (xattrtype == HFS_SETACLSTATE) { 1573 /* ACL */ 1574 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME, 1575 (HFSPlusAttrKey *)&iterator->key); 1576 } else if (xattrtype == HFS_SET_XATTREXTENTS_STATE) { 1577 /* Extent-based extended attributes */ 1578 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME, 1579 (HFSPlusAttrKey *)&iterator->key); 1580 } else { 1581 result = EINVAL; 1582 goto exit; 1583 } 1584 1585 /* Start a transaction for our changes. */ 1586 if (hfs_start_transaction(hfsmp) != 0) { 1587 result = EINVAL; 1588 goto exit; 1589 } 1590 btfile = VTOF(hfsmp->hfs_attribute_vp); 1591 1592 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); 1593 1594 if (state == 0) { 1595 /* Remove the attribute. */ 1596 result = BTDeleteRecord(btfile, iterator); 1597 if (result == btNotFound) 1598 result = 0; 1599 } else { 1600 FSBufferDescriptor btdata; 1601 HFSPlusAttrData attrdata; 1602 u_int16_t datasize; 1603 1604 datasize = sizeof(attrdata); 1605 btdata.bufferAddress = &attrdata; 1606 btdata.itemSize = datasize; 1607 btdata.itemCount = 1; 1608 attrdata.recordType = kHFSPlusAttrInlineData; 1609 attrdata.reserved[0] = 0; 1610 attrdata.reserved[1] = 0; 1611 attrdata.attrSize = 2; 1612 attrdata.attrData[0] = 0; 1613 attrdata.attrData[1] = 0; 1614 1615 /* Insert the attribute. */ 1616 result = BTInsertRecord(btfile, iterator, &btdata, datasize); 1617 if (result == btExists) 1618 result = 0; 1619 } 1620 (void) BTFlushPath(btfile); 1621 1622 hfs_systemfile_unlock(hfsmp, lockflags); 1623 1624 /* Finish the transaction of our changes. */ 1625 hfs_end_transaction(hfsmp); 1626exit: 1627 if (iterator) { 1628 FREE(iterator, M_TEMP); 1629 } 1630 if (result == 0) { 1631 if (xattrtype == HFS_SETACLSTATE) { 1632 if (state == 0) { 1633 vfs_clearextendedsecurity(HFSTOVFS(hfsmp)); 1634 } else { 1635 vfs_setextendedsecurity(HFSTOVFS(hfsmp)); 1636 } 1637 } else { 1638 /* HFS_SET_XATTREXTENTS_STATE */ 1639 HFS_MOUNT_LOCK(hfsmp, TRUE); 1640 if (state == 0) { 1641 hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS; 1642 } else { 1643 hfsmp->hfs_flags |= HFS_XATTR_EXTENTS; 1644 } 1645 HFS_MOUNT_UNLOCK(hfsmp, TRUE); 1646 } 1647 } 1648 1649 return MacToVFSError(result); 1650} 1651 1652 1653 /* 1654 * Check for volume attributes stored as EA for root file system. 1655 * Supported attributes are - 1656 * 1. ACLs 1657 * 2. Extent-based Extended Attributes 1658 */ 1659__private_extern__ 1660void 1661hfs_check_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype) 1662{ 1663 struct BTreeIterator * iterator; 1664 struct filefork *btfile; 1665 int lockflags; 1666 int result; 1667 1668 if (hfsmp->hfs_flags & HFS_STANDARD || 1669 hfsmp->hfs_attribute_vp == NULL) { 1670 return; 1671 } 1672 1673 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1674 if (iterator == NULL) { 1675 return; 1676 } 1677 bzero(iterator, sizeof(*iterator)); 1678 1679 /* 1680 * Build a b-tree key. 1681 * We use the root's parent id (1) to hold this volume attribute. 1682 */ 1683 if (xattrtype == HFS_SETACLSTATE) { 1684 /* ACLs */ 1685 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME, 1686 (HFSPlusAttrKey *)&iterator->key); 1687 } else { 1688 /* Extent-based extended attributes */ 1689 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME, 1690 (HFSPlusAttrKey *)&iterator->key); 1691 } 1692 btfile = VTOF(hfsmp->hfs_attribute_vp); 1693 1694 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); 1695 1696 /* Check for our attribute. */ 1697 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL); 1698 1699 hfs_systemfile_unlock(hfsmp, lockflags); 1700 FREE(iterator, M_TEMP); 1701 1702 if (result == 0) { 1703 if (xattrtype == HFS_SETACLSTATE) { 1704 vfs_setextendedsecurity(HFSTOVFS(hfsmp)); 1705 } else { 1706 HFS_MOUNT_LOCK(hfsmp, TRUE); 1707 hfsmp->hfs_flags |= HFS_XATTR_EXTENTS; 1708 HFS_MOUNT_UNLOCK(hfsmp, TRUE); 1709 } 1710 } 1711} 1712 1713 1714/* 1715 * hfs_attrkeycompare - compare two attribute b-tree keys. 1716 * 1717 * The name portion of the key is compared using a 16-bit binary comparison. 1718 * This is called from the b-tree code. 1719 */ 1720__private_extern__ 1721int 1722hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey) 1723{ 1724 u_int32_t searchFileID, trialFileID; 1725 int result; 1726 1727 searchFileID = searchKey->fileID; 1728 trialFileID = trialKey->fileID; 1729 result = 0; 1730 1731 if (searchFileID > trialFileID) { 1732 ++result; 1733 } else if (searchFileID < trialFileID) { 1734 --result; 1735 } else { 1736 u_int16_t * str1 = &searchKey->attrName[0]; 1737 u_int16_t * str2 = &trialKey->attrName[0]; 1738 int length1 = searchKey->attrNameLen; 1739 int length2 = trialKey->attrNameLen; 1740 u_int16_t c1, c2; 1741 int length; 1742 1743 if (length1 < length2) { 1744 length = length1; 1745 --result; 1746 } else if (length1 > length2) { 1747 length = length2; 1748 ++result; 1749 } else { 1750 length = length1; 1751 } 1752 1753 while (length--) { 1754 c1 = *(str1++); 1755 c2 = *(str2++); 1756 1757 if (c1 > c2) { 1758 result = 1; 1759 break; 1760 } 1761 if (c1 < c2) { 1762 result = -1; 1763 break; 1764 } 1765 } 1766 if (result) 1767 return (result); 1768 /* 1769 * Names are equal; compare startBlock 1770 */ 1771 if (searchKey->startBlock == trialKey->startBlock) 1772 return (0); 1773 else 1774 return (searchKey->startBlock < trialKey->startBlock ? -1 : 1); 1775 } 1776 1777 return result; 1778} 1779 1780 1781/* 1782 * hfs_buildattrkey - build an Attribute b-tree key 1783 */ 1784__private_extern__ 1785int 1786hfs_buildattrkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key) 1787{ 1788 int result = 0; 1789 size_t unicodeBytes = 0; 1790 1791 if (attrname != NULL) { 1792 /* 1793 * Convert filename from UTF-8 into Unicode 1794 */ 1795 result = utf8_decodestr((const u_int8_t *)attrname, strlen(attrname), key->attrName, 1796 &unicodeBytes, sizeof(key->attrName), 0, 0); 1797 if (result) { 1798 if (result != ENAMETOOLONG) 1799 result = EINVAL; /* name has invalid characters */ 1800 return (result); 1801 } 1802 key->attrNameLen = unicodeBytes / sizeof(UniChar); 1803 key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes; 1804 } else { 1805 key->attrNameLen = 0; 1806 key->keyLength = kHFSPlusAttrKeyMinimumLength; 1807 } 1808 key->pad = 0; 1809 key->fileID = fileID; 1810 key->startBlock = 0; 1811 1812 return (0); 1813 } 1814 1815/* 1816 * getnodecount - calculate starting node count for attributes b-tree. 1817 */ 1818static int 1819getnodecount(struct hfsmount *hfsmp, size_t nodesize) 1820{ 1821 u_int64_t freebytes; 1822 u_int64_t calcbytes; 1823 1824 /* 1825 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB. 1826 * 10.5: Attempt to be as big as the catalog clump size. 1827 * 1828 * Use no more than 10 % of the remaining free space. 1829 */ 1830 freebytes = (u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize; 1831 1832 calcbytes = MIN(hfsmp->hfs_catalog_cp->c_datafork->ff_size / 5, 20 * 1024 * 1024); 1833 1834 calcbytes = MAX(calcbytes, hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize); 1835 1836 calcbytes = MIN(calcbytes, freebytes / 10); 1837 1838 return (MAX(2, (int)(calcbytes / nodesize))); 1839} 1840 1841 1842/* 1843 * getmaxinlineattrsize - calculate maximum inline attribute size. 1844 * 1845 * This yields 3,802 bytes for an 8K node size. 1846 */ 1847static size_t 1848getmaxinlineattrsize(struct vnode * attrvp) 1849{ 1850 struct BTreeInfoRec btinfo; 1851 size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE; 1852 size_t maxsize; 1853 1854 if (attrvp != NULL) { 1855 (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK); 1856 if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0) 1857 nodesize = btinfo.nodeSize; 1858 hfs_unlock(VTOC(attrvp)); 1859 } 1860 maxsize = nodesize; 1861 maxsize -= sizeof(BTNodeDescriptor); /* minus node descriptor */ 1862 maxsize -= 3 * sizeof(u_int16_t); /* minus 3 index slots */ 1863 maxsize /= 2; /* 2 key/rec pairs minumum */ 1864 maxsize -= sizeof(HFSPlusAttrKey); /* minus maximum key size */ 1865 maxsize -= sizeof(HFSPlusAttrData) - 2; /* minus data header */ 1866 maxsize &= 0xFFFFFFFE; /* multiple of 2 bytes */ 1867 1868 return (maxsize); 1869} 1870 1871/* 1872 * Get a referenced vnode for attribute data I/O. 1873 */ 1874static int 1875get_attr_data_vnode(struct hfsmount *hfsmp, vnode_t *vpp) 1876{ 1877 vnode_t vp; 1878 int result = 0; 1879 1880 vp = hfsmp->hfs_attrdata_vp; 1881 if (vp == NULLVP) { 1882 struct cat_desc cat_desc; 1883 struct cat_attr cat_attr; 1884 struct cat_fork cat_fork; 1885 1886 /* We don't tag it as a system file since we intend to use cluster I/O. */ 1887 bzero(&cat_desc, sizeof(cat_desc)); 1888 cat_desc.cd_parentcnid = kHFSRootParentID; 1889 cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename; 1890 cat_desc.cd_namelen = strlen(hfs_attrdatafilename); 1891 cat_desc.cd_cnid = kHFSAttributeDataFileID; 1892 1893 bzero(&cat_attr, sizeof(cat_attr)); 1894 cat_attr.ca_linkcount = 1; 1895 cat_attr.ca_mode = S_IFREG; 1896 cat_attr.ca_fileid = cat_desc.cd_cnid; 1897 cat_attr.ca_blocks = hfsmp->totalBlocks; 1898 1899 /* 1900 * The attribute data file is a virtual file that spans the 1901 * entire file system space. 1902 * 1903 * Each extent-based attribute occupies a unique portion of 1904 * in this virtual file. The cluster I/O is done using actual 1905 * allocation block offsets so no additional mapping is needed 1906 * for the VNOP_BLOCKMAP call. 1907 * 1908 * This approach allows the attribute data to be cached without 1909 * incurring the high cost of using a separate vnode per attribute. 1910 * 1911 * Since we need to acquire the attribute b-tree file lock anyways, 1912 * the virtual file doesn't introduce any additional serialization. 1913 */ 1914 bzero(&cat_fork, sizeof(cat_fork)); 1915 cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize; 1916 cat_fork.cf_blocks = hfsmp->totalBlocks; 1917 cat_fork.cf_extents[0].startBlock = 0; 1918 cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks; 1919 1920 result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr, &cat_fork, &vp); 1921 if (result == 0) { 1922 HFS_MOUNT_LOCK(hfsmp, 1); 1923 /* Check if someone raced us for creating this vnode. */ 1924 if (hfsmp->hfs_attrdata_vp != NULLVP) { 1925 HFS_MOUNT_UNLOCK(hfsmp, 1); 1926 vnode_put(vp); 1927 vnode_recycle(vp); 1928 vp = hfsmp->hfs_attrdata_vp; 1929 } else { 1930 hfsmp->hfs_attrdata_vp = vp; 1931 HFS_MOUNT_UNLOCK(hfsmp, 1); 1932 /* Keep a reference on this vnode until unmount */ 1933 vnode_ref_ext(vp, O_EVTONLY); 1934 hfs_unlock(VTOC(vp)); 1935 } 1936 } 1937 } else { 1938 if ((result = vnode_get(vp))) 1939 vp = NULLVP; 1940 } 1941 *vpp = vp; 1942 return (result); 1943} 1944 1945/* 1946 * Read an extent based attribute. 1947 */ 1948static int 1949read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents) 1950{ 1951 vnode_t evp = NULLVP; 1952 int bufsize; 1953 int iosize; 1954 int attrsize; 1955 int blksize; 1956 int i; 1957 int result = 0; 1958 1959 if ((result = get_attr_data_vnode(hfsmp, &evp))) { 1960 return (result); 1961 } 1962 hfs_lock_truncate(VTOC(evp), 0); 1963 1964 bufsize = (int)uio_resid(uio); 1965 attrsize = (int)datasize; 1966 blksize = (int)hfsmp->blockSize; 1967 1968 /* 1969 * Read the attribute data one extent at a time. 1970 * For the typical case there is only one extent. 1971 */ 1972 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) { 1973 iosize = (int)extents[i].blockCount * blksize; 1974 iosize = MIN(iosize, attrsize); 1975 iosize = MIN(iosize, bufsize); 1976 uio_setresid(uio, iosize); 1977 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize); 1978 1979 result = cluster_read(evp, uio, VTOF(evp)->ff_size, IO_SYNC | IO_UNIT); 1980 1981#if HFS_XATTR_VERBOSE 1982 printf("read_attr_data: cr iosize %d [%d, %d] (%d)\n", 1983 iosize, extents[i].startBlock, extents[i].blockCount, result); 1984#endif 1985 if (result) 1986 break; 1987 attrsize -= iosize; 1988 bufsize -= iosize; 1989 } 1990 uio_setresid(uio, bufsize); 1991 uio_setoffset(uio, datasize); 1992 1993 hfs_unlock_truncate(VTOC(evp), 0); 1994 vnode_put(evp); 1995 return (result); 1996} 1997 1998/* 1999 * Write an extent based attribute. 2000 */ 2001static int 2002write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents) 2003{ 2004 vnode_t evp = NULLVP; 2005 off_t filesize; 2006 int bufsize; 2007 int attrsize; 2008 int iosize; 2009 int blksize; 2010 int i; 2011 int result = 0; 2012 2013 /* Get exclusive use of attribute data vnode. */ 2014 if ((result = get_attr_data_vnode(hfsmp, &evp))) { 2015 return (result); 2016 } 2017 hfs_lock_truncate(VTOC(evp), 0); 2018 2019 bufsize = uio_resid(uio); 2020 attrsize = (int) datasize; 2021 blksize = (int) hfsmp->blockSize; 2022 filesize = VTOF(evp)->ff_size; 2023 2024 /* 2025 * Write the attribute data one extent at a time. 2026 */ 2027 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) { 2028 iosize = (int)extents[i].blockCount * blksize; 2029 iosize = MIN(iosize, attrsize); 2030 iosize = MIN(iosize, bufsize); 2031 uio_setresid(uio, iosize); 2032 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize); 2033 2034 result = cluster_write(evp, uio, filesize, filesize, filesize, 2035 (off_t) 0, IO_SYNC | IO_UNIT); 2036#if HFS_XATTR_VERBOSE 2037 printf("write_attr_data: cw iosize %d [%d, %d] (%d)\n", 2038 iosize, extents[i].startBlock, extents[i].blockCount, result); 2039#endif 2040 if (result) 2041 break; 2042 attrsize -= iosize; 2043 bufsize -= iosize; 2044 } 2045 uio_setresid(uio, bufsize); 2046 uio_setoffset(uio, datasize); 2047 2048 hfs_unlock_truncate(VTOC(evp), 0); 2049 vnode_put(evp); 2050 return (result); 2051} 2052 2053/* 2054 * Allocate blocks for an extent based attribute. 2055 */ 2056static int 2057alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks) 2058{ 2059 int blkcnt; 2060 int startblk; 2061 int lockflags; 2062 int i; 2063 int maxextents; 2064 int result = 0; 2065 2066 startblk = hfsmp->hfs_metazone_end; 2067 blkcnt = howmany(attrsize, hfsmp->blockSize); 2068 if (blkcnt > (int)hfs_freeblks(hfsmp, 0)) { 2069 return (ENOSPC); 2070 } 2071 *blocks = blkcnt; 2072 maxextents = extentbufsize / sizeof(HFSPlusExtentDescriptor); 2073 2074 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK); 2075 2076 for (i = 0; (blkcnt > 0) && (i < maxextents); i++) { 2077 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0, 0, 2078 &extents[i].startBlock, &extents[i].blockCount); 2079#if HFS_XATTR_VERBOSE 2080 printf("alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n", 2081 blkcnt, extents[i].startBlock, extents[i].blockCount, result); 2082#endif 2083 if (result) { 2084 extents[i].startBlock = 0; 2085 extents[i].blockCount = 0; 2086 break; 2087 } 2088 blkcnt -= extents[i].blockCount; 2089 startblk = extents[i].startBlock + extents[i].blockCount; 2090 } 2091 /* 2092 * If it didn't fit in the extents buffer then bail. 2093 */ 2094 if (blkcnt) { 2095 result = ENOSPC; 2096 2097#if HFS_XATTR_VERBOSE 2098 printf("alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt); 2099#endif 2100 for (; i <= 0; i--) { 2101 if ((blkcnt = extents[i].blockCount) != 0) { 2102 (void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt); 2103 extents[i].startBlock = 0; 2104 extents[i].blockCount = 0; 2105 } 2106 } 2107 } 2108 2109 hfs_systemfile_unlock(hfsmp, lockflags); 2110 return MacToVFSError(result); 2111} 2112 2113/* 2114 * Release blocks from an extent based attribute. 2115 */ 2116static void 2117free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents) 2118{ 2119 vnode_t evp = NULLVP; 2120 int remblks = blkcnt; 2121 int lockflags; 2122 int i; 2123 2124 if (get_attr_data_vnode(hfsmp, &evp) != 0) { 2125 evp = NULLVP; 2126 } 2127 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK); 2128 2129 for (i = 0; (remblks > 0) && (extents[i].blockCount != 0); i++) { 2130 if (extents[i].blockCount > (u_int32_t)blkcnt) { 2131#if HFS_XATTR_VERBOSE 2132 printf("free_attr_blks: skipping bad extent [%d, %d]\n", 2133 extents[i].startBlock, extents[i].blockCount); 2134#endif 2135 extents[i].blockCount = 0; 2136 continue; 2137 } 2138 if (extents[i].startBlock == 0) { 2139 break; 2140 } 2141 (void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount); 2142 extents[i].startBlock = 0; 2143 extents[i].blockCount = 0; 2144 remblks -= extents[i].blockCount; 2145 2146#if HFS_XATTR_VERBOSE 2147 printf("free_attr_blks: BlockDeallocate [%d, %d]\n", 2148 extents[i].startBlock, extents[i].blockCount); 2149#endif 2150 /* Discard any resident pages for this block range. */ 2151 if (evp) { 2152 off_t start, end; 2153 2154 start = (u_int64_t)extents[i].startBlock * (u_int64_t)hfsmp->blockSize; 2155 end = start + (u_int64_t)extents[i].blockCount * (u_int64_t)hfsmp->blockSize; 2156 (void) ubc_msync(hfsmp->hfs_attrdata_vp, start, end, &start, UBC_INVALIDATE); 2157 } 2158 } 2159 2160 hfs_systemfile_unlock(hfsmp, lockflags); 2161 if (evp) { 2162 vnode_put(evp); 2163 } 2164} 2165 2166static int 2167has_overflow_extents(HFSPlusForkData *forkdata) 2168{ 2169 u_int32_t blocks; 2170 2171 if (forkdata->extents[7].blockCount == 0) 2172 return (0); 2173 2174 blocks = forkdata->extents[0].blockCount + 2175 forkdata->extents[1].blockCount + 2176 forkdata->extents[2].blockCount + 2177 forkdata->extents[3].blockCount + 2178 forkdata->extents[4].blockCount + 2179 forkdata->extents[5].blockCount + 2180 forkdata->extents[6].blockCount + 2181 forkdata->extents[7].blockCount; 2182 2183 return (forkdata->totalBlocks > blocks); 2184} 2185 2186static int 2187count_extent_blocks(int maxblks, HFSPlusExtentRecord extents) 2188{ 2189 int blocks; 2190 int i; 2191 2192 for (i = 0, blocks = 0; i < kHFSPlusExtentDensity; ++i) { 2193 /* Ignore obvious bogus extents. */ 2194 if (extents[i].blockCount > (u_int32_t)maxblks) 2195 continue; 2196 if (extents[i].startBlock == 0 || extents[i].blockCount == 0) 2197 break; 2198 blocks += extents[i].blockCount; 2199 } 2200 return (blocks); 2201} 2202