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