1/* 2 * Copyright (c) 2004-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#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/fsctl.h> 39#include <sys/vnode_internal.h> 40#include <sys/kauth.h> 41#include <sys/cprotect.h> 42#include <sys/uio_internal.h> 43 44#include "hfs.h" 45#include "hfs_cnode.h" 46#include "hfs_mount.h" 47#include "hfs_format.h" 48#include "hfs_endian.h" 49#include "hfs_btreeio.h" 50#include "hfs_fsctl.h" 51 52#include "hfscommon/headers/BTreesInternal.h" 53 54#define HFS_XATTR_VERBOSE 0 55 56#define ATTRIBUTE_FILE_NODE_SIZE 8192 57 58 59/* State information for the listattr_callback callback function. */ 60struct listattr_callback_state { 61 u_int32_t fileID; 62 int result; 63 uio_t uio; 64 size_t size; 65#if HFS_COMPRESSION 66 int showcompressed; 67 vfs_context_t ctx; 68 vnode_t vp; 69#endif /* HFS_COMPRESSION */ 70}; 71 72 73/* HFS Internal Names */ 74#define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity" 75#define XATTR_XATTREXTENTS_NAME "system.xattrextents" 76 77static u_int32_t emptyfinfo[8] = {0}; 78 79static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo); 80 81const char hfs_attrdatafilename[] = "Attribute Data"; 82 83static int listattr_callback(const HFSPlusAttrKey *key, const HFSPlusAttrData *data, 84 struct listattr_callback_state *state); 85 86static int remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator); 87 88static int getnodecount(struct hfsmount *hfsmp, size_t nodesize); 89 90static size_t getmaxinlineattrsize(struct vnode * attrvp); 91 92static int read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents); 93 94static int write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents); 95 96static int alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks); 97 98static void free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents); 99 100static int has_overflow_extents(HFSPlusForkData *forkdata); 101 102static int count_extent_blocks(int maxblks, HFSPlusExtentRecord extents); 103 104#if NAMEDSTREAMS 105/* 106 * Obtain the vnode for a stream. 107 */ 108int 109hfs_vnop_getnamedstream(struct vnop_getnamedstream_args* ap) 110{ 111 vnode_t vp = ap->a_vp; 112 vnode_t *svpp = ap->a_svpp; 113 struct cnode *cp; 114 int error = 0; 115 116 *svpp = NULL; 117 118 /* 119 * We only support the "com.apple.ResourceFork" stream. 120 */ 121 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 122 return (ENOATTR); 123 } 124 cp = VTOC(vp); 125 if ( !S_ISREG(cp->c_mode) ) { 126 return (EPERM); 127 } 128#if HFS_COMPRESSION 129 int hide_rsrc = hfs_hides_rsrc(ap->a_context, VTOC(vp), 1); 130#endif /* HFS_COMPRESSION */ 131 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 132 return (error); 133 } 134 if ((!hfs_has_rsrc(cp) 135#if HFS_COMPRESSION 136 || hide_rsrc 137#endif /* HFS_COMPRESSION */ 138 ) && (ap->a_operation != NS_OPEN)) { 139 hfs_unlock(cp); 140 return (ENOATTR); 141 } 142 error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp); 143 hfs_unlock(cp); 144 145 return (error); 146} 147 148/* 149 * Create a stream. 150 */ 151int 152hfs_vnop_makenamedstream(struct vnop_makenamedstream_args* ap) 153{ 154 vnode_t vp = ap->a_vp; 155 vnode_t *svpp = ap->a_svpp; 156 struct cnode *cp; 157 int error = 0; 158 159 *svpp = NULL; 160 161 /* 162 * We only support the "com.apple.ResourceFork" stream. 163 */ 164 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 165 return (ENOATTR); 166 } 167 cp = VTOC(vp); 168 if ( !S_ISREG(cp->c_mode) ) { 169 return (EPERM); 170 } 171#if HFS_COMPRESSION 172 if (hfs_hides_rsrc(ap->a_context, VTOC(vp), 1)) { 173 if (VNODE_IS_RSRC(vp)) { 174 return EINVAL; 175 } else { 176 error = decmpfs_decompress_file(vp, VTOCMP(vp), -1, 1, 0); 177 if (error != 0) 178 return error; 179 } 180 } 181#endif /* HFS_COMPRESSION */ 182 if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 183 return (error); 184 } 185 error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp); 186 hfs_unlock(cp); 187 188 return (error); 189} 190 191/* 192 * Remove a stream. 193 */ 194int 195hfs_vnop_removenamedstream(struct vnop_removenamedstream_args* ap) 196{ 197 vnode_t svp = ap->a_svp; 198 cnode_t *scp = VTOC(svp); 199 int error = 0; 200 201 /* 202 * We only support the "com.apple.ResourceFork" stream. 203 */ 204 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 205 return (ENOATTR); 206 } 207#if HFS_COMPRESSION 208 if (hfs_hides_rsrc(ap->a_context, scp, 1)) { 209 /* do nothing */ 210 return 0; 211 } 212#endif /* HFS_COMPRESSION */ 213 214 hfs_lock_truncate(scp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT); 215 if (VTOF(svp)->ff_size) { 216 // hfs_truncate will deal with the cnode lock 217 error = hfs_truncate(svp, 0, IO_NDELAY, 0, ap->a_context); 218 } 219 hfs_unlock_truncate(scp, HFS_LOCK_DEFAULT); 220 221 return error; 222} 223#endif 224 225 226/* Zero out the date added field for the specified cnode */ 227static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo) 228{ 229 u_int8_t *finfo = finderinfo; 230 231 /* Advance finfo by 16 bytes to the 2nd half of the finderinfo */ 232 finfo = finfo + 16; 233 234 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) { 235 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo; 236 extinfo->document_id = 0; 237 extinfo->date_added = 0; 238 extinfo->write_gen_counter = 0; 239 } else if (S_ISDIR(cp->c_attr.ca_mode)) { 240 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo; 241 extinfo->document_id = 0; 242 extinfo->date_added = 0; 243 extinfo->write_gen_counter = 0; 244 } else { 245 /* Return an error */ 246 return -1; 247 } 248 return 0; 249 250} 251 252/* 253 * Retrieve the data of an extended attribute. 254 */ 255int 256hfs_vnop_getxattr(struct vnop_getxattr_args *ap) 257/* 258 struct vnop_getxattr_args { 259 struct vnodeop_desc *a_desc; 260 vnode_t a_vp; 261 char * a_name; 262 uio_t a_uio; 263 size_t *a_size; 264 int a_options; 265 vfs_context_t a_context; 266 }; 267*/ 268{ 269 struct vnode *vp = ap->a_vp; 270 struct cnode *cp; 271 struct hfsmount *hfsmp; 272 uio_t uio = ap->a_uio; 273 size_t bufsize; 274 int result; 275 276 cp = VTOC(vp); 277 if (vp == cp->c_vp) { 278#if HFS_COMPRESSION 279 int decmpfs_hide = hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1); /* 1 == don't take the cnode lock */ 280 if (decmpfs_hide && !(ap->a_options & XATTR_SHOWCOMPRESSION)) 281 return ENOATTR; 282#endif /* HFS_COMPRESSION */ 283 284 /* Get the Finder Info. */ 285 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 286 u_int8_t finderinfo[32]; 287 bufsize = 32; 288 289 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) { 290 return (result); 291 } 292 /* Make a copy since we may not export all of it. */ 293 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo)); 294 hfs_unlock(cp); 295 296 /* Zero out the date added field in the local copy */ 297 hfs_zero_hidden_fields (cp, finderinfo); 298 299 /* Don't expose a symlink's private type/creator. */ 300 if (vnode_islnk(vp)) { 301 struct FndrFileInfo *fip; 302 303 fip = (struct FndrFileInfo *)&finderinfo; 304 fip->fdType = 0; 305 fip->fdCreator = 0; 306 } 307 /* If Finder Info is empty then it doesn't exist. */ 308 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) { 309 return (ENOATTR); 310 } 311 if (uio == NULL) { 312 *ap->a_size = bufsize; 313 return (0); 314 } 315 if ((user_size_t)uio_resid(uio) < bufsize) 316 return (ERANGE); 317 318 result = uiomove((caddr_t)&finderinfo , bufsize, uio); 319 320 return (result); 321 } 322 /* Read the Resource Fork. */ 323 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 324 struct vnode *rvp = NULL; 325 int openunlinked = 0; 326 int namelen = 0; 327 328 if ( !S_ISREG(cp->c_mode) ) { 329 return (EPERM); 330 } 331 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 332 return (result); 333 } 334 namelen = cp->c_desc.cd_namelen; 335 336 if (!hfs_has_rsrc(cp)) { 337 hfs_unlock(cp); 338 return (ENOATTR); 339 } 340 hfsmp = VTOHFS(vp); 341 if ((cp->c_flag & C_DELETED) && (namelen == 0)) { 342 openunlinked = 1; 343 } 344 345 result = hfs_vgetrsrc(hfsmp, vp, &rvp); 346 hfs_unlock(cp); 347 if (result) { 348 return (result); 349 } 350 if (uio == NULL) { 351 *ap->a_size = (size_t)VTOF(rvp)->ff_size; 352 } else { 353#if HFS_COMPRESSION 354 user_ssize_t uio_size = 0; 355 if (decmpfs_hide) 356 uio_size = uio_resid(uio); 357#endif /* HFS_COMPRESSION */ 358 result = VNOP_READ(rvp, uio, 0, ap->a_context); 359#if HFS_COMPRESSION 360 if (decmpfs_hide && 361 (result == 0) && 362 (uio_resid(uio) == uio_size)) { 363 /* 364 * We intentionally make the above call to VNOP_READ so that 365 * it can return an authorization/permission/etc. Error 366 * based on ap->a_context and thus deny this operation; 367 * in that case, result != 0 and we won't proceed. 368 * 369 * However, if result == 0, it will have returned no data 370 * because hfs_vnop_read hid the resource fork 371 * (hence uio_resid(uio) == uio_size, i.e. the uio is untouched) 372 * 373 * In that case, we try again with the decmpfs_ctx context 374 * to get the actual data 375 */ 376 result = VNOP_READ(rvp, uio, 0, decmpfs_ctx); 377 } 378#endif /* HFS_COMPRESSION */ 379 } 380 /* force the rsrc fork vnode to recycle right away */ 381 if (openunlinked) { 382 int vref; 383 vref = vnode_ref (rvp); 384 if (vref == 0) { 385 vnode_rele (rvp); 386 } 387 vnode_recycle(rvp); 388 } 389 vnode_put(rvp); 390 return (result); 391 } 392 } 393 hfsmp = VTOHFS(vp); 394 /* 395 * Standard HFS only supports native FinderInfo and Resource Forks. 396 */ 397 if (hfsmp->hfs_flags & HFS_STANDARD) { 398 return (EPERM); 399 } 400 401 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) { 402 return (result); 403 } 404 405 /* Check for non-rsrc, non-finderinfo EAs */ 406 result = hfs_getxattr_internal (cp, ap, VTOHFS(cp->c_vp), 0); 407 408 hfs_unlock(cp); 409 410 return MacToVFSError(result); 411} 412 413// Has same limitations as hfs_getxattr_internal below 414int hfs_xattr_read(vnode_t vp, const char *name, void *data, size_t *size) 415{ 416 char uio_buf[UIO_SIZEOF(1)]; 417 uio_t uio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, uio_buf, 418 sizeof(uio_buf)); 419 420 uio_addiov(uio, CAST_USER_ADDR_T(data), *size); 421 422 struct vnop_getxattr_args args = { 423 .a_uio = uio, 424 .a_name = name, 425 .a_size = size 426 }; 427 428 return hfs_getxattr_internal(VTOC(vp), &args, VTOHFS(vp), 0); 429} 430 431/* 432 * getxattr_internal 433 * 434 * We break out this internal function which searches the attributes B-Tree and the 435 * overflow extents file to find non-resource, non-finderinfo EAs. There may be cases 436 * where we need to get EAs in contexts where we are already holding the cnode lock, 437 * and to re-enter hfs_vnop_getxattr would cause us to double-lock the cnode. Instead, 438 * we can just directly call this function. 439 * 440 * We pass the hfsmp argument directly here because we may not necessarily have a cnode to 441 * operate on. Under normal conditions, we have a file or directory to query, but if we 442 * are operating on the root directory (id 1), then we may not have a cnode. In this case, if hte 443 * 'cp' argument is NULL, then we need to use the 'fileid' argument as the entry to manipulate 444 * 445 * NOTE: This function assumes the cnode lock for 'cp' is held exclusive or shared. 446 */ 447int hfs_getxattr_internal (struct cnode *cp, struct vnop_getxattr_args *ap, 448 struct hfsmount *hfsmp, u_int32_t fileid) 449{ 450 451 struct filefork *btfile; 452 struct BTreeIterator * iterator = NULL; 453 size_t bufsize = 0; 454 HFSPlusAttrRecord *recp = NULL; 455 FSBufferDescriptor btdata; 456 int lockflags = 0; 457 int result = 0; 458 u_int16_t datasize = 0; 459 uio_t uio = ap->a_uio; 460 u_int32_t target_id = 0; 461 462 if (cp) { 463 target_id = cp->c_fileid; 464 } else { 465 target_id = fileid; 466 } 467 468 469 /* Bail if we don't have an EA B-Tree. */ 470 if ((hfsmp->hfs_attribute_vp == NULL) || 471 ((cp) && (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0)) { 472 result = ENOATTR; 473 goto exit; 474 } 475 476 /* Initialize the B-Tree iterator for searching for the proper EA */ 477 btfile = VTOF(hfsmp->hfs_attribute_vp); 478 479 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 480 if (iterator == NULL) { 481 result = ENOMEM; 482 goto exit; 483 } 484 bzero(iterator, sizeof(*iterator)); 485 486 /* Allocate memory for reading in the attribute record. This buffer is 487 * big enough to read in all types of attribute records. It is not big 488 * enough to read inline attribute data which is read in later. 489 */ 490 MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK); 491 if (recp == NULL) { 492 result = ENOMEM; 493 goto exit; 494 } 495 btdata.bufferAddress = recp; 496 btdata.itemSize = sizeof(HFSPlusAttrRecord); 497 btdata.itemCount = 1; 498 499 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 500 if (result) { 501 goto exit; 502 } 503 504 /* Lookup the attribute in the Attribute B-Tree */ 505 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 506 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 507 hfs_systemfile_unlock(hfsmp, lockflags); 508 509 if (result) { 510 if (result == btNotFound) { 511 result = ENOATTR; 512 } 513 goto exit; 514 } 515 516 /* 517 * Operate differently if we have inline EAs that can fit in the attribute B-Tree or if 518 * we have extent based EAs. 519 */ 520 switch (recp->recordType) { 521 522 /* Attribute fits in the Attribute B-Tree */ 523 case kHFSPlusAttrInlineData: { 524 /* 525 * Sanity check record size. It's not required to have any 526 * user data, so the minimum size is 2 bytes less that the 527 * size of HFSPlusAttrData (since HFSPlusAttrData struct 528 * has 2 bytes set aside for attribute data). 529 */ 530 if (datasize < (sizeof(HFSPlusAttrData) - 2)) { 531 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n", 532 hfsmp->vcbVN, target_id, ap->a_name, datasize, sizeof(HFSPlusAttrData)); 533 result = ENOATTR; 534 break; 535 } 536 *ap->a_size = recp->attrData.attrSize; 537 if (uio && recp->attrData.attrSize != 0) { 538 if (*ap->a_size > (user_size_t)uio_resid(uio)) { 539 /* User provided buffer is not large enough for the xattr data */ 540 result = ERANGE; 541 } else { 542 /* Previous BTreeSearchRecord() read in only the attribute record, 543 * and not the attribute data. Now allocate enough memory for 544 * both attribute record and data, and read the attribute record again. 545 */ 546 bufsize = sizeof(HFSPlusAttrData) - 2 + recp->attrData.attrSize; 547 FREE(recp, M_TEMP); 548 MALLOC(recp, HFSPlusAttrRecord *, bufsize, M_TEMP, M_WAITOK); 549 if (recp == NULL) { 550 result = ENOMEM; 551 goto exit; 552 } 553 554 btdata.bufferAddress = recp; 555 btdata.itemSize = bufsize; 556 btdata.itemCount = 1; 557 558 bzero(iterator, sizeof(*iterator)); 559 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 560 if (result) { 561 goto exit; 562 } 563 564 /* Lookup the attribute record and inline data */ 565 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 566 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 567 hfs_systemfile_unlock(hfsmp, lockflags); 568 if (result) { 569 if (result == btNotFound) { 570 result = ENOATTR; 571 } 572 goto exit; 573 } 574 575 /* Copy-out the attribute data to the user buffer */ 576 *ap->a_size = recp->attrData.attrSize; 577 result = uiomove((caddr_t) &recp->attrData.attrData , recp->attrData.attrSize, uio); 578 } 579 } 580 break; 581 } 582 583 /* Extent-Based EAs */ 584 case kHFSPlusAttrForkData: { 585 if (datasize < sizeof(HFSPlusAttrForkData)) { 586 printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n", 587 hfsmp->vcbVN, target_id, ap->a_name, datasize, sizeof(HFSPlusAttrForkData)); 588 result = ENOATTR; 589 break; 590 } 591 *ap->a_size = recp->forkData.theFork.logicalSize; 592 if (uio == NULL) { 593 break; 594 } 595 if (*ap->a_size > (user_size_t)uio_resid(uio)) { 596 result = ERANGE; 597 break; 598 } 599 /* Process overflow extents if necessary. */ 600 if (has_overflow_extents(&recp->forkData.theFork)) { 601 HFSPlusExtentDescriptor *extentbuf; 602 HFSPlusExtentDescriptor *extentptr; 603 size_t extentbufsize; 604 u_int32_t totalblocks; 605 u_int32_t blkcnt; 606 u_int32_t attrlen; 607 608 totalblocks = recp->forkData.theFork.totalBlocks; 609 /* Ignore bogus block counts. */ 610 if (totalblocks > howmany(HFS_XATTR_MAXSIZE, hfsmp->blockSize)) { 611 result = ERANGE; 612 break; 613 } 614 attrlen = recp->forkData.theFork.logicalSize; 615 616 /* Get a buffer to hold the worst case amount of extents. */ 617 extentbufsize = totalblocks * sizeof(HFSPlusExtentDescriptor); 618 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord)); 619 MALLOC(extentbuf, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK); 620 if (extentbuf == NULL) { 621 result = ENOMEM; 622 break; 623 } 624 bzero(extentbuf, extentbufsize); 625 extentptr = extentbuf; 626 627 /* Grab the first 8 extents. */ 628 bcopy(&recp->forkData.theFork.extents[0], extentptr, sizeof(HFSPlusExtentRecord)); 629 extentptr += kHFSPlusExtentDensity; 630 blkcnt = count_extent_blocks(totalblocks, recp->forkData.theFork.extents); 631 632 /* Now lookup the overflow extents. */ 633 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 634 while (blkcnt < totalblocks) { 635 ((HFSPlusAttrKey *)&iterator->key)->startBlock = blkcnt; 636 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 637 if (result || 638 (recp->recordType != kHFSPlusAttrExtents) || 639 (datasize < sizeof(HFSPlusAttrExtents))) { 640 printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n", 641 ap->a_name, blkcnt, totalblocks); 642 result = ENOATTR; 643 break; /* break from while */ 644 } 645 /* Grab the next 8 extents. */ 646 bcopy(&recp->overflowExtents.extents[0], extentptr, sizeof(HFSPlusExtentRecord)); 647 extentptr += kHFSPlusExtentDensity; 648 blkcnt += count_extent_blocks(totalblocks, recp->overflowExtents.extents); 649 } 650 651 /* Release Attr B-Tree lock */ 652 hfs_systemfile_unlock(hfsmp, lockflags); 653 654 if (blkcnt < totalblocks) { 655 result = ENOATTR; 656 } else { 657 result = read_attr_data(hfsmp, uio, attrlen, extentbuf); 658 } 659 FREE(extentbuf, M_TEMP); 660 661 } else { /* No overflow extents. */ 662 result = read_attr_data(hfsmp, uio, recp->forkData.theFork.logicalSize, recp->forkData.theFork.extents); 663 } 664 break; 665 } 666 667 default: 668 /* We only support Extent or inline EAs. Default to ENOATTR for anything else */ 669 result = ENOATTR; 670 break; 671 } 672 673exit: 674 if (iterator) { 675 FREE(iterator, M_TEMP); 676 } 677 if (recp) { 678 FREE(recp, M_TEMP); 679 } 680 681 return result; 682 683} 684 685 686/* 687 * Set the data of an extended attribute. 688 */ 689int 690hfs_vnop_setxattr(struct vnop_setxattr_args *ap) 691/* 692 struct vnop_setxattr_args { 693 struct vnodeop_desc *a_desc; 694 vnode_t a_vp; 695 char * a_name; 696 uio_t a_uio; 697 int a_options; 698 vfs_context_t a_context; 699 }; 700*/ 701{ 702 struct vnode *vp = ap->a_vp; 703 struct cnode *cp = NULL; 704 struct hfsmount *hfsmp; 705 uio_t uio = ap->a_uio; 706 size_t attrsize; 707 void * user_data_ptr = NULL; 708 int result; 709 time_t orig_ctime=VTOC(vp)->c_ctime; 710 711 if (ap->a_name == NULL || ap->a_name[0] == '\0') { 712 return (EINVAL); /* invalid name */ 713 } 714 hfsmp = VTOHFS(vp); 715 if (VNODE_IS_RSRC(vp)) { 716 return (EPERM); 717 } 718 719#if HFS_COMPRESSION 720 if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) ) { /* 1 == don't take the cnode lock */ 721 result = decmpfs_decompress_file(vp, VTOCMP(vp), -1, 1, 0); 722 if (result != 0) 723 return result; 724 } 725#endif /* HFS_COMPRESSION */ 726 727 check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_WRITE_OP, NSPACE_REARM_NO_ARG); 728 729 /* Set the Finder Info. */ 730 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 731 u_int8_t finderinfo[32]; 732 struct FndrFileInfo *fip; 733 void * finderinfo_start; 734 u_int8_t *finfo = NULL; 735 u_int16_t fdFlags; 736 u_int32_t dateadded = 0; 737 u_int32_t write_gen_counter = 0; 738 u_int32_t document_id = 0; 739 740 attrsize = sizeof(VTOC(vp)->c_finderinfo); 741 742 if ((user_size_t)uio_resid(uio) != attrsize) { 743 return (ERANGE); 744 } 745 /* Grab the new Finder Info data. */ 746 if ((result = uiomove((caddr_t)&finderinfo , attrsize, uio))) { 747 return (result); 748 } 749 fip = (struct FndrFileInfo *)&finderinfo; 750 751 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 752 return (result); 753 } 754 cp = VTOC(vp); 755 756 /* Symlink's don't have an external type/creator. */ 757 if (vnode_islnk(vp)) { 758 /* Skip over type/creator fields. */ 759 finderinfo_start = &cp->c_finderinfo[8]; 760 attrsize -= 8; 761 } else { 762 finderinfo_start = &cp->c_finderinfo[0]; 763 /* 764 * Don't allow the external setting of 765 * file type to kHardLinkFileType. 766 */ 767 if (fip->fdType == SWAP_BE32(kHardLinkFileType)) { 768 hfs_unlock(cp); 769 return (EPERM); 770 } 771 } 772 773 /* Grab the current date added from the cnode */ 774 dateadded = hfs_get_dateadded (cp); 775 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) { 776 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)((u_int8_t*)cp->c_finderinfo + 16); 777 /* 778 * Grab generation counter directly from the cnode 779 * instead of calling hfs_get_gencount(), because 780 * for zero generation count values hfs_get_gencount() 781 * lies and bumps it up to one. 782 */ 783 write_gen_counter = extinfo->write_gen_counter; 784 document_id = extinfo->document_id; 785 } else if (S_ISDIR(cp->c_attr.ca_mode)) { 786 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)cp->c_finderinfo + 16); 787 write_gen_counter = extinfo->write_gen_counter; 788 document_id = extinfo->document_id; 789 } 790 791 /* 792 * Zero out the finder info's reserved fields like date added, 793 * generation counter, and document id to ignore user's attempts 794 * to set it 795 */ 796 hfs_zero_hidden_fields(cp, finderinfo); 797 798 if (bcmp(finderinfo_start, emptyfinfo, attrsize)) { 799 /* attr exists and "create" was specified. */ 800 if (ap->a_options & XATTR_CREATE) { 801 hfs_unlock(cp); 802 return (EEXIST); 803 } 804 } else { /* empty */ 805 /* attr doesn't exists and "replace" was specified. */ 806 if (ap->a_options & XATTR_REPLACE) { 807 hfs_unlock(cp); 808 return (ENOATTR); 809 } 810 } 811 812 /* 813 * Now restore the date added and other reserved fields to the finderinfo to 814 * be written out. Advance to the 2nd half of the finderinfo to write them 815 * out into the buffer. 816 * 817 * Make sure to endian swap the date added back into big endian. When we used 818 * hfs_get_dateadded above to retrieve it, it swapped into local endianness 819 * for us. But now that we're writing it out, put it back into big endian. 820 */ 821 finfo = &finderinfo[16]; 822 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) { 823 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo; 824 extinfo->date_added = OSSwapHostToBigInt32(dateadded); 825 extinfo->write_gen_counter = write_gen_counter; 826 extinfo->document_id = document_id; 827 } else if (S_ISDIR(cp->c_attr.ca_mode)) { 828 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo; 829 extinfo->date_added = OSSwapHostToBigInt32(dateadded); 830 extinfo->write_gen_counter = write_gen_counter; 831 extinfo->document_id = document_id; 832 } 833 834 /* Set the cnode's Finder Info. */ 835 if (attrsize == sizeof(cp->c_finderinfo)) { 836 bcopy(&finderinfo[0], finderinfo_start, attrsize); 837 } else { 838 bcopy(&finderinfo[8], finderinfo_start, attrsize); 839 } 840 841 /* Updating finderInfo updates change time and modified time */ 842 cp->c_touch_chgtime = TRUE; 843 cp->c_flag |= C_MODIFIED; 844 845 /* 846 * Mirror the invisible bit to the UF_HIDDEN flag. 847 * 848 * The fdFlags for files and frFlags for folders are both 8 bytes 849 * into the userInfo (the first 16 bytes of the Finder Info). They 850 * are both 16-bit fields. 851 */ 852 fdFlags = *((u_int16_t *) &cp->c_finderinfo[8]); 853 if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) { 854 cp->c_bsdflags |= UF_HIDDEN; 855 } else { 856 cp->c_bsdflags &= ~UF_HIDDEN; 857 } 858 859 result = hfs_update(vp, FALSE); 860 861 hfs_unlock(cp); 862 return (result); 863 } 864 /* Write the Resource Fork. */ 865 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 866 struct vnode *rvp = NULL; 867 int namelen = 0; 868 int openunlinked = 0; 869 870 if (!vnode_isreg(vp)) { 871 return (EPERM); 872 } 873 if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 874 return (result); 875 } 876 cp = VTOC(vp); 877 namelen = cp->c_desc.cd_namelen; 878 879 if (hfs_has_rsrc(cp)) { 880 /* attr exists and "create" was specified. */ 881 if (ap->a_options & XATTR_CREATE) { 882 hfs_unlock(cp); 883 return (EEXIST); 884 } 885 } else { 886 /* attr doesn't exists and "replace" was specified. */ 887 if (ap->a_options & XATTR_REPLACE) { 888 hfs_unlock(cp); 889 return (ENOATTR); 890 } 891 } 892 893 /* 894 * Note that we could be called on to grab the rsrc fork vnode 895 * for a file that has become open-unlinked. 896 */ 897 if ((cp->c_flag & C_DELETED) && (namelen == 0)) { 898 openunlinked = 1; 899 } 900 901 result = hfs_vgetrsrc(hfsmp, vp, &rvp); 902 hfs_unlock(cp); 903 if (result) { 904 return (result); 905 } 906 /* VNOP_WRITE marks cnode as needing a modtime update */ 907 result = VNOP_WRITE(rvp, uio, 0, ap->a_context); 908 909 /* if open unlinked, force it inactive */ 910 if (openunlinked) { 911 int vref; 912 vref = vnode_ref (rvp); 913 if (vref == 0) { 914 vnode_rele(rvp); 915 } 916 vnode_recycle (rvp); 917 } else { 918 /* cnode is not open-unlinked, so re-lock cnode to sync */ 919 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 920 vnode_recycle (rvp); 921 vnode_put(rvp); 922 return result; 923 } 924 925 /* hfs fsync rsrc fork to force to disk and update modtime */ 926 result = hfs_fsync (rvp, MNT_NOWAIT, 0, vfs_context_proc (ap->a_context)); 927 hfs_unlock (cp); 928 } 929 930 vnode_put(rvp); 931 return (result); 932 } 933 /* 934 * Standard HFS only supports native FinderInfo and Resource Forks. 935 */ 936 if (hfsmp->hfs_flags & HFS_STANDARD) { 937 return (EPERM); 938 } 939 attrsize = uio_resid(uio); 940 941 /* Enforce an upper limit. */ 942 if (attrsize > HFS_XATTR_MAXSIZE) { 943 result = E2BIG; 944 goto exit; 945 } 946 947 /* 948 * Attempt to copy the users attr data before taking any locks, 949 * only if it will be an inline attribute. For larger attributes, 950 * the data will be directly read from the uio. 951 */ 952 if (attrsize > 0 && 953 hfsmp->hfs_max_inline_attrsize != 0 && 954 attrsize < hfsmp->hfs_max_inline_attrsize) { 955 MALLOC(user_data_ptr, void *, attrsize, M_TEMP, M_WAITOK); 956 if (user_data_ptr == NULL) { 957 result = ENOMEM; 958 goto exit; 959 } 960 961 result = uiomove((caddr_t)user_data_ptr, attrsize, uio); 962 if (result) { 963 goto exit; 964 } 965 } 966 967 result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT); 968 if (result) { 969 goto exit; 970 } 971 cp = VTOC(vp); 972 973 /* 974 * If we're trying to set a non-finderinfo, non-resourcefork EA, then 975 * call the breakout function. 976 */ 977 result = hfs_setxattr_internal (cp, user_data_ptr, attrsize, ap, VTOHFS(vp), 0); 978 979 exit: 980 if (cp) { 981 hfs_unlock(cp); 982 } 983 if (user_data_ptr) { 984 FREE(user_data_ptr, M_TEMP); 985 } 986 987 return (result == btNotFound ? ENOATTR : MacToVFSError(result)); 988} 989 990// Has same limitations as hfs_setxattr_internal below 991int hfs_xattr_write(vnode_t vp, const char *name, const void *data, size_t size) 992{ 993 struct vnop_setxattr_args args = { 994 .a_vp = vp, 995 .a_name = name, 996 }; 997 998 return hfs_setxattr_internal(VTOC(vp), data, size, &args, VTOHFS(vp), 0); 999} 1000 1001/* 1002 * hfs_setxattr_internal 1003 * 1004 * Internal function to set non-rsrc, non-finderinfo EAs to either the attribute B-Tree or 1005 * extent-based EAs. 1006 * 1007 * See comments from hfs_getxattr_internal on why we need to pass 'hfsmp' and fileid here. 1008 * The gist is that we could end up writing to the root folder which may not have a cnode. 1009 * 1010 * Assumptions: 1011 * 1. cnode 'cp' is locked EXCLUSIVE before calling this function. 1012 * 2. data_ptr contains data to be written. If gathering data from userland, this must be 1013 * done before calling this function. 1014 * 3. If data originates entirely in-kernel, use a null UIO, and ensure the size is less than 1015 * hfsmp->hfs_max_inline_attrsize bytes long. 1016 */ 1017int hfs_setxattr_internal (struct cnode *cp, const void *data_ptr, size_t attrsize, 1018 struct vnop_setxattr_args *ap, struct hfsmount *hfsmp, 1019 u_int32_t fileid) 1020{ 1021 uio_t uio = ap->a_uio; 1022 struct vnode *vp = ap->a_vp; 1023 int started_transaction = 0; 1024 struct BTreeIterator * iterator = NULL; 1025 struct filefork *btfile = NULL; 1026 FSBufferDescriptor btdata; 1027 HFSPlusAttrRecord attrdata; /* 90 bytes */ 1028 HFSPlusAttrRecord *recp = NULL; 1029 HFSPlusExtentDescriptor *extentptr = NULL; 1030 int result = 0; 1031 int lockflags = 0; 1032 int exists = 0; 1033 int allocatedblks = 0; 1034 u_int32_t target_id; 1035 int takelock = 1; 1036 1037 if (cp) { 1038 target_id = cp->c_fileid; 1039 } else { 1040 target_id = fileid; 1041 if (target_id != 1) { 1042 /* 1043 * If we are manipulating something other than 1044 * the root folder (id 1), and do not have a cnode-in-hand, 1045 * then we must already hold the requisite b-tree locks from 1046 * earlier up the call stack. (See hfs_makenode) 1047 */ 1048 takelock = 0; 1049 } 1050 } 1051 1052 /* Start a transaction for our changes. */ 1053 if (hfs_start_transaction(hfsmp) != 0) { 1054 result = EINVAL; 1055 goto exit; 1056 } 1057 started_transaction = 1; 1058 1059 /* 1060 * Once we started the transaction, nobody can compete 1061 * with us, so make sure this file is still there. 1062 */ 1063 if ((cp) && (cp->c_flag & C_NOEXISTS)) { 1064 result = ENOENT; 1065 goto exit; 1066 } 1067 1068 /* 1069 * If there isn't an attributes b-tree then create one. 1070 */ 1071 if (hfsmp->hfs_attribute_vp == NULL) { 1072 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE, 1073 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE)); 1074 if (result) { 1075 goto exit; 1076 } 1077 } 1078 if (hfsmp->hfs_max_inline_attrsize == 0) { 1079 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp); 1080 } 1081 1082 if (takelock) { 1083 /* Take exclusive access to the attributes b-tree. */ 1084 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); 1085 } 1086 1087 /* Build the b-tree key. */ 1088 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1089 if (iterator == NULL) { 1090 result = ENOMEM; 1091 goto exit; 1092 } 1093 bzero(iterator, sizeof(*iterator)); 1094 result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 1095 if (result) { 1096 goto exit; 1097 } 1098 1099 /* Preflight for replace/create semantics. */ 1100 btfile = VTOF(hfsmp->hfs_attribute_vp); 1101 btdata.bufferAddress = &attrdata; 1102 btdata.itemSize = sizeof(attrdata); 1103 btdata.itemCount = 1; 1104 exists = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL) == 0; 1105 1106 /* Replace requires that the attribute already exists. */ 1107 if ((ap->a_options & XATTR_REPLACE) && !exists) { 1108 result = ENOATTR; 1109 goto exit; 1110 } 1111 /* Create requires that the attribute doesn't exist. */ 1112 if ((ap->a_options & XATTR_CREATE) && exists) { 1113 result = EEXIST; 1114 goto exit; 1115 } 1116 1117 /* If it won't fit inline then use extent-based attributes. */ 1118 if (attrsize > hfsmp->hfs_max_inline_attrsize) { 1119 size_t extentbufsize; 1120 int blkcnt; 1121 int extentblks; 1122 u_int32_t *keystartblk; 1123 int i; 1124 1125 if (uio == NULL) { 1126 /* 1127 * setxattrs originating from in-kernel are not supported if they are bigger 1128 * than the inline max size. Just return ENOATTR and force them to do it with a 1129 * smaller EA. 1130 */ 1131 result = EPERM; 1132 goto exit; 1133 } 1134 1135 /* Get some blocks. */ 1136 blkcnt = howmany(attrsize, hfsmp->blockSize); 1137 extentbufsize = blkcnt * sizeof(HFSPlusExtentDescriptor); 1138 extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord)); 1139 MALLOC(extentptr, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK); 1140 if (extentptr == NULL) { 1141 result = ENOMEM; 1142 goto exit; 1143 } 1144 bzero(extentptr, extentbufsize); 1145 result = alloc_attr_blks(hfsmp, attrsize, extentbufsize, extentptr, &allocatedblks); 1146 if (result) { 1147 allocatedblks = 0; 1148 goto exit; /* no more space */ 1149 } 1150 /* Copy data into the blocks. */ 1151 result = write_attr_data(hfsmp, uio, attrsize, extentptr); 1152 if (result) { 1153 if (vp) { 1154 const char *name = vnode_getname(vp); 1155 printf("hfs_setxattr: write_attr_data vol=%s err (%d) %s:%s\n", 1156 hfsmp->vcbVN, result, name ? name : "", ap->a_name); 1157 if (name) 1158 vnode_putname(name); 1159 } 1160 goto exit; 1161 } 1162 1163 /* Now remove any previous attribute. */ 1164 if (exists) { 1165 result = remove_attribute_records(hfsmp, iterator); 1166 if (result) { 1167 if (vp) { 1168 const char *name = vnode_getname(vp); 1169 printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n", 1170 hfsmp->vcbVN, result, name ? name : "", ap->a_name); 1171 if (name) 1172 vnode_putname(name); 1173 } 1174 goto exit; 1175 } 1176 } 1177 /* Create attribute fork data record. */ 1178 MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK); 1179 if (recp == NULL) { 1180 result = ENOMEM; 1181 goto exit; 1182 } 1183 btdata.bufferAddress = recp; 1184 btdata.itemCount = 1; 1185 btdata.itemSize = sizeof(HFSPlusAttrForkData); 1186 1187 recp->recordType = kHFSPlusAttrForkData; 1188 recp->forkData.reserved = 0; 1189 recp->forkData.theFork.logicalSize = attrsize; 1190 recp->forkData.theFork.clumpSize = 0; 1191 recp->forkData.theFork.totalBlocks = blkcnt; 1192 bcopy(extentptr, recp->forkData.theFork.extents, sizeof(HFSPlusExtentRecord)); 1193 1194 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 1195 1196 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize); 1197 if (result) { 1198 printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n", 1199 hfsmp->vcbVN, target_id, ap->a_name, result); 1200 goto exit; 1201 } 1202 extentblks = count_extent_blocks(blkcnt, recp->forkData.theFork.extents); 1203 blkcnt -= extentblks; 1204 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock; 1205 i = 0; 1206 1207 /* Create overflow extents as needed. */ 1208 while (blkcnt > 0) { 1209 /* Initialize the key and record. */ 1210 *keystartblk += (u_int32_t)extentblks; 1211 btdata.itemSize = sizeof(HFSPlusAttrExtents); 1212 recp->recordType = kHFSPlusAttrExtents; 1213 recp->overflowExtents.reserved = 0; 1214 1215 /* Copy the next set of extents. */ 1216 i += kHFSPlusExtentDensity; 1217 bcopy(&extentptr[i], recp->overflowExtents.extents, sizeof(HFSPlusExtentRecord)); 1218 1219 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize); 1220 if (result) { 1221 printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n", 1222 hfsmp->vcbVN, target_id, ap->a_name, result); 1223 goto exit; 1224 } 1225 extentblks = count_extent_blocks(blkcnt, recp->overflowExtents.extents); 1226 blkcnt -= extentblks; 1227 } 1228 } else { /* Inline data */ 1229 if (exists) { 1230 result = remove_attribute_records(hfsmp, iterator); 1231 if (result) { 1232 goto exit; 1233 } 1234 } 1235 1236 /* Calculate size of record rounded up to multiple of 2 bytes. */ 1237 btdata.itemSize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0); 1238 MALLOC(recp, HFSPlusAttrRecord *, btdata.itemSize, M_TEMP, M_WAITOK); 1239 if (recp == NULL) { 1240 result = ENOMEM; 1241 goto exit; 1242 } 1243 recp->recordType = kHFSPlusAttrInlineData; 1244 recp->attrData.reserved[0] = 0; 1245 recp->attrData.reserved[1] = 0; 1246 recp->attrData.attrSize = attrsize; 1247 1248 /* Copy in the attribute data (if any). */ 1249 if (attrsize > 0) { 1250 if (data_ptr) { 1251 bcopy(data_ptr, &recp->attrData.attrData, attrsize); 1252 } else { 1253 /* 1254 * A null UIO meant it originated in-kernel. If they didn't supply data_ptr 1255 * then deny the copy operation. 1256 */ 1257 if (uio == NULL) { 1258 result = EPERM; 1259 goto exit; 1260 } 1261 result = uiomove((caddr_t)&recp->attrData.attrData, attrsize, uio); 1262 } 1263 1264 if (result) { 1265 goto exit; 1266 } 1267 } 1268 1269 (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 1270 1271 btdata.bufferAddress = recp; 1272 btdata.itemCount = 1; 1273 result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize); 1274 } 1275 1276exit: 1277 if (btfile && started_transaction) { 1278 (void) BTFlushPath(btfile); 1279 } 1280 if (lockflags) { 1281 hfs_systemfile_unlock(hfsmp, lockflags); 1282 } 1283 if (result == 0) { 1284 if (vp) { 1285 cp = VTOC(vp); 1286 /* Setting an attribute only updates change time and not 1287 * modified time of the file. 1288 */ 1289 cp->c_touch_chgtime = TRUE; 1290 cp->c_attr.ca_recflags |= kHFSHasAttributesMask; 1291 if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) { 1292 cp->c_attr.ca_recflags |= kHFSHasSecurityMask; 1293 } 1294 (void) hfs_update(vp, 0); 1295 } 1296 } 1297 if (started_transaction) { 1298 if (result && allocatedblks) { 1299 free_attr_blks(hfsmp, allocatedblks, extentptr); 1300 } 1301 hfs_end_transaction(hfsmp); 1302 } 1303 1304 if (recp) { 1305 FREE(recp, M_TEMP); 1306 } 1307 if (extentptr) { 1308 FREE(extentptr, M_TEMP); 1309 } 1310 if (iterator) { 1311 FREE(iterator, M_TEMP); 1312 } 1313 1314 return result; 1315} 1316 1317 1318 1319 1320/* 1321 * Remove an extended attribute. 1322 */ 1323int 1324hfs_vnop_removexattr(struct vnop_removexattr_args *ap) 1325/* 1326 struct vnop_removexattr_args { 1327 struct vnodeop_desc *a_desc; 1328 vnode_t a_vp; 1329 char * a_name; 1330 int a_options; 1331 vfs_context_t a_context; 1332 }; 1333*/ 1334{ 1335 struct vnode *vp = ap->a_vp; 1336 struct cnode *cp = VTOC(vp); 1337 struct hfsmount *hfsmp; 1338 struct BTreeIterator * iterator = NULL; 1339 int lockflags; 1340 int result; 1341 time_t orig_ctime=VTOC(vp)->c_ctime; 1342 1343 if (ap->a_name == NULL || ap->a_name[0] == '\0') { 1344 return (EINVAL); /* invalid name */ 1345 } 1346 hfsmp = VTOHFS(vp); 1347 if (VNODE_IS_RSRC(vp)) { 1348 return (EPERM); 1349 } 1350 1351#if HFS_COMPRESSION 1352 if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) && !(ap->a_options & XATTR_SHOWCOMPRESSION)) { 1353 return ENOATTR; 1354 } 1355#endif /* HFS_COMPRESSION */ 1356 1357 check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_DELETE_OP, NSPACE_REARM_NO_ARG); 1358 1359 /* If Resource Fork is non-empty then truncate it. */ 1360 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 1361 struct vnode *rvp = NULL; 1362 1363 if ( !vnode_isreg(vp) ) { 1364 return (EPERM); 1365 } 1366 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 1367 return (result); 1368 } 1369 if (!hfs_has_rsrc(cp)) { 1370 hfs_unlock(cp); 1371 return (ENOATTR); 1372 } 1373 result = hfs_vgetrsrc(hfsmp, vp, &rvp); 1374 hfs_unlock(cp); 1375 if (result) { 1376 return (result); 1377 } 1378 1379 hfs_lock_truncate(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT); 1380 1381 // Tell UBC now before we take the cnode lock and start the transaction 1382 hfs_ubc_setsize(rvp, 0, false); 1383 1384 if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 1385 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT); 1386 vnode_put(rvp); 1387 return (result); 1388 } 1389 1390 /* Start a transaction for encapsulating changes in 1391 * hfs_truncate() and hfs_update() 1392 */ 1393 if ((result = hfs_start_transaction(hfsmp))) { 1394 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT); 1395 hfs_unlock(cp); 1396 vnode_put(rvp); 1397 return (result); 1398 } 1399 1400 result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context); 1401 if (result == 0) { 1402 cp->c_touch_chgtime = TRUE; 1403 cp->c_flag |= C_MODIFIED; 1404 result = hfs_update(vp, FALSE); 1405 } 1406 1407 hfs_end_transaction(hfsmp); 1408 hfs_unlock_truncate(VTOC(rvp), HFS_LOCK_DEFAULT); 1409 hfs_unlock(VTOC(rvp)); 1410 1411 vnode_put(rvp); 1412 return (result); 1413 } 1414 /* Clear out the Finder Info. */ 1415 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 1416 void * finderinfo_start; 1417 int finderinfo_size; 1418 u_int8_t finderinfo[32]; 1419 u_int32_t date_added, write_gen_counter, document_id; 1420 u_int8_t *finfo = NULL; 1421 1422 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 1423 return (result); 1424 } 1425 1426 /* Use the local copy to store our temporary changes. */ 1427 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo)); 1428 1429 1430 /* Zero out the date added field in the local copy */ 1431 hfs_zero_hidden_fields (cp, finderinfo); 1432 1433 /* Don't expose a symlink's private type/creator. */ 1434 if (vnode_islnk(vp)) { 1435 struct FndrFileInfo *fip; 1436 1437 fip = (struct FndrFileInfo *)&finderinfo; 1438 fip->fdType = 0; 1439 fip->fdCreator = 0; 1440 } 1441 1442 /* Do the byte compare against the local copy */ 1443 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) { 1444 hfs_unlock(cp); 1445 return (ENOATTR); 1446 } 1447 1448 /* 1449 * If there was other content, zero out everything except 1450 * type/creator and date added. First, save the date added. 1451 */ 1452 finfo = cp->c_finderinfo; 1453 finfo = finfo + 16; 1454 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) { 1455 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo; 1456 date_added = extinfo->date_added; 1457 write_gen_counter = extinfo->write_gen_counter; 1458 document_id = extinfo->document_id; 1459 } else if (S_ISDIR(cp->c_attr.ca_mode)) { 1460 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo; 1461 date_added = extinfo->date_added; 1462 write_gen_counter = extinfo->write_gen_counter; 1463 document_id = extinfo->document_id; 1464 } 1465 1466 if (vnode_islnk(vp)) { 1467 /* Ignore type/creator */ 1468 finderinfo_start = &cp->c_finderinfo[8]; 1469 finderinfo_size = sizeof(cp->c_finderinfo) - 8; 1470 } else { 1471 finderinfo_start = &cp->c_finderinfo[0]; 1472 finderinfo_size = sizeof(cp->c_finderinfo); 1473 } 1474 bzero(finderinfo_start, finderinfo_size); 1475 1476 1477 /* Now restore the date added */ 1478 if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) { 1479 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo; 1480 extinfo->date_added = date_added; 1481 extinfo->write_gen_counter = write_gen_counter; 1482 extinfo->document_id = document_id; 1483 } else if (S_ISDIR(cp->c_attr.ca_mode)) { 1484 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo; 1485 extinfo->date_added = date_added; 1486 extinfo->write_gen_counter = write_gen_counter; 1487 extinfo->document_id = document_id; 1488 } 1489 1490 /* Updating finderInfo updates change time and modified time */ 1491 cp->c_touch_chgtime = TRUE; 1492 cp->c_flag |= C_MODIFIED; 1493 hfs_update(vp, FALSE); 1494 1495 hfs_unlock(cp); 1496 1497 return (0); 1498 } 1499 /* 1500 * Standard HFS only supports native FinderInfo and Resource Forks. 1501 */ 1502 if (hfsmp->hfs_flags & HFS_STANDARD) { 1503 return (EPERM); 1504 } 1505 if (hfsmp->hfs_attribute_vp == NULL) { 1506 return (ENOATTR); 1507 } 1508 1509 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1510 if (iterator == NULL) { 1511 return (ENOMEM); 1512 } 1513 bzero(iterator, sizeof(*iterator)); 1514 1515 if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) { 1516 goto exit_nolock; 1517 } 1518 1519 result = hfs_buildattrkey(cp->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key); 1520 if (result) { 1521 goto exit; 1522 } 1523 1524 if (hfs_start_transaction(hfsmp) != 0) { 1525 result = EINVAL; 1526 goto exit; 1527 } 1528 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK); 1529 1530 result = remove_attribute_records(hfsmp, iterator); 1531 1532 hfs_systemfile_unlock(hfsmp, lockflags); 1533 1534 if (result == 0) { 1535 cp->c_touch_chgtime = TRUE; 1536 1537 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 1538 1539 /* If no more attributes exist, clear attribute bit */ 1540 result = file_attribute_exist(hfsmp, cp->c_fileid); 1541 if (result == 0) { 1542 cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask; 1543 } 1544 if (result == EEXIST) { 1545 result = 0; 1546 } 1547 1548 hfs_systemfile_unlock(hfsmp, lockflags); 1549 1550 /* If ACL was removed, clear security bit */ 1551 if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) { 1552 cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask; 1553 } 1554 (void) hfs_update(vp, 0); 1555 } 1556 1557 hfs_end_transaction(hfsmp); 1558exit: 1559 hfs_unlock(cp); 1560exit_nolock: 1561 FREE(iterator, M_TEMP); 1562 return MacToVFSError(result); 1563} 1564 1565/* Check if any attribute record exist for given fileID. This function 1566 * is called by hfs_vnop_removexattr to determine if it should clear the 1567 * attribute bit in the catalog record or not. 1568 * 1569 * Note - you must acquire a shared lock on the attribute btree before 1570 * calling this function. 1571 * 1572 * Output: 1573 * EEXIST - If attribute record was found 1574 * 0 - Attribute was not found 1575 * (other) - Other error (such as EIO) 1576 */ 1577int 1578file_attribute_exist(struct hfsmount *hfsmp, uint32_t fileID) 1579{ 1580 HFSPlusAttrKey *key; 1581 struct BTreeIterator * iterator = NULL; 1582 struct filefork *btfile; 1583 int result = 0; 1584 1585 // if there's no attribute b-tree we sure as heck 1586 // can't have any attributes! 1587 if (hfsmp->hfs_attribute_vp == NULL) { 1588 return false; 1589 } 1590 1591 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1592 if (iterator == NULL) { 1593 result = ENOMEM; 1594 goto out; 1595 } 1596 bzero(iterator, sizeof(*iterator)); 1597 key = (HFSPlusAttrKey *)&iterator->key; 1598 1599 result = hfs_buildattrkey(fileID, NULL, key); 1600 if (result) { 1601 goto out; 1602 } 1603 1604 btfile = VTOF(hfsmp->hfs_attribute_vp); 1605 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL); 1606 if (result && (result != btNotFound)) { 1607 goto out; 1608 } 1609 1610 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL); 1611 /* If no next record was found or fileID for next record did not match, 1612 * no more attributes exist for this fileID 1613 */ 1614 if ((result && (result == btNotFound)) || (key->fileID != fileID)) { 1615 result = 0; 1616 } else { 1617 result = EEXIST; 1618 } 1619 1620out: 1621 if (iterator) { 1622 FREE(iterator, M_TEMP); 1623 } 1624 return result; 1625} 1626 1627 1628/* 1629 * Remove all the records for a given attribute. 1630 * 1631 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr. 1632 * - A transaction must have been started. 1633 * - The Attribute b-tree file must be locked exclusive. 1634 * - The Allocation Bitmap file must be locked exclusive. 1635 * - The iterator key must be initialized. 1636 */ 1637int 1638remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator) 1639{ 1640 struct filefork *btfile; 1641 FSBufferDescriptor btdata; 1642 HFSPlusAttrRecord attrdata; /* 90 bytes */ 1643 u_int16_t datasize; 1644 int result; 1645 1646 btfile = VTOF(hfsmp->hfs_attribute_vp); 1647 1648 btdata.bufferAddress = &attrdata; 1649 btdata.itemSize = sizeof(attrdata); 1650 btdata.itemCount = 1; 1651 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 1652 if (result) { 1653 goto exit; /* no records. */ 1654 } 1655 /* 1656 * Free the blocks from extent based attributes. 1657 * 1658 * Note that the block references (btree records) are removed 1659 * before releasing the blocks in the allocation bitmap. 1660 */ 1661 if (attrdata.recordType == kHFSPlusAttrForkData) { 1662 int totalblks; 1663 int extentblks; 1664 u_int32_t *keystartblk; 1665 1666 if (datasize < sizeof(HFSPlusAttrForkData)) { 1667 printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize, sizeof(HFSPlusAttrForkData)); 1668 } 1669 totalblks = attrdata.forkData.theFork.totalBlocks; 1670 1671 /* Process the first 8 extents. */ 1672 extentblks = count_extent_blocks(totalblks, attrdata.forkData.theFork.extents); 1673 if (extentblks > totalblks) 1674 panic("hfs: remove_attribute_records: corruption..."); 1675 if (BTDeleteRecord(btfile, iterator) == 0) { 1676 free_attr_blks(hfsmp, extentblks, attrdata.forkData.theFork.extents); 1677 } 1678 totalblks -= extentblks; 1679 keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock; 1680 1681 /* Process any overflow extents. */ 1682 while (totalblks) { 1683 *keystartblk += (u_int32_t)extentblks; 1684 1685 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL); 1686 if (result || 1687 (attrdata.recordType != kHFSPlusAttrExtents) || 1688 (datasize < sizeof(HFSPlusAttrExtents))) { 1689 printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n", 1690 hfsmp->vcbVN, MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks); 1691 result = ENOATTR; 1692 break; /* break from while */ 1693 } 1694 /* Process the next 8 extents. */ 1695 extentblks = count_extent_blocks(totalblks, attrdata.overflowExtents.extents); 1696 if (extentblks > totalblks) 1697 panic("hfs: remove_attribute_records: corruption..."); 1698 if (BTDeleteRecord(btfile, iterator) == 0) { 1699 free_attr_blks(hfsmp, extentblks, attrdata.overflowExtents.extents); 1700 } 1701 totalblks -= extentblks; 1702 } 1703 } else { 1704 result = BTDeleteRecord(btfile, iterator); 1705 } 1706 (void) BTFlushPath(btfile); 1707exit: 1708 return (result == btNotFound ? ENOATTR : MacToVFSError(result)); 1709} 1710 1711 1712/* 1713 * Retrieve the list of extended attribute names. 1714 */ 1715int 1716hfs_vnop_listxattr(struct vnop_listxattr_args *ap) 1717/* 1718 struct vnop_listxattr_args { 1719 struct vnodeop_desc *a_desc; 1720 vnode_t a_vp; 1721 uio_t a_uio; 1722 size_t *a_size; 1723 int a_options; 1724 vfs_context_t a_context; 1725*/ 1726{ 1727 struct vnode *vp = ap->a_vp; 1728 struct cnode *cp = VTOC(vp); 1729 struct hfsmount *hfsmp; 1730 uio_t uio = ap->a_uio; 1731 struct BTreeIterator * iterator = NULL; 1732 struct filefork *btfile; 1733 struct listattr_callback_state state; 1734 user_addr_t user_start = 0; 1735 user_size_t user_len = 0; 1736 int lockflags; 1737 int result; 1738 u_int8_t finderinfo[32]; 1739 1740 1741 if (VNODE_IS_RSRC(vp)) { 1742 return (EPERM); 1743 } 1744 1745#if HFS_COMPRESSION 1746 int compressed = hfs_file_is_compressed(cp, 1); /* 1 == don't take the cnode lock */ 1747#endif /* HFS_COMPRESSION */ 1748 1749 hfsmp = VTOHFS(vp); 1750 *ap->a_size = 0; 1751 1752 /* 1753 * Take the truncate lock; this serializes us against the ioctl 1754 * to truncate data & reset the decmpfs state 1755 * in the compressed file handler. 1756 */ 1757 hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT); 1758 1759 /* Now the regular cnode lock (shared) */ 1760 if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) { 1761 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT); 1762 return (result); 1763 } 1764 1765 /* 1766 * Make a copy of the cnode's finderinfo to a local so we can 1767 * zero out the date added field. Also zero out the private type/creator 1768 * for symlinks. 1769 */ 1770 bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo)); 1771 hfs_zero_hidden_fields (cp, finderinfo); 1772 1773 /* Don't expose a symlink's private type/creator. */ 1774 if (vnode_islnk(vp)) { 1775 struct FndrFileInfo *fip; 1776 1777 fip = (struct FndrFileInfo *)&finderinfo; 1778 fip->fdType = 0; 1779 fip->fdCreator = 0; 1780 } 1781 1782 1783 /* If Finder Info is non-empty then export it's name. */ 1784 if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) { 1785 if (uio == NULL) { 1786 *ap->a_size += sizeof(XATTR_FINDERINFO_NAME); 1787 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) { 1788 result = ERANGE; 1789 goto exit; 1790 } else { 1791 result = uiomove(XATTR_FINDERINFO_NAME, 1792 sizeof(XATTR_FINDERINFO_NAME), uio); 1793 if (result) 1794 goto exit; 1795 } 1796 } 1797 /* If Resource Fork is non-empty then export it's name. */ 1798 if (S_ISREG(cp->c_mode) && hfs_has_rsrc(cp)) { 1799#if HFS_COMPRESSION 1800 if ((ap->a_options & XATTR_SHOWCOMPRESSION) || 1801 !compressed || 1802 !decmpfs_hides_rsrc(ap->a_context, VTOCMP(vp)) 1803 ) 1804#endif /* HFS_COMPRESSION */ 1805 { 1806 if (uio == NULL) { 1807 *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME); 1808 } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) { 1809 result = ERANGE; 1810 goto exit; 1811 } else { 1812 result = uiomove(XATTR_RESOURCEFORK_NAME, 1813 sizeof(XATTR_RESOURCEFORK_NAME), uio); 1814 if (result) 1815 goto exit; 1816 } 1817 } 1818 } 1819 /* 1820 * Standard HFS only supports native FinderInfo and Resource Forks. 1821 * Return at this point. 1822 */ 1823 if (hfsmp->hfs_flags & HFS_STANDARD) { 1824 result = 0; 1825 goto exit; 1826 } 1827 /* Bail if we don't have any extended attributes. */ 1828 if ((hfsmp->hfs_attribute_vp == NULL) || 1829 (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) { 1830 result = 0; 1831 goto exit; 1832 } 1833 btfile = VTOF(hfsmp->hfs_attribute_vp); 1834 1835 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1836 if (iterator == NULL) { 1837 result = ENOMEM; 1838 goto exit; 1839 } 1840 bzero(iterator, sizeof(*iterator)); 1841 result = hfs_buildattrkey(cp->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key); 1842 if (result) 1843 goto exit; 1844 1845 /* 1846 * Lock the user's buffer here so that we won't fault on 1847 * it in uiomove while holding the attributes b-tree lock. 1848 */ 1849 if (uio && uio_isuserspace(uio)) { 1850 user_start = uio_curriovbase(uio); 1851 user_len = uio_curriovlen(uio); 1852 1853 if ((result = vslock(user_start, user_len)) != 0) { 1854 user_start = 0; 1855 goto exit; 1856 } 1857 } 1858 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 1859 1860 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL); 1861 if (result && result != btNotFound) { 1862 hfs_systemfile_unlock(hfsmp, lockflags); 1863 goto exit; 1864 } 1865 1866 state.fileID = cp->c_fileid; 1867 state.result = 0; 1868 state.uio = uio; 1869 state.size = 0; 1870#if HFS_COMPRESSION 1871 state.showcompressed = !compressed || ap->a_options & XATTR_SHOWCOMPRESSION; 1872 state.ctx = ap->a_context; 1873 state.vp = vp; 1874#endif /* HFS_COMPRESSION */ 1875 1876 /* 1877 * Process entries starting just after iterator->key. 1878 */ 1879 result = BTIterateRecords(btfile, kBTreeNextRecord, iterator, 1880 (IterateCallBackProcPtr)listattr_callback, &state); 1881 hfs_systemfile_unlock(hfsmp, lockflags); 1882 if (uio == NULL) { 1883 *ap->a_size += state.size; 1884 } 1885 1886 if (state.result || result == btNotFound) 1887 result = state.result; 1888 1889exit: 1890 if (user_start) { 1891 vsunlock(user_start, user_len, TRUE); 1892 } 1893 if (iterator) { 1894 FREE(iterator, M_TEMP); 1895 } 1896 hfs_unlock(cp); 1897 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT); 1898 1899 return MacToVFSError(result); 1900} 1901 1902 1903/* 1904 * Callback - called for each attribute record 1905 */ 1906static int 1907listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state) 1908{ 1909 char attrname[XATTR_MAXNAMELEN + 1]; 1910 ssize_t bytecount; 1911 int result; 1912 1913 if (state->fileID != key->fileID) { 1914 state->result = 0; 1915 return (0); /* stop */ 1916 } 1917 /* 1918 * Skip over non-primary keys 1919 */ 1920 if (key->startBlock != 0) { 1921 return (1); /* continue */ 1922 } 1923 1924 /* Convert the attribute name into UTF-8. */ 1925 result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar), 1926 (u_int8_t *)attrname, (size_t *)&bytecount, sizeof(attrname), '/', 0); 1927 if (result) { 1928 state->result = result; 1929 return (0); /* stop */ 1930 } 1931 bytecount++; /* account for null termination char */ 1932 1933 if (xattr_protected(attrname)) 1934 return (1); /* continue */ 1935 1936#if HFS_COMPRESSION 1937 if (!state->showcompressed && decmpfs_hides_xattr(state->ctx, VTOCMP(state->vp), attrname) ) 1938 return 1; /* continue */ 1939#endif /* HFS_COMPRESSION */ 1940 1941 if (state->uio == NULL) { 1942 state->size += bytecount; 1943 } else { 1944 if (bytecount > uio_resid(state->uio)) { 1945 state->result = ERANGE; 1946 return (0); /* stop */ 1947 } 1948 result = uiomove((caddr_t) attrname, bytecount, state->uio); 1949 if (result) { 1950 state->result = result; 1951 return (0); /* stop */ 1952 } 1953 } 1954 return (1); /* continue */ 1955} 1956 1957/* 1958 * Remove all the attributes from a cnode. 1959 * 1960 * This function creates/ends its own transaction so that each 1961 * attribute is deleted in its own transaction (to avoid having 1962 * a transaction grow too large). 1963 * 1964 * This function takes the necessary locks on the attribute 1965 * b-tree file and the allocation (bitmap) file. 1966 */ 1967int 1968hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid) 1969{ 1970 BTreeIterator *iterator = NULL; 1971 HFSPlusAttrKey *key; 1972 struct filefork *btfile; 1973 int result, lockflags; 1974 1975 if (hfsmp->hfs_attribute_vp == NULL) { 1976 return (0); 1977 } 1978 btfile = VTOF(hfsmp->hfs_attribute_vp); 1979 1980 MALLOC(iterator, BTreeIterator *, sizeof(BTreeIterator), M_TEMP, M_WAITOK); 1981 if (iterator == NULL) { 1982 return (ENOMEM); 1983 } 1984 bzero(iterator, sizeof(BTreeIterator)); 1985 key = (HFSPlusAttrKey *)&iterator->key; 1986 1987 /* Loop until there are no more attributes for this file id */ 1988 for(;;) { 1989 if (hfs_start_transaction(hfsmp) != 0) { 1990 result = EINVAL; 1991 goto exit; 1992 } 1993 1994 /* Lock the attribute b-tree and the allocation (bitmap) files */ 1995 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK); 1996 1997 /* 1998 * Go to first possible attribute key/record pair 1999 */ 2000 (void) hfs_buildattrkey(fileid, NULL, key); 2001 result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL); 2002 if (result || key->fileID != fileid) { 2003 hfs_systemfile_unlock(hfsmp, lockflags); 2004 hfs_end_transaction(hfsmp); 2005 goto exit; 2006 } 2007 result = remove_attribute_records(hfsmp, iterator); 2008 2009#if HFS_XATTR_VERBOSE 2010 if (result) { 2011 printf("hfs_removeallattr: unexpected err %d\n", result); 2012 } 2013#endif 2014 hfs_systemfile_unlock(hfsmp, lockflags); 2015 hfs_end_transaction(hfsmp); 2016 if (result) 2017 break; 2018 } 2019exit: 2020 FREE(iterator, M_TEMP); 2021 return (result == btNotFound ? 0: MacToVFSError(result)); 2022} 2023 2024__private_extern__ 2025void 2026hfs_xattr_init(struct hfsmount * hfsmp) 2027{ 2028 /* 2029 * If there isn't an attributes b-tree then create one. 2030 */ 2031 if (!(hfsmp->hfs_flags & HFS_STANDARD) && 2032 (hfsmp->hfs_attribute_vp == NULL) && 2033 !(hfsmp->hfs_flags & HFS_READ_ONLY)) { 2034 (void) hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE, 2035 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE)); 2036 } 2037 if (hfsmp->hfs_attribute_vp) 2038 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp); 2039} 2040 2041/* 2042 * Enable/Disable volume attributes stored as EA for root file system. 2043 * Supported attributes are - 2044 * 1. Extent-based Extended Attributes 2045 */ 2046int 2047hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state) 2048{ 2049 struct BTreeIterator * iterator = NULL; 2050 struct filefork *btfile; 2051 int lockflags; 2052 int result; 2053 2054 if (hfsmp->hfs_flags & HFS_STANDARD) { 2055 return (ENOTSUP); 2056 } 2057 if (xattrtype != HFS_SET_XATTREXTENTS_STATE) { 2058 return EINVAL; 2059 } 2060 2061 /* 2062 * If there isn't an attributes b-tree then create one. 2063 */ 2064 if (hfsmp->hfs_attribute_vp == NULL) { 2065 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE, 2066 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE)); 2067 if (result) { 2068 return (result); 2069 } 2070 } 2071 2072 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 2073 if (iterator == NULL) { 2074 return (ENOMEM); 2075 } 2076 bzero(iterator, sizeof(*iterator)); 2077 2078 /* 2079 * Build a b-tree key. 2080 * We use the root's parent id (1) to hold this volume attribute. 2081 */ 2082 (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME, 2083 (HFSPlusAttrKey *)&iterator->key); 2084 2085 /* Start a transaction for our changes. */ 2086 if (hfs_start_transaction(hfsmp) != 0) { 2087 result = EINVAL; 2088 goto exit; 2089 } 2090 btfile = VTOF(hfsmp->hfs_attribute_vp); 2091 2092 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); 2093 2094 if (state == 0) { 2095 /* Remove the attribute. */ 2096 result = BTDeleteRecord(btfile, iterator); 2097 if (result == btNotFound) 2098 result = 0; 2099 } else { 2100 FSBufferDescriptor btdata; 2101 HFSPlusAttrData attrdata; 2102 u_int16_t datasize; 2103 2104 datasize = sizeof(attrdata); 2105 btdata.bufferAddress = &attrdata; 2106 btdata.itemSize = datasize; 2107 btdata.itemCount = 1; 2108 attrdata.recordType = kHFSPlusAttrInlineData; 2109 attrdata.reserved[0] = 0; 2110 attrdata.reserved[1] = 0; 2111 attrdata.attrSize = 2; 2112 attrdata.attrData[0] = 0; 2113 attrdata.attrData[1] = 0; 2114 2115 /* Insert the attribute. */ 2116 result = BTInsertRecord(btfile, iterator, &btdata, datasize); 2117 if (result == btExists) 2118 result = 0; 2119 } 2120 (void) BTFlushPath(btfile); 2121 2122 hfs_systemfile_unlock(hfsmp, lockflags); 2123 2124 /* Finish the transaction of our changes. */ 2125 hfs_end_transaction(hfsmp); 2126 2127 /* Update the state in the mount point */ 2128 hfs_lock_mount (hfsmp); 2129 if (state == 0) { 2130 hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS; 2131 } else { 2132 hfsmp->hfs_flags |= HFS_XATTR_EXTENTS; 2133 } 2134 hfs_unlock_mount (hfsmp); 2135 2136exit: 2137 if (iterator) { 2138 FREE(iterator, M_TEMP); 2139 } 2140 return MacToVFSError(result); 2141} 2142 2143 2144/* 2145 * hfs_attrkeycompare - compare two attribute b-tree keys. 2146 * 2147 * The name portion of the key is compared using a 16-bit binary comparison. 2148 * This is called from the b-tree code. 2149 */ 2150__private_extern__ 2151int 2152hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey) 2153{ 2154 u_int32_t searchFileID, trialFileID; 2155 int result; 2156 2157 searchFileID = searchKey->fileID; 2158 trialFileID = trialKey->fileID; 2159 result = 0; 2160 2161 if (searchFileID > trialFileID) { 2162 ++result; 2163 } else if (searchFileID < trialFileID) { 2164 --result; 2165 } else { 2166 u_int16_t * str1 = &searchKey->attrName[0]; 2167 u_int16_t * str2 = &trialKey->attrName[0]; 2168 int length1 = searchKey->attrNameLen; 2169 int length2 = trialKey->attrNameLen; 2170 u_int16_t c1, c2; 2171 int length; 2172 2173 if (length1 < length2) { 2174 length = length1; 2175 --result; 2176 } else if (length1 > length2) { 2177 length = length2; 2178 ++result; 2179 } else { 2180 length = length1; 2181 } 2182 2183 while (length--) { 2184 c1 = *(str1++); 2185 c2 = *(str2++); 2186 2187 if (c1 > c2) { 2188 result = 1; 2189 break; 2190 } 2191 if (c1 < c2) { 2192 result = -1; 2193 break; 2194 } 2195 } 2196 if (result) 2197 return (result); 2198 /* 2199 * Names are equal; compare startBlock 2200 */ 2201 if (searchKey->startBlock == trialKey->startBlock) { 2202 return (0); 2203 } else { 2204 return (searchKey->startBlock < trialKey->startBlock ? -1 : 1); 2205 } 2206 } 2207 2208 return result; 2209} 2210 2211 2212/* 2213 * hfs_buildattrkey - build an Attribute b-tree key 2214 */ 2215__private_extern__ 2216int 2217hfs_buildattrkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key) 2218{ 2219 int result = 0; 2220 size_t unicodeBytes = 0; 2221 2222 if (attrname != NULL) { 2223 /* 2224 * Convert filename from UTF-8 into Unicode 2225 */ 2226 result = utf8_decodestr((const u_int8_t *)attrname, strlen(attrname), key->attrName, 2227 &unicodeBytes, sizeof(key->attrName), 0, 0); 2228 if (result) { 2229 if (result != ENAMETOOLONG) 2230 result = EINVAL; /* name has invalid characters */ 2231 return (result); 2232 } 2233 key->attrNameLen = unicodeBytes / sizeof(UniChar); 2234 key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes; 2235 } else { 2236 key->attrNameLen = 0; 2237 key->keyLength = kHFSPlusAttrKeyMinimumLength; 2238 } 2239 key->pad = 0; 2240 key->fileID = fileID; 2241 key->startBlock = 0; 2242 2243 return (0); 2244 } 2245 2246/* 2247 * getnodecount - calculate starting node count for attributes b-tree. 2248 */ 2249static int 2250getnodecount(struct hfsmount *hfsmp, size_t nodesize) 2251{ 2252 u_int64_t freebytes; 2253 u_int64_t calcbytes; 2254 2255 /* 2256 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB. 2257 * 10.5: Attempt to be as big as the catalog clump size. 2258 * 2259 * Use no more than 10 % of the remaining free space. 2260 */ 2261 freebytes = (u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize; 2262 2263 calcbytes = MIN(hfsmp->hfs_catalog_cp->c_datafork->ff_size / 5, 20 * 1024 * 1024); 2264 2265 calcbytes = MAX(calcbytes, hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize); 2266 2267 calcbytes = MIN(calcbytes, freebytes / 10); 2268 2269 return (MAX(2, (int)(calcbytes / nodesize))); 2270} 2271 2272 2273/* 2274 * getmaxinlineattrsize - calculate maximum inline attribute size. 2275 * 2276 * This yields 3,802 bytes for an 8K node size. 2277 */ 2278static size_t 2279getmaxinlineattrsize(struct vnode * attrvp) 2280{ 2281 struct BTreeInfoRec btinfo; 2282 size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE; 2283 size_t maxsize; 2284 2285 if (attrvp != NULL) { 2286 (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT); 2287 if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0) 2288 nodesize = btinfo.nodeSize; 2289 hfs_unlock(VTOC(attrvp)); 2290 } 2291 maxsize = nodesize; 2292 maxsize -= sizeof(BTNodeDescriptor); /* minus node descriptor */ 2293 maxsize -= 3 * sizeof(u_int16_t); /* minus 3 index slots */ 2294 maxsize /= 2; /* 2 key/rec pairs minumum */ 2295 maxsize -= sizeof(HFSPlusAttrKey); /* minus maximum key size */ 2296 maxsize -= sizeof(HFSPlusAttrData) - 2; /* minus data header */ 2297 maxsize &= 0xFFFFFFFE; /* multiple of 2 bytes */ 2298 2299 return (maxsize); 2300} 2301 2302/* 2303 * Initialize vnode for attribute data I/O. 2304 * 2305 * On success, 2306 * - returns zero 2307 * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp 2308 * - an iocount is taken on the attrdata vnode which exists 2309 * for the entire duration of the mount. It is only dropped 2310 * during unmount 2311 * - the attrdata cnode is not locked 2312 * 2313 * On failure, 2314 * - returns non-zero value 2315 * - the caller does not have to worry about any locks or references 2316 */ 2317int init_attrdata_vnode(struct hfsmount *hfsmp) 2318{ 2319 vnode_t vp; 2320 int result = 0; 2321 struct cat_desc cat_desc; 2322 struct cat_attr cat_attr; 2323 struct cat_fork cat_fork; 2324 int newvnode_flags = 0; 2325 2326 bzero(&cat_desc, sizeof(cat_desc)); 2327 cat_desc.cd_parentcnid = kHFSRootParentID; 2328 cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename; 2329 cat_desc.cd_namelen = strlen(hfs_attrdatafilename); 2330 cat_desc.cd_cnid = kHFSAttributeDataFileID; 2331 /* Tag vnode as system file, note that we can still use cluster I/O */ 2332 cat_desc.cd_flags |= CD_ISMETA; 2333 2334 bzero(&cat_attr, sizeof(cat_attr)); 2335 cat_attr.ca_linkcount = 1; 2336 cat_attr.ca_mode = S_IFREG; 2337 cat_attr.ca_fileid = cat_desc.cd_cnid; 2338 cat_attr.ca_blocks = hfsmp->totalBlocks; 2339 2340 /* 2341 * The attribute data file is a virtual file that spans the 2342 * entire file system space. 2343 * 2344 * Each extent-based attribute occupies a unique portion of 2345 * in this virtual file. The cluster I/O is done using actual 2346 * allocation block offsets so no additional mapping is needed 2347 * for the VNOP_BLOCKMAP call. 2348 * 2349 * This approach allows the attribute data to be cached without 2350 * incurring the high cost of using a separate vnode per attribute. 2351 * 2352 * Since we need to acquire the attribute b-tree file lock anyways, 2353 * the virtual file doesn't introduce any additional serialization. 2354 */ 2355 bzero(&cat_fork, sizeof(cat_fork)); 2356 cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize; 2357 cat_fork.cf_blocks = hfsmp->totalBlocks; 2358 cat_fork.cf_extents[0].startBlock = 0; 2359 cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks; 2360 2361 result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr, 2362 &cat_fork, &vp, &newvnode_flags); 2363 if (result == 0) { 2364 hfsmp->hfs_attrdata_vp = vp; 2365 hfs_unlock(VTOC(vp)); 2366 } 2367 return (result); 2368} 2369 2370/* 2371 * Read an extent based attribute. 2372 */ 2373static int 2374read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents) 2375{ 2376 vnode_t evp = hfsmp->hfs_attrdata_vp; 2377 int bufsize; 2378 int64_t iosize; 2379 int attrsize; 2380 int blksize; 2381 int i; 2382 int result = 0; 2383 2384 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT); 2385 2386 bufsize = (int)uio_resid(uio); 2387 attrsize = (int)datasize; 2388 blksize = (int)hfsmp->blockSize; 2389 2390 /* 2391 * Read the attribute data one extent at a time. 2392 * For the typical case there is only one extent. 2393 */ 2394 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) { 2395 iosize = extents[i].blockCount * blksize; 2396 iosize = MIN(iosize, attrsize); 2397 iosize = MIN(iosize, bufsize); 2398 uio_setresid(uio, iosize); 2399 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize); 2400 2401 result = cluster_read(evp, uio, VTOF(evp)->ff_size, IO_SYNC | IO_UNIT); 2402 2403#if HFS_XATTR_VERBOSE 2404 printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n", 2405 iosize, extents[i].startBlock, extents[i].blockCount, result); 2406#endif 2407 if (result) 2408 break; 2409 attrsize -= iosize; 2410 bufsize -= iosize; 2411 } 2412 uio_setresid(uio, bufsize); 2413 uio_setoffset(uio, datasize); 2414 2415 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT); 2416 return (result); 2417} 2418 2419/* 2420 * Write an extent based attribute. 2421 */ 2422static int 2423write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents) 2424{ 2425 vnode_t evp = hfsmp->hfs_attrdata_vp; 2426 off_t filesize; 2427 int bufsize; 2428 int attrsize; 2429 int64_t iosize; 2430 int blksize; 2431 int i; 2432 int result = 0; 2433 2434 hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT); 2435 2436 bufsize = uio_resid(uio); 2437 attrsize = (int) datasize; 2438 blksize = (int) hfsmp->blockSize; 2439 filesize = VTOF(evp)->ff_size; 2440 2441 /* 2442 * Write the attribute data one extent at a time. 2443 */ 2444 for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) { 2445 iosize = extents[i].blockCount * blksize; 2446 iosize = MIN(iosize, attrsize); 2447 iosize = MIN(iosize, bufsize); 2448 uio_setresid(uio, iosize); 2449 uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize); 2450 2451 result = cluster_write(evp, uio, filesize, filesize, filesize, 2452 (off_t) 0, IO_SYNC | IO_UNIT); 2453#if HFS_XATTR_VERBOSE 2454 printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n", 2455 iosize, extents[i].startBlock, extents[i].blockCount, result); 2456#endif 2457 if (result) 2458 break; 2459 attrsize -= iosize; 2460 bufsize -= iosize; 2461 } 2462 uio_setresid(uio, bufsize); 2463 uio_setoffset(uio, datasize); 2464 2465 hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT); 2466 return (result); 2467} 2468 2469/* 2470 * Allocate blocks for an extent based attribute. 2471 */ 2472static int 2473alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks) 2474{ 2475 int blkcnt; 2476 int startblk; 2477 int lockflags; 2478 int i; 2479 int maxextents; 2480 int result = 0; 2481 2482 startblk = hfsmp->hfs_metazone_end; 2483 blkcnt = howmany(attrsize, hfsmp->blockSize); 2484 if (blkcnt > (int)hfs_freeblks(hfsmp, 0)) { 2485 return (ENOSPC); 2486 } 2487 *blocks = blkcnt; 2488 maxextents = extentbufsize / sizeof(HFSPlusExtentDescriptor); 2489 2490 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK); 2491 2492 for (i = 0; (blkcnt > 0) && (i < maxextents); i++) { 2493 /* Try allocating and see if we find something decent */ 2494 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0, 2495 &extents[i].startBlock, &extents[i].blockCount); 2496 /* 2497 * If we couldn't find anything, then re-try the allocation but allow 2498 * journal flushes. 2499 */ 2500 if (result == dskFulErr) { 2501 result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, HFS_ALLOC_FLUSHTXN, 2502 &extents[i].startBlock, &extents[i].blockCount); 2503 } 2504 2505 2506#if HFS_XATTR_VERBOSE 2507 printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n", 2508 blkcnt, extents[i].startBlock, extents[i].blockCount, result); 2509#endif 2510 if (result) { 2511 extents[i].startBlock = 0; 2512 extents[i].blockCount = 0; 2513 break; 2514 } 2515 blkcnt -= extents[i].blockCount; 2516 startblk = extents[i].startBlock + extents[i].blockCount; 2517 } 2518 /* 2519 * If it didn't fit in the extents buffer then bail. 2520 */ 2521 if (blkcnt) { 2522 result = ENOSPC; 2523 2524#if HFS_XATTR_VERBOSE 2525 printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt); 2526#endif 2527 for (; i >= 0; i--) { 2528 if ((blkcnt = extents[i].blockCount) != 0) { 2529 (void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt, 0); 2530 extents[i].startBlock = 0; 2531 extents[i].blockCount = 0; 2532 } 2533 } 2534 } 2535 2536 hfs_systemfile_unlock(hfsmp, lockflags); 2537 return MacToVFSError(result); 2538} 2539 2540/* 2541 * Release blocks from an extent based attribute. 2542 */ 2543static void 2544free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents) 2545{ 2546 vnode_t evp = hfsmp->hfs_attrdata_vp; 2547 int remblks = blkcnt; 2548 int lockflags; 2549 int i; 2550 2551 lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK); 2552 2553 for (i = 0; (remblks > 0) && (extents[i].blockCount != 0); i++) { 2554 if (extents[i].blockCount > (u_int32_t)blkcnt) { 2555#if HFS_XATTR_VERBOSE 2556 printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n", 2557 extents[i].startBlock, extents[i].blockCount); 2558#endif 2559 extents[i].blockCount = 0; 2560 continue; 2561 } 2562 if (extents[i].startBlock == 0) { 2563 break; 2564 } 2565 (void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount, 0); 2566 remblks -= extents[i].blockCount; 2567 extents[i].startBlock = 0; 2568 extents[i].blockCount = 0; 2569 2570#if HFS_XATTR_VERBOSE 2571 printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n", 2572 extents[i].startBlock, extents[i].blockCount); 2573#endif 2574 /* Discard any resident pages for this block range. */ 2575 if (evp) { 2576 off_t start, end; 2577 2578 start = (u_int64_t)extents[i].startBlock * (u_int64_t)hfsmp->blockSize; 2579 end = start + (u_int64_t)extents[i].blockCount * (u_int64_t)hfsmp->blockSize; 2580 (void) ubc_msync(hfsmp->hfs_attrdata_vp, start, end, &start, UBC_INVALIDATE); 2581 } 2582 } 2583 2584 hfs_systemfile_unlock(hfsmp, lockflags); 2585} 2586 2587static int 2588has_overflow_extents(HFSPlusForkData *forkdata) 2589{ 2590 u_int32_t blocks; 2591 2592 if (forkdata->extents[7].blockCount == 0) 2593 return (0); 2594 2595 blocks = forkdata->extents[0].blockCount + 2596 forkdata->extents[1].blockCount + 2597 forkdata->extents[2].blockCount + 2598 forkdata->extents[3].blockCount + 2599 forkdata->extents[4].blockCount + 2600 forkdata->extents[5].blockCount + 2601 forkdata->extents[6].blockCount + 2602 forkdata->extents[7].blockCount; 2603 2604 return (forkdata->totalBlocks > blocks); 2605} 2606 2607static int 2608count_extent_blocks(int maxblks, HFSPlusExtentRecord extents) 2609{ 2610 int blocks; 2611 int i; 2612 2613 for (i = 0, blocks = 0; i < kHFSPlusExtentDensity; ++i) { 2614 /* Ignore obvious bogus extents. */ 2615 if (extents[i].blockCount > (u_int32_t)maxblks) 2616 continue; 2617 if (extents[i].startBlock == 0 || extents[i].blockCount == 0) 2618 break; 2619 blocks += extents[i].blockCount; 2620 } 2621 return (blocks); 2622} 2623 2624