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