1/* 2 * Copyright (c) 2004-2008 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce 30 * support for mandatory and extensible security protections. This notice 31 * is included in support of clause 2.2 (b) of the Apple Public License, 32 * Version 2.0. 33 */ 34 35#include <sys/param.h> 36 37#include <sys/fcntl.h> 38#include <sys/fsevents.h> 39#include <sys/kernel.h> 40#include <sys/kauth.h> 41#include <sys/malloc.h> 42#include <sys/mount_internal.h> 43#include <sys/namei.h> 44#include <sys/proc_internal.h> 45#include <sys/stat.h> 46#include <sys/uio.h> 47#include <sys/utfconv.h> 48#include <sys/vnode.h> 49#include <sys/vnode_internal.h> 50#include <sys/xattr.h> 51 52#include <libkern/OSByteOrder.h> 53#include <vm/vm_kern.h> 54 55#if CONFIG_MACF 56#include <security/mac_framework.h> 57#endif 58 59 60#if NAMEDSTREAMS 61 62/* 63 * Cast to 'unsigned int' loses precision - hope that's OK... 64 */ 65#define MAKE_SHADOW_NAME(VP, NAME) \ 66 snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%x%08x%x", (unsigned int)(VP), (VP)->v_id, (unsigned int)(VP)->v_data); 67 68static vnode_t shadow_dvp; /* tmp directory to hold stream shadow files */ 69static int shadow_vid; 70static int shadow_sequence; 71 72 73static int default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context); 74 75static int default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context); 76 77static int default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context); 78 79static int getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, int *creator, vfs_context_t context); 80 81static int get_shadow_dir(vnode_t *sdvpp, vfs_context_t context); 82 83#endif 84 85 86/* 87 * Default xattr support routines. 88 */ 89 90static int default_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, 91 vfs_context_t context); 92 93 94 95/* 96 * Retrieve the data of an extended attribute. 97 */ 98int 99vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, 100 int options, vfs_context_t context) 101{ 102 int error; 103 104 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { 105 return (EPERM); 106 } 107#if NAMEDSTREAMS 108 /* getxattr calls are not allowed for streams. */ 109 if (vp->v_flag & VISNAMEDSTREAM) { 110 error = EPERM; 111 goto out; 112 } 113#endif 114 /* 115 * Non-kernel request need extra checks performed. 116 * 117 * The XATTR_NOSECURITY flag implies a kernel request. 118 */ 119 if (!(options & XATTR_NOSECURITY)) { 120#if CONFIG_MACF 121 error = mac_vnode_check_getextattr(context, vp, name, uio); 122 if (error) 123 goto out; 124#endif /* MAC */ 125 if ((error = xattr_validatename(name))) { 126 goto out; 127 } 128 if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context))) { 129 goto out; 130 } 131 /* The offset can only be non-zero for resource forks. */ 132 if (uio != NULL && uio_offset(uio) != 0 && 133 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 134 error = EINVAL; 135 goto out; 136 } 137 } 138 139 /* The offset can only be non-zero for resource forks. */ 140 if (uio != NULL && uio_offset(uio) != 0 && 141 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 142 error = EINVAL; 143 goto out; 144 } 145 146 error = VNOP_GETXATTR(vp, name, uio, size, options, context); 147 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { 148 /* 149 * A filesystem may keep some EAs natively and return ENOTSUP for others. 150 * SMB returns ENOTSUP for finderinfo and resource forks. 151 */ 152 error = default_getxattr(vp, name, uio, size, options, context); 153 } 154out: 155 return (error); 156} 157 158/* 159 * Set the data of an extended attribute. 160 */ 161int 162vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context) 163{ 164 int error; 165 166 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { 167 return (EPERM); 168 } 169#if NAMEDSTREAMS 170 /* setxattr calls are not allowed for streams. */ 171 if (vp->v_flag & VISNAMEDSTREAM) { 172 error = EPERM; 173 goto out; 174 } 175#endif 176 if ((options & (XATTR_REPLACE|XATTR_CREATE)) == (XATTR_REPLACE|XATTR_CREATE)) { 177 return (EINVAL); 178 } 179 if ((error = xattr_validatename(name))) { 180 return (error); 181 } 182 if (!(options & XATTR_NOSECURITY)) { 183#if CONFIG_MACF 184 error = mac_vnode_check_setextattr(context, vp, name, uio); 185 if (error) 186 goto out; 187#endif /* MAC */ 188 error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context); 189 if (error) 190 goto out; 191 } 192 /* The offset can only be non-zero for resource forks. */ 193 if (uio_offset(uio) != 0 && 194 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0 ) { 195 error = EINVAL; 196 goto out; 197 } 198 199 error = VNOP_SETXATTR(vp, name, uio, options, context); 200#ifdef DUAL_EAS 201 /* 202 * An EJUSTRETURN is from a filesystem which keeps this xattr 203 * natively as well as in a dot-underscore file. In this case the 204 * EJUSTRETURN means the filesytem has done nothing, but identifies the 205 * EA as one which may be represented natively and/or in a DU, and 206 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in 207 * in vn_setxattr can we do the getxattrs needed to ascertain whether 208 * the XATTR_{CREATE,REPLACE} should yield an error. 209 */ 210 if (error == EJUSTRETURN) { 211 int native = 0, dufile = 0; 212 size_t sz; /* not used */ 213 214 native = VNOP_GETXATTR(vp, name, NULL, &sz, 0, context) ? 0 : 1; 215 dufile = default_getxattr(vp, name, NULL, &sz, 0, context) ? 0 : 1; 216 if (options & XATTR_CREATE && (native || dufile)) { 217 error = EEXIST; 218 goto out; 219 } 220 if (options & XATTR_REPLACE && !(native || dufile)) { 221 error = ENOATTR; 222 goto out; 223 } 224 /* 225 * Having determined no CREATE/REPLACE error should result, we 226 * zero those bits, so both backing stores get written to. 227 */ 228 options &= ~(XATTR_CREATE | XATTR_REPLACE); 229 error = VNOP_SETXATTR(vp, name, uio, options, context); 230 /* the mainline path here is to have error==ENOTSUP ... */ 231 } 232#endif /* DUAL_EAS */ 233 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { 234 /* 235 * A filesystem may keep some EAs natively and return ENOTSUP for others. 236 * SMB returns ENOTSUP for finderinfo and resource forks. 237 */ 238 error = default_setxattr(vp, name, uio, options, context); 239 } 240#if CONFIG_MACF 241 if ((error == 0) && !(options & XATTR_NOSECURITY) && 242 (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL)) 243 mac_vnode_label_update_extattr(vnode_mount(vp), vp, name); 244#endif 245out: 246 return (error); 247} 248 249/* 250 * Remove an extended attribute. 251 */ 252int 253vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context) 254{ 255 int error; 256 257 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { 258 return (EPERM); 259 } 260#if NAMEDSTREAMS 261 /* removexattr calls are not allowed for streams. */ 262 if (vp->v_flag & VISNAMEDSTREAM) { 263 error = EPERM; 264 goto out; 265 } 266#endif 267 if ((error = xattr_validatename(name))) { 268 return (error); 269 } 270 if (!(options & XATTR_NOSECURITY)) { 271#if CONFIG_MACF 272 error = mac_vnode_check_deleteextattr(context, vp, name); 273 if (error) 274 goto out; 275#endif /* MAC */ 276 error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context); 277 if (error) 278 goto out; 279 } 280 error = VNOP_REMOVEXATTR(vp, name, options, context); 281 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { 282 /* 283 * A filesystem may keep some EAs natively and return ENOTSUP for others. 284 * SMB returns ENOTSUP for finderinfo and resource forks. 285 */ 286 error = default_removexattr(vp, name, options, context); 287#ifdef DUAL_EAS 288 } else if (error == EJUSTRETURN) { 289 /* 290 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well 291 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove 292 * a native xattr, so failure to find it in a DU file during 293 * default_removexattr should not be considered an error. 294 */ 295 error = default_removexattr(vp, name, options, context); 296 if (error == ENOATTR) 297 error = 0; 298#endif /* DUAL_EAS */ 299 } 300#if CONFIG_MACF 301 if ((error == 0) && !(options & XATTR_NOSECURITY) && 302 (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL)) 303 mac_vnode_label_update_extattr(vnode_mount(vp), vp, name); 304#endif 305out: 306 return (error); 307} 308 309/* 310 * Retrieve the list of extended attribute names. 311 */ 312int 313vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t context) 314{ 315 int error; 316 317 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { 318 return (EPERM); 319 } 320#if NAMEDSTREAMS 321 /* listxattr calls are not allowed for streams. */ 322 if (vp->v_flag & VISNAMEDSTREAM) { 323 return (EPERM); 324 } 325#endif 326 327 if (!(options & XATTR_NOSECURITY)) { 328#if CONFIG_MACF 329 error = mac_vnode_check_listextattr(context, vp); 330 if (error) 331 goto out; 332#endif /* MAC */ 333 334 error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context); 335 if (error) 336 goto out; 337 } 338 339 error = VNOP_LISTXATTR(vp, uio, size, options, context); 340 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { 341 /* 342 * A filesystem may keep some but not all EAs natively, in which case 343 * the native EA names will have been uiomove-d out (or *size updated) 344 * and the default_listxattr here will finish the job. Note SMB takes 345 * advantage of this for its finder-info and resource forks. 346 */ 347 error = default_listxattr(vp, uio, size, options, context); 348 } 349out: 350 return (error); 351} 352 353int 354xattr_validatename(const char *name) 355{ 356 int namelen; 357 358 if (name == NULL || name[0] == '\0') { 359 return (EINVAL); 360 } 361 namelen = strnlen(name, XATTR_MAXNAMELEN); 362 if (name[namelen] != '\0') 363 return (ENAMETOOLONG); 364 365 if (utf8_validatestr((const unsigned char *)name, namelen) != 0) 366 return (EINVAL); 367 368 return (0); 369} 370 371 372/* 373 * Determine whether an EA is a protected system attribute. 374 */ 375int 376xattr_protected(const char *attrname) 377{ 378 return(!strncmp(attrname, "com.apple.system.", 17)); 379} 380 381 382#if NAMEDSTREAMS 383/* 384 * Obtain a named stream from vnode vp. 385 */ 386errno_t 387vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, int flags, vfs_context_t context) 388{ 389 int error; 390 391 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) 392 error = VNOP_GETNAMEDSTREAM(vp, svpp, name, op, flags, context); 393 else 394 error = default_getnamedstream(vp, svpp, name, op, context); 395 396 if (error == 0) { 397 uint32_t streamflags = VISNAMEDSTREAM; 398 vnode_t svp = *svpp; 399 400 if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) { 401 streamflags |= VISSHADOW; 402 } 403 404 /* Tag the vnode. */ 405 vnode_lock_spin(svp); 406 svp->v_flag |= streamflags; 407 vnode_unlock(svp); 408 /* Make the file its parent. 409 * Note: This parent link helps us distinguish vnodes for 410 * shadow stream files from vnodes for resource fork on file 411 * systems that support named streams natively (both have 412 * VISNAMEDSTREAM set) by allowing access to mount structure 413 * for checking MNTK_NAMED_STREAMS bit at many places in the code 414 */ 415 vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT); 416 } 417 418 return (error); 419} 420 421/* 422 * Make a named stream for vnode vp. 423 */ 424errno_t 425vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t context) 426{ 427 int error; 428 429 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) 430 error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context); 431 else 432 error = default_makenamedstream(vp, svpp, name, context); 433 434 if (error == 0) { 435 uint32_t streamflags = VISNAMEDSTREAM; 436 vnode_t svp = *svpp; 437 438 /* Tag the vnode. */ 439 if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) { 440 streamflags |= VISSHADOW; 441 } 442 443 /* Tag the vnode. */ 444 vnode_lock_spin(svp); 445 svp->v_flag |= streamflags; 446 vnode_unlock(svp); 447 448 /* Make the file its parent. 449 * Note: This parent link helps us distinguish vnodes for 450 * shadow stream files from vnodes for resource fork on file 451 * systems that support named streams natively (both have 452 * VISNAMEDSTREAM set) by allowing access to mount structure 453 * for checking MNTK_NAMED_STREAMS bit at many places in the code 454 */ 455 vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT); 456 } 457 return (error); 458} 459 460/* 461 * Remove a named stream from vnode vp. 462 */ 463errno_t 464vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t context) 465{ 466 int error; 467 468 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) 469 error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context); 470 else 471 error = default_removenamedstream(vp, name, context); 472 473 return (error); 474} 475 476#define NS_IOBUFSIZE (128 * 1024) 477 478/* 479 * Release a named stream shadow file. 480 * 481 * Note: This function is called from two places where we do not need 482 * to check if the vnode has any references held before deleting the 483 * shadow file. Once from vclean() when the vnode is being reclaimed 484 * and we do not hold any references on the vnode. Second time from 485 * default_getnamedstream() when we get an error during shadow stream 486 * file initialization so that other processes who are waiting for the 487 * shadow stream file initialization by the creator will get opportunity 488 * to create and initialize the file again. 489 */ 490errno_t 491vnode_relenamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) 492{ 493 vnode_t dvp; 494 struct componentname cn; 495 char tmpname[48]; 496 errno_t err; 497 498 cache_purge(svp); 499 500 vnode_lock(svp); 501 MAKE_SHADOW_NAME(vp, tmpname); 502 vnode_unlock(svp); 503 504 cn.cn_nameiop = DELETE; 505 cn.cn_flags = ISLASTCN; 506 cn.cn_context = context; 507 cn.cn_pnbuf = tmpname; 508 cn.cn_pnlen = sizeof(tmpname); 509 cn.cn_nameptr = cn.cn_pnbuf; 510 cn.cn_namelen = strlen(tmpname); 511 512 /* Obtain the vnode for the shadow files directory. */ 513 err = get_shadow_dir(&dvp, context); 514 if (err != 0) { 515 return err; 516 } 517 518 (void) VNOP_REMOVE(dvp, svp, &cn, 0, context); 519 vnode_put(dvp); 520 521 return (0); 522} 523 524/* 525 * Flush a named stream shadow file. 526 */ 527errno_t 528vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) 529{ 530 struct vnode_attr va; 531 uio_t auio = NULL; 532 caddr_t bufptr = NULL; 533 size_t bufsize = 0; 534 size_t offset; 535 size_t iosize; 536 size_t datasize; 537 int error; 538 539 VATTR_INIT(&va); 540 VATTR_WANTED(&va, va_data_size); 541 if (VNOP_GETATTR(svp, &va, context) != 0 || 542 !VATTR_IS_SUPPORTED(&va, va_data_size)) { 543 return (0); 544 } 545 datasize = va.va_data_size; 546 if ((datasize == 0)) { 547 (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context); 548 return (0); 549 } 550 551 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE); 552 if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) { 553 return (ENOMEM); 554 } 555 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); 556 offset = 0; 557 558 /* 559 * Copy the shadow stream file data into the resource fork. 560 */ 561 error = VNOP_OPEN(svp, 0, context); 562 if (error) { 563 printf("vnode_flushnamedstream: err %d opening file\n", error); 564 goto out; 565 } 566 while (offset < datasize) { 567 iosize = MIN(datasize - offset, iosize); 568 569 uio_reset(auio, offset, UIO_SYSSPACE32, UIO_READ); 570 uio_addiov(auio, (uintptr_t)bufptr, iosize); 571 error = VNOP_READ(svp, auio, 0, context); 572 if (error) { 573 break; 574 } 575 /* Since there's no truncate xattr we must remove the resource fork. */ 576 if (offset == 0) { 577 error = default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context); 578 if ((error != 0) && (error != ENOATTR)) { 579 break; 580 } 581 } 582 uio_reset(auio, offset, UIO_SYSSPACE32, UIO_WRITE); 583 uio_addiov(auio, (uintptr_t)bufptr, iosize); 584 error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context); 585 if (error) { 586 break; 587 } 588 offset += iosize; 589 } 590 (void) VNOP_CLOSE(svp, 0, context); 591out: 592 if (bufptr) { 593 kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize); 594 } 595 if (auio) { 596 uio_free(auio); 597 } 598 return (error); 599} 600 601 602static int 603getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, 604 int *creator, vfs_context_t context) 605{ 606 vnode_t dvp = NULLVP; 607 vnode_t svp = NULLVP; 608 struct componentname cn; 609 struct vnode_attr va; 610 char tmpname[48]; 611 size_t datasize = 0; 612 int error = 0; 613 614 *creator = 0; 615 616 /* Establish a unique file name. */ 617 MAKE_SHADOW_NAME(vp, tmpname); 618 bzero(&cn, sizeof(cn)); 619 cn.cn_nameiop = LOOKUP; 620 cn.cn_flags = ISLASTCN; 621 cn.cn_context = context; 622 cn.cn_pnbuf = tmpname; 623 cn.cn_pnlen = sizeof(tmpname); 624 cn.cn_nameptr = cn.cn_pnbuf; 625 cn.cn_namelen = strlen(tmpname); 626 627 /* Pick up uid, gid, mode and date from original file. */ 628 VATTR_INIT(&va); 629 VATTR_WANTED(&va, va_uid); 630 VATTR_WANTED(&va, va_gid); 631 VATTR_WANTED(&va, va_mode); 632 VATTR_WANTED(&va, va_create_time); 633 VATTR_WANTED(&va, va_modify_time); 634 if (VNOP_GETATTR(vp, &va, context) != 0 || 635 !VATTR_IS_SUPPORTED(&va, va_uid) || 636 !VATTR_IS_SUPPORTED(&va, va_gid) || 637 !VATTR_IS_SUPPORTED(&va, va_mode)) { 638 va.va_uid = KAUTH_UID_NONE; 639 va.va_gid = KAUTH_GID_NONE; 640 va.va_mode = S_IRUSR | S_IWUSR; 641 } 642 va.va_vaflags = VA_EXCLUSIVE; 643 VATTR_SET(&va, va_type, VREG); 644 /* We no longer change the access, but we still hide it. */ 645 VATTR_SET(&va, va_flags, UF_HIDDEN); 646 647 /* Obtain the vnode for the shadow files directory. */ 648 if (get_shadow_dir(&dvp, context) != 0) { 649 error = ENOTDIR; 650 goto out; 651 } 652 if (!makestream) { 653 /* See if someone else already has it open. */ 654 if (VNOP_LOOKUP(dvp, &svp, &cn, context) == 0) { 655 /* Double check existence by asking for size. */ 656 VATTR_INIT(&va); 657 VATTR_WANTED(&va, va_data_size); 658 if (VNOP_GETATTR(svp, &va, context) == 0 && 659 VATTR_IS_SUPPORTED(&va, va_data_size)) { 660 goto out; /* OK to use. */ 661 } 662 } 663 664 /* Otherwise make sure the resource fork data exists. */ 665 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &datasize, 666 XATTR_NOSECURITY, context); 667 /* 668 * To maintain binary compatibility with legacy Carbon 669 * emulated resource fork support, if the resource fork 670 * doesn't exist but the Finder Info does, then act as 671 * if an empty resource fork is present (see 4724359). 672 */ 673 if ((error == ENOATTR) && 674 (vn_getxattr(vp, XATTR_FINDERINFO_NAME, NULL, &datasize, 675 XATTR_NOSECURITY, context) == 0)) { 676 datasize = 0; 677 error = 0; 678 } else { 679 if (error) { 680 goto out; 681 } 682 683 /* If the resource fork exists, its size is expected to be non-zero. */ 684 if (datasize == 0) { 685 error = ENOATTR; 686 goto out; 687 } 688 } 689 } 690 /* Create the shadow stream file. */ 691 error = VNOP_CREATE(dvp, &svp, &cn, &va, context); 692 if (error == 0) { 693 *creator = 1; 694 } else if ((error == EEXIST) && !makestream) { 695 error = VNOP_LOOKUP(dvp, &svp, &cn, context); 696 } 697out: 698 if (dvp) { 699 vnode_put(dvp); 700 } 701 if (error) { 702 /* On errors, clean up shadow stream file. */ 703 if (svp) { 704 vnode_put(svp); 705 svp = NULLVP; 706 } 707 } 708 *svpp = svp; 709 if (rsrcsize) { 710 *rsrcsize = datasize; 711 } 712 return (error); 713} 714 715 716static int 717default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context) 718{ 719 vnode_t svp = NULLVP; 720 uio_t auio = NULL; 721 caddr_t bufptr = NULL; 722 size_t bufsize = 0; 723 size_t datasize = 0; 724 int creator; 725 int error; 726 727 /* 728 * Only the "com.apple.ResourceFork" stream is supported here. 729 */ 730 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 731 *svpp = NULLVP; 732 return (ENOATTR); 733 } 734retry: 735 /* 736 * Obtain a shadow file for the resource fork I/O. 737 */ 738 error = getshadowfile(vp, &svp, 0, &datasize, &creator, context); 739 if (error) { 740 *svpp = NULLVP; 741 return (error); 742 } 743 744 /* 745 * The creator of the shadow file provides its file data, 746 * all other threads should wait until its ready. 747 */ 748 if (!creator) { 749 vnode_lock(svp); 750 if (svp->v_flag & VISNAMEDSTREAM) { 751 /* data is ready, go use it */ 752 vnode_unlock(svp); 753 goto out; 754 } else { 755 /* its not ready, wait for it (sleep using v_parent as channel) */ 756 msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP, 757 "getnamedstream", NULL); 758 vnode_put(svp); 759 svp = NULLVP; 760 goto retry; 761 } 762 } 763 764 /* 765 * Copy the real resource fork data into shadow stream file. 766 */ 767 if (op == NS_OPEN && datasize != 0) { 768 size_t offset; 769 size_t iosize; 770 771 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE); 772 if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) { 773 error = ENOMEM; 774 goto out; 775 } 776 777 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); 778 offset = 0; 779 780 error = VNOP_OPEN(svp, 0, context); 781 if (error) { 782 goto out; 783 } 784 while (offset < datasize) { 785 size_t tmpsize; 786 787 iosize = MIN(datasize - offset, iosize); 788 789 uio_reset(auio, offset, UIO_SYSSPACE32, UIO_READ); 790 uio_addiov(auio, (uintptr_t)bufptr, iosize); 791 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize, 792 XATTR_NOSECURITY, context); 793 if (error) { 794 break; 795 } 796 797 uio_reset(auio, offset, UIO_SYSSPACE32, UIO_WRITE); 798 uio_addiov(auio, (uintptr_t)bufptr, iosize); 799 error = VNOP_WRITE(svp, auio, 0, context); 800 if (error) { 801 break; 802 } 803 offset += iosize; 804 } 805 (void) VNOP_CLOSE(svp, 0, context); 806 } 807out: 808 /* Wake up anyone waiting for svp file content */ 809 if (creator) { 810 if (error == 0) { 811 vnode_lock(svp); 812 svp->v_flag |= VISNAMEDSTREAM; 813 wakeup((caddr_t)&svp->v_parent); 814 vnode_unlock(svp); 815 } else { 816 /* On post create errors, get rid of the shadow file. This 817 * way, if there is another process waiting for initialization 818 * of the shadow file by the current process, it will wake up 819 * and retry by creating and initializing the shadow file again. 820 */ 821 (void) vnode_relenamedstream(vp, svp, context); 822 wakeup((caddr_t)&svp->v_parent); 823 } 824 } 825 826 if (bufptr) { 827 kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize); 828 } 829 if (auio) { 830 uio_free(auio); 831 } 832 if (error) { 833 /* On errors, clean up shadow stream file. */ 834 if (svp) { 835 vnode_put(svp); 836 svp = NULLVP; 837 } 838 } 839 *svpp = svp; 840 return (error); 841} 842 843static int 844default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context) 845{ 846 int creator; 847 int error; 848 849 /* 850 * Only the "com.apple.ResourceFork" stream is supported here. 851 */ 852 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 853 *svpp = NULLVP; 854 return (ENOATTR); 855 } 856 error = getshadowfile(vp, svpp, 1, NULL, &creator, context); 857 858 /* 859 * Wake up any waiters over in default_getnamedstream(). 860 */ 861 if ((error == 0) && (*svpp != NULL) && creator) { 862 vnode_t svp = *svpp; 863 864 vnode_lock(svp); 865 svp->v_flag |= VISNAMEDSTREAM; 866 /* Wakeup any waiters on the v_parent channel */ 867 wakeup((caddr_t)&svp->v_parent); 868 vnode_unlock(svp); 869 } 870 return (error); 871} 872 873static int 874default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context) 875{ 876 /* 877 * Only the "com.apple.ResourceFork" stream is supported here. 878 */ 879 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { 880 return (ENOATTR); 881 } 882 /* 883 * XXX - what about other opened instances? 884 */ 885 return default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context); 886} 887 888static int 889get_shadow_dir(vnode_t *sdvpp, vfs_context_t context) 890{ 891 vnode_t dvp = NULLVP; 892 vnode_t sdvp = NULLVP; 893 struct componentname cn; 894 struct vnode_attr va; 895 char tmpname[48]; 896 uint32_t tmp_fsid; 897 int error; 898 899 /* Check if we've already created it. */ 900 if (shadow_dvp != NULLVP) { 901 if ((error = vnode_getwithvid(shadow_dvp, shadow_vid))) { 902 shadow_dvp = NULLVP; 903 } else { 904 *sdvpp = shadow_dvp; 905 return (0); 906 } 907 } 908 909 /* Obtain the vnode for "/tmp" directory. */ 910 if (vnode_lookup("/tmp", 0, &dvp, context) != 0) { 911 error = ENOTSUP; 912 goto out; 913 } 914 915 /* Create the shadow stream directory. */ 916 snprintf(tmpname, sizeof(tmpname), ".vfs_rsrc_streams_%x%x", 917 (unsigned int)rootvnode, shadow_sequence); 918 bzero(&cn, sizeof(cn)); 919 cn.cn_nameiop = LOOKUP; 920 cn.cn_flags = ISLASTCN; 921 cn.cn_context = context; 922 cn.cn_pnbuf = tmpname; 923 cn.cn_pnlen = sizeof(tmpname); 924 cn.cn_nameptr = cn.cn_pnbuf; 925 cn.cn_namelen = strlen(tmpname); 926 927 /* 928 * owned by root, only readable by root, hidden 929 */ 930 VATTR_INIT(&va); 931 VATTR_SET(&va, va_uid, 0); 932 VATTR_SET(&va, va_gid, 0); 933 VATTR_SET(&va, va_mode, S_IRUSR | S_IXUSR); 934 VATTR_SET(&va, va_type, VDIR); 935 VATTR_SET(&va, va_flags, UF_HIDDEN); 936 va.va_vaflags = VA_EXCLUSIVE; 937 938 error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, context); 939 940 /* 941 * There can be only one winner for an exclusive create. 942 */ 943 if (error == 0) { 944 /* Take a long term ref to keep this dir around. */ 945 error = vnode_ref(sdvp); 946 if (error == 0) { 947 shadow_dvp = sdvp; 948 shadow_vid = sdvp->v_id; 949 } 950 } else if (error == EEXIST) { 951 /* loser has to look up directory */ 952 error = VNOP_LOOKUP(dvp, &sdvp, &cn, context); 953 if (error == 0) { 954 /* Make sure its in fact a directory */ 955 if (sdvp->v_type != VDIR) { 956 goto baddir; 957 } 958 /* Obtain the fsid for /tmp directory */ 959 VATTR_INIT(&va); 960 VATTR_WANTED(&va, va_fsid); 961 if (VNOP_GETATTR(dvp, &va, context) != 0 || 962 !VATTR_IS_SUPPORTED(&va, va_fsid)) { 963 goto baddir; 964 } 965 tmp_fsid = va.va_fsid; 966 967 VATTR_INIT(&va); 968 VATTR_WANTED(&va, va_uid); 969 VATTR_WANTED(&va, va_gid); 970 VATTR_WANTED(&va, va_mode); 971 VATTR_WANTED(&va, va_fsid); 972 VATTR_WANTED(&va, va_dirlinkcount); 973 VATTR_WANTED(&va, va_acl); 974 /* Provide defaults for attrs that may not be supported */ 975 va.va_dirlinkcount = 1; 976 va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE; 977 978 if (VNOP_GETATTR(sdvp, &va, context) != 0 || 979 !VATTR_IS_SUPPORTED(&va, va_uid) || 980 !VATTR_IS_SUPPORTED(&va, va_gid) || 981 !VATTR_IS_SUPPORTED(&va, va_mode) || 982 !VATTR_IS_SUPPORTED(&va, va_fsid)) { 983 goto baddir; 984 } 985 /* 986 * Make sure its what we want: 987 * - owned by root 988 * - not writable by anyone 989 * - on same file system as /tmp 990 * - not a hard-linked directory 991 * - no ACLs (they might grant write access) 992 */ 993 if ((va.va_uid != 0) || (va.va_gid != 0) || 994 (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) || 995 (va.va_fsid != tmp_fsid) || 996 (va.va_dirlinkcount != 1) || 997 (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) { 998 goto baddir; 999 } 1000 } 1001 } 1002out: 1003 if (dvp) { 1004 vnode_put(dvp); 1005 } 1006 if (error) { 1007 /* On errors, clean up shadow stream directory. */ 1008 if (sdvp) { 1009 vnode_put(sdvp); 1010 sdvp = NULLVP; 1011 } 1012 } 1013 *sdvpp = sdvp; 1014 return (error); 1015 1016baddir: 1017 /* This is not the dir we're looking for, move along */ 1018 ++shadow_sequence; /* try something else next time */ 1019 error = ENOTDIR; 1020 goto out; 1021} 1022#endif 1023 1024 1025 1026/* 1027 * Default Implementation (Non-native EA) 1028 */ 1029 1030 1031/* 1032 Typical "._" AppleDouble Header File layout: 1033 ------------------------------------------------------------ 1034 MAGIC 0x00051607 1035 VERSION 0x00020000 1036 FILLER 0 1037 COUNT 2 1038 .-- AD ENTRY[0] Finder Info Entry (must be first) 1039 .--+-- AD ENTRY[1] Resource Fork Entry (must be last) 1040 | '-> FINDER INFO 1041 | ///////////// Fixed Size Data (32 bytes) 1042 | EXT ATTR HDR 1043 | ///////////// 1044 | ATTR ENTRY[0] --. 1045 | ATTR ENTRY[1] --+--. 1046 | ATTR ENTRY[2] --+--+--. 1047 | ... | | | 1048 | ATTR ENTRY[N] --+--+--+--. 1049 | ATTR DATA 0 <-' | | | 1050 | //////////// | | | 1051 | ATTR DATA 1 <----' | | 1052 | ///////////// | | 1053 | ATTR DATA 2 <-------' | 1054 | ///////////// | 1055 | ... | 1056 | ATTR DATA N <----------' 1057 | ///////////// 1058 | Attribute Free Space 1059 | 1060 '----> RESOURCE FORK 1061 ///////////// Variable Sized Data 1062 ///////////// 1063 ///////////// 1064 ///////////// 1065 ///////////// 1066 ///////////// 1067 ... 1068 ///////////// 1069 1070 ------------------------------------------------------------ 1071 1072 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are 1073 stored as part of the Finder Info. The length in the Finder 1074 Info AppleDouble entry includes the length of the extended 1075 attribute header, attribute entries, and attribute data. 1076*/ 1077 1078 1079/* 1080 * On Disk Data Structures 1081 * 1082 * Note: Motorola 68K alignment and big-endian. 1083 * 1084 * See RFC 1740 for additional information about the AppleDouble file format. 1085 * 1086 */ 1087 1088#define ADH_MAGIC 0x00051607 1089#define ADH_VERSION 0x00020000 1090#define ADH_MACOSX "Mac OS X " 1091 1092/* 1093 * AppleDouble Entry ID's 1094 */ 1095#define AD_DATA 1 /* Data fork */ 1096#define AD_RESOURCE 2 /* Resource fork */ 1097#define AD_REALNAME 3 /* File�s name on home file system */ 1098#define AD_COMMENT 4 /* Standard Mac comment */ 1099#define AD_ICONBW 5 /* Mac black & white icon */ 1100#define AD_ICONCOLOR 6 /* Mac color icon */ 1101#define AD_UNUSED 7 /* Not used */ 1102#define AD_FILEDATES 8 /* File dates; create, modify, etc */ 1103#define AD_FINDERINFO 9 /* Mac Finder info & extended info */ 1104#define AD_MACINFO 10 /* Mac file info, attributes, etc */ 1105#define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */ 1106#define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */ 1107#define AD_AFPNAME 13 /* Short name on AFP server */ 1108#define AD_AFPINFO 14 /* AFP file info, attrib., etc */ 1109#define AD_AFPDIRID 15 /* AFP directory ID */ 1110#define AD_ATTRIBUTES AD_FINDERINFO 1111 1112 1113#define ATTR_FILE_PREFIX "._" 1114#define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */ 1115 1116#define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */ 1117 1118/* Implementation Limits */ 1119#define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */ 1120#define ATTR_MAX_HDR_SIZE 65536 1121/* 1122 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header 1123 * size supported (including the attribute entries). All of 1124 * the attribute entries must reside within this limit. If 1125 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE 1126 * boundry, then all of the attribute data I/O is performed 1127 * separately from the attribute header I/O. 1128 * 1129 * In particular, all of the attr_entry structures must lie 1130 * completely within the first ATTR_MAX_HDR_SIZE bytes of the 1131 * AppleDouble file. However, the attribute data (i.e. the 1132 * contents of the extended attributes) may extend beyond the 1133 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this 1134 * limit is to allow the implementation to optimize by reading 1135 * the first ATTR_MAX_HDR_SIZE bytes of the file. 1136 */ 1137 1138 1139#pragma options align=mac68k 1140 1141#define FINDERINFOSIZE 32 1142 1143typedef struct apple_double_entry { 1144 u_int32_t type; /* entry type: see list, 0 invalid */ 1145 u_int32_t offset; /* entry data offset from the beginning of the file. */ 1146 u_int32_t length; /* entry data length in bytes. */ 1147} apple_double_entry_t; 1148 1149 1150typedef struct apple_double_header { 1151 u_int32_t magic; /* == ADH_MAGIC */ 1152 u_int32_t version; /* format version: 2 = 0x00020000 */ 1153 u_int32_t filler[4]; 1154 u_int16_t numEntries; /* number of entries which follow */ 1155 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */ 1156 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */ 1157 u_int8_t pad[2]; /* get better alignment inside attr_header */ 1158} apple_double_header_t; 1159 1160#define ADHDRSIZE (4+4+16+2) 1161 1162/* Entries are aligned on 4 byte boundaries */ 1163typedef struct attr_entry { 1164 u_int32_t offset; /* file offset to data */ 1165 u_int32_t length; /* size of attribute data */ 1166 u_int16_t flags; 1167 u_int8_t namelen; 1168 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */ 1169} attr_entry_t; 1170 1171 1172/* Header + entries must fit into 64K. Data may extend beyond 64K. */ 1173typedef struct attr_header { 1174 apple_double_header_t appledouble; 1175 u_int32_t magic; /* == ATTR_HDR_MAGIC */ 1176 u_int32_t debug_tag; /* for debugging == file id of owning file */ 1177 u_int32_t total_size; /* file offset of end of attribute header + entries + data */ 1178 u_int32_t data_start; /* file offset to attribute data area */ 1179 u_int32_t data_length; /* length of attribute data area */ 1180 u_int32_t reserved[3]; 1181 u_int16_t flags; 1182 u_int16_t num_attrs; 1183} attr_header_t; 1184 1185 1186/* Empty Resource Fork Header */ 1187typedef struct rsrcfork_header { 1188 u_int32_t fh_DataOffset; 1189 u_int32_t fh_MapOffset; 1190 u_int32_t fh_DataLength; 1191 u_int32_t fh_MapLength; 1192 u_int8_t systemData[112]; 1193 u_int8_t appData[128]; 1194 u_int32_t mh_DataOffset; 1195 u_int32_t mh_MapOffset; 1196 u_int32_t mh_DataLength; 1197 u_int32_t mh_MapLength; 1198 u_int32_t mh_Next; 1199 u_int16_t mh_RefNum; 1200 u_int8_t mh_Attr; 1201 u_int8_t mh_InMemoryAttr; 1202 u_int16_t mh_Types; 1203 u_int16_t mh_Names; 1204 u_int16_t typeCount; 1205} rsrcfork_header_t; 1206 1207#define RF_FIRST_RESOURCE 256 1208#define RF_NULL_MAP_LENGTH 30 1209#define RF_EMPTY_TAG "This resource fork intentionally left blank " 1210 1211#pragma options align=reset 1212 1213/* Runtime information about the attribute file. */ 1214typedef struct attr_info { 1215 vfs_context_t context; 1216 vnode_t filevp; 1217 size_t filesize; 1218 size_t iosize; 1219 u_int8_t *rawdata; 1220 size_t rawsize; /* minimum of filesize or ATTR_MAX_HDR_SIZE */ 1221 apple_double_header_t *filehdr; 1222 apple_double_entry_t *finderinfo; 1223 apple_double_entry_t *rsrcfork; 1224 attr_header_t *attrhdr; 1225 attr_entry_t *attr_entry; 1226 u_int8_t readonly; 1227 u_int8_t emptyfinderinfo; 1228} attr_info_t; 1229 1230 1231#define ATTR_SETTING 1 1232 1233#define ATTR_ALIGN 3L /* Use four-byte alignment */ 1234 1235#define ATTR_ENTRY_LENGTH(namelen) \ 1236 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN)) 1237 1238#define ATTR_NEXT(ae) \ 1239 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen)) 1240 1241#define ATTR_VALID(ae, ai) \ 1242 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize)) 1243 1244#define SWAP16(x) OSSwapBigToHostInt16((x)) 1245#define SWAP32(x) OSSwapBigToHostInt32((x)) 1246#define SWAP64(x) OSSwapBigToHostInt64((x)) 1247 1248 1249static u_int32_t emptyfinfo[8] = {0}; 1250 1251 1252/* 1253 * Local support routines 1254 */ 1255static void close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context); 1256 1257static int open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context); 1258 1259static int create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context); 1260 1261static int remove_xattrfile(vnode_t xvp, vfs_context_t context); 1262 1263static int get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context); 1264 1265static void rel_xattrinfo(attr_info_t *ainfop); 1266 1267static int write_xattrinfo(attr_info_t *ainfop); 1268 1269static void init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr); 1270 1271static int lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context); 1272 1273static int unlock_xattrfile(vnode_t xvp, vfs_context_t context); 1274 1275 1276#if BYTE_ORDER == LITTLE_ENDIAN 1277 static void swap_adhdr(apple_double_header_t *adh); 1278 static void swap_attrhdr(attr_header_t *ah, attr_info_t* info); 1279 1280#else 1281#define swap_adhdr(x) 1282#define swap_attrhdr(x, y) 1283#endif 1284 1285static int check_and_swap_attrhdr(attr_header_t *ah, attr_info_t* ainfop); 1286static int shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context); 1287static int shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context); 1288 1289 1290/* 1291 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer 1292 * is in big endian (as it would exist on disk). Verifies the following: 1293 * - magic field 1294 * - version field 1295 * - number of entries 1296 * - that each entry fits within the file size 1297 * 1298 * If the header is invalid, ENOATTR is returned. 1299 * 1300 * NOTE: Does not attempt to validate the extended attributes header that 1301 * may be embedded in the Finder Info entry. 1302 */ 1303static int check_and_swap_apple_double_header(attr_info_t *ainfop) 1304{ 1305 int i, j; 1306 u_int32_t header_end; 1307 u_int32_t entry_end; 1308 size_t rawsize; 1309 apple_double_header_t *header; 1310 1311 rawsize = ainfop->rawsize; 1312 header = (apple_double_header_t *) ainfop->rawdata; 1313 1314 /* Is the file big enough to contain an AppleDouble header? */ 1315 if (rawsize < offsetof(apple_double_header_t, entries)) 1316 return ENOATTR; 1317 1318 /* Swap the AppleDouble header fields to native order */ 1319 header->magic = SWAP32(header->magic); 1320 header->version = SWAP32(header->version); 1321 header->numEntries = SWAP16(header->numEntries); 1322 1323 /* Sanity check the AppleDouble header fields */ 1324 if (header->magic != ADH_MAGIC || 1325 header->version != ADH_VERSION || 1326 header->numEntries < 1 || 1327 header->numEntries > 15) { 1328 return ENOATTR; 1329 } 1330 1331 /* Calculate where the entries[] array ends */ 1332 header_end = offsetof(apple_double_header_t, entries) + 1333 header->numEntries * sizeof(apple_double_entry_t); 1334 1335 /* Is the file big enough to contain the AppleDouble entries? */ 1336 if (rawsize < header_end) { 1337 return ENOATTR; 1338 } 1339 1340 /* Swap and sanity check each AppleDouble entry */ 1341 for (i=0; i<header->numEntries; i++) { 1342 /* Swap the per-entry fields to native order */ 1343 header->entries[i].type = SWAP32(header->entries[i].type); 1344 header->entries[i].offset = SWAP32(header->entries[i].offset); 1345 header->entries[i].length = SWAP32(header->entries[i].length); 1346 1347 entry_end = header->entries[i].offset + header->entries[i].length; 1348 1349 /* 1350 * Does the entry's content start within the header itself, 1351 * did the addition overflow, or does the entry's content 1352 * extend past the end of the file? 1353 */ 1354 if (header->entries[i].offset < header_end || 1355 entry_end < header->entries[i].offset || 1356 entry_end > ainfop->filesize) { 1357 return ENOATTR; 1358 } 1359 1360 /* 1361 * Does the current entry's content overlap with a previous 1362 * entry's content? 1363 * 1364 * Yes, this is O(N**2), and there are more efficient algorithms 1365 * for testing pairwise overlap of N ranges when N is large. 1366 * But we have already ensured N < 16, and N is almost always 2. 1367 * So there's no point in using a more complex algorithm. 1368 */ 1369 1370 for (j=0; j<i; j++) { 1371 if (entry_end > header->entries[j].offset && 1372 header->entries[j].offset + header->entries[j].length > header->entries[i].offset) { 1373 return ENOATTR; 1374 } 1375 } 1376 } 1377 1378 return 0; 1379} 1380 1381 1382 1383/* 1384 * Retrieve the data of an extended attribute. 1385 */ 1386int 1387default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, 1388 __unused int options, vfs_context_t context) 1389{ 1390 vnode_t xvp = NULL; 1391 attr_info_t ainfo; 1392 attr_header_t *header; 1393 attr_entry_t *entry; 1394 u_int8_t *attrdata; 1395 size_t datalen; 1396 int namelen; 1397 int isrsrcfork; 1398 int fileflags; 1399 int i; 1400 int error; 1401 1402 fileflags = FREAD; 1403 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 1404 isrsrcfork = 1; 1405 /* 1406 * Open the file locked (shared) since the Carbon 1407 * File Manager may have the Apple Double file open 1408 * and could be changing the resource fork. 1409 */ 1410 fileflags |= O_SHLOCK; 1411 } else { 1412 isrsrcfork = 0; 1413 } 1414 1415 if ((error = open_xattrfile(vp, fileflags, &xvp, context))) { 1416 return (error); 1417 } 1418 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { 1419 close_xattrfile(xvp, fileflags, context); 1420 return (error); 1421 } 1422 1423 /* Get the Finder Info. */ 1424 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 1425 1426 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) { 1427 error = ENOATTR; 1428 } else if (uio == NULL) { 1429 *size = FINDERINFOSIZE; 1430 error = 0; 1431 } else if (uio_offset(uio) != 0) { 1432 error = EINVAL; 1433 } else if (uio_resid(uio) < FINDERINFOSIZE) { 1434 error = ERANGE; 1435 } else { 1436 attrdata = (u_int8_t*)ainfo.filehdr + ainfo.finderinfo->offset; 1437 error = uiomove((caddr_t)attrdata, FINDERINFOSIZE, uio); 1438 } 1439 goto out; 1440 } 1441 1442 /* Read the Resource Fork. */ 1443 if (isrsrcfork) { 1444 if (!vnode_isreg(vp)) { 1445 error = EPERM; 1446 } else if (ainfo.rsrcfork == NULL) { 1447 error = ENOATTR; 1448 } else if (uio == NULL) { 1449 *size = (size_t)ainfo.rsrcfork->length; 1450 } else { 1451 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset); 1452 error = VNOP_READ(xvp, uio, 0, context); 1453 if (error == 0) 1454 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset); 1455 } 1456 goto out; 1457 } 1458 1459 if (ainfo.attrhdr == NULL || ainfo.attr_entry == NULL) { 1460 error = ENOATTR; 1461 goto out; 1462 } 1463 if (uio_offset(uio) != 0) { 1464 error = EINVAL; 1465 goto out; 1466 } 1467 error = ENOATTR; 1468 namelen = strlen(name) + 1; 1469 header = ainfo.attrhdr; 1470 entry = ainfo.attr_entry; 1471 /* 1472 * Search for attribute name in the header. 1473 */ 1474 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { 1475 if (strncmp((const char *)entry->name, name, namelen) == 0) { 1476 datalen = (size_t)entry->length; 1477 if (uio == NULL) { 1478 *size = datalen; 1479 error = 0; 1480 break; 1481 } 1482 if (uio_resid(uio) < (user_ssize_t)datalen) { 1483 error = ERANGE; 1484 break; 1485 } 1486 if (entry->offset + datalen < ATTR_MAX_HDR_SIZE) { 1487 attrdata = ((u_int8_t *)header + entry->offset); 1488 error = uiomove((caddr_t)attrdata, datalen, uio); 1489 } else { 1490 uio_setoffset(uio, entry->offset); 1491 error = VNOP_READ(xvp, uio, 0, context); 1492 uio_setoffset(uio, 0); 1493 } 1494 break; 1495 } 1496 entry = ATTR_NEXT(entry); 1497 } 1498out: 1499 rel_xattrinfo(&ainfo); 1500 close_xattrfile(xvp, fileflags, context); 1501 1502 return (error); 1503} 1504 1505/* 1506 * Set the data of an extended attribute. 1507 */ 1508int 1509default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context) 1510{ 1511 vnode_t xvp = NULL; 1512 attr_info_t ainfo; 1513 attr_header_t *header; 1514 attr_entry_t *entry; 1515 attr_entry_t *lastentry; 1516 u_int8_t *attrdata; 1517 size_t datalen; 1518 size_t entrylen; 1519 size_t datafreespace; 1520 int namelen; 1521 int found = 0; 1522 int i; 1523 int splitdata; 1524 int fileflags; 1525 int error; 1526 char finfo[FINDERINFOSIZE]; 1527 1528 datalen = uio_resid(uio); 1529 namelen = strlen(name) + 1; 1530 entrylen = ATTR_ENTRY_LENGTH(namelen); 1531 1532 /* 1533 * By convention, Finder Info that is all zeroes is equivalent to not 1534 * having a Finder Info EA. So if we're trying to set the Finder Info 1535 * to all zeroes, then delete it instead. If a file didn't have an 1536 * AppleDouble file before, this prevents creating an AppleDouble file 1537 * with no useful content. 1538 * 1539 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check 1540 * for all zeroes Finder Info before opening the AppleDouble file. 1541 * But if either of those options were specified, we need to open the 1542 * AppleDouble file to see whether there was already Finder Info (so we 1543 * can return an error if needed); this case is handled further below. 1544 * 1545 * NOTE: this copies the Finder Info data into the "finfo" local. 1546 */ 1547 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 1548 /* 1549 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags. 1550 * That means we probably have to open_xattrfile and get_xattrinfo. 1551 */ 1552 if (uio_offset(uio) != 0 || datalen != FINDERINFOSIZE) { 1553 return EINVAL; 1554 } 1555 error = uiomove(finfo, datalen, uio); 1556 if (error) 1557 return error; 1558 if ((options & (XATTR_CREATE|XATTR_REPLACE)) == 0 && 1559 bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) { 1560 error = default_removexattr(vp, name, 0, context); 1561 if (error == ENOATTR) 1562 error = 0; 1563 return error; 1564 } 1565 } 1566 1567start: 1568 /* 1569 * Open the file locked since setting an attribute 1570 * can change the layout of the Apple Double file. 1571 */ 1572 fileflags = FREAD | FWRITE | O_EXLOCK; 1573 if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xvp, context))) { 1574 return (error); 1575 } 1576 if ((error = get_xattrinfo(xvp, ATTR_SETTING, &ainfo, context))) { 1577 close_xattrfile(xvp, fileflags, context); 1578 return (error); 1579 } 1580 1581 /* Set the Finder Info. */ 1582 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 1583 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) { 1584 /* attr exists and "create" was specified? */ 1585 if (options & XATTR_CREATE) { 1586 error = EEXIST; 1587 goto out; 1588 } 1589 } else { 1590 /* attr doesn't exists and "replace" was specified? */ 1591 if (options & XATTR_REPLACE) { 1592 error = ENOATTR; 1593 goto out; 1594 } 1595 } 1596 if (options != 0 && bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) { 1597 /* 1598 * Setting the Finder Info to all zeroes is equivalent to 1599 * removing it. Close the xattr file and let 1600 * default_removexattr do the work (including deleting 1601 * the xattr file if there are no other xattrs). 1602 * 1603 * Note that we have to handle the case where the 1604 * Finder Info was already all zeroes, and we ignore 1605 * ENOATTR. 1606 * 1607 * The common case where options == 0 was handled above. 1608 */ 1609 rel_xattrinfo(&ainfo); 1610 close_xattrfile(xvp, fileflags, context); 1611 error = default_removexattr(vp, name, 0, context); 1612 if (error == ENOATTR) 1613 error = 0; 1614 return error; 1615 } 1616 if (ainfo.finderinfo) { 1617 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset; 1618 bcopy(finfo, attrdata, datalen); 1619 ainfo.iosize = sizeof(attr_header_t); 1620 error = write_xattrinfo(&ainfo); 1621 goto out; 1622 } 1623 error = ENOATTR; 1624 goto out; 1625 } 1626 1627 /* Write the Resource Fork. */ 1628 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 1629 u_int32_t endoffset; 1630 1631 if (!vnode_isreg(vp)) { 1632 error = EPERM; 1633 goto out; 1634 } 1635 if (ainfo.rsrcfork && ainfo.rsrcfork->length) { 1636 /* attr exists and "create" was specified? */ 1637 if (options & XATTR_CREATE) { 1638 error = EEXIST; 1639 goto out; 1640 } 1641 } else { 1642 /* attr doesn't exists and "replace" was specified? */ 1643 if (options & XATTR_REPLACE) { 1644 error = ENOATTR; 1645 goto out; 1646 } 1647 } 1648 endoffset = uio_resid(uio) + uio_offset(uio); /* new size */ 1649 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset); 1650 error = VNOP_WRITE(xvp, uio, 0, context); 1651 if (error) 1652 goto out; 1653 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset); 1654 if (endoffset > ainfo.rsrcfork->length) { 1655 ainfo.rsrcfork->length = endoffset; 1656 ainfo.iosize = sizeof(attr_header_t); 1657 error = write_xattrinfo(&ainfo); 1658 goto out; 1659 } 1660 goto out; 1661 } 1662 1663 if (datalen > ATTR_MAX_SIZE) { 1664 return (E2BIG); /* EINVAL instead ? */ 1665 } 1666 1667 if (ainfo.attrhdr == NULL) { 1668 error = ENOATTR; 1669 goto out; 1670 } 1671 header = ainfo.attrhdr; 1672 entry = ainfo.attr_entry; 1673 1674 /* Check if data area crosses the maximum header size. */ 1675 if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE) 1676 splitdata = 1; /* do data I/O separately */ 1677 else 1678 splitdata = 0; 1679 1680 /* 1681 * See if attribute already exists. 1682 */ 1683 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { 1684 if (strncmp((const char *)entry->name, name, namelen) == 0) { 1685 found = 1; 1686 break; 1687 } 1688 entry = ATTR_NEXT(entry); 1689 } 1690 1691 if (found) { 1692 if (options & XATTR_CREATE) { 1693 error = EEXIST; 1694 goto out; 1695 } 1696 if (datalen == entry->length) { 1697 if (splitdata) { 1698 uio_setoffset(uio, entry->offset); 1699 error = VNOP_WRITE(xvp, uio, 0, context); 1700 uio_setoffset(uio, 0); 1701 if (error) { 1702 printf("setxattr: VNOP_WRITE error %d\n", error); 1703 } 1704 } else { 1705 attrdata = (u_int8_t *)header + entry->offset; 1706 error = uiomove((caddr_t)attrdata, datalen, uio); 1707 if (error) 1708 goto out; 1709 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; 1710 error = write_xattrinfo(&ainfo); 1711 if (error) { 1712 printf("setxattr: write_xattrinfo error %d\n", error); 1713 } 1714 } 1715 goto out; 1716 } else { 1717 /* 1718 * Brute force approach - just remove old entry and set new entry. 1719 */ 1720 found = 0; 1721 rel_xattrinfo(&ainfo); 1722 close_xattrfile(xvp, fileflags, context); 1723 error = default_removexattr(vp, name, options, context); 1724 if (error) { 1725 return (error); 1726 } 1727 /* Clear XATTR_REPLACE option since we just removed the attribute. */ 1728 options &= ~XATTR_REPLACE; 1729 goto start; /* start over */ 1730 } 1731 1732 } 1733 1734 if (options & XATTR_REPLACE) { 1735 error = ENOATTR; /* nothing there to replace */ 1736 goto out; 1737 } 1738 /* Check if header size limit has been reached. */ 1739 if ((header->data_start + entrylen) > ATTR_MAX_HDR_SIZE) { 1740 error = ENOSPC; 1741 goto out; 1742 } 1743 1744 datafreespace = header->total_size - (header->data_start + header->data_length); 1745 1746 /* Check if we need more space. */ 1747 if ((datalen + entrylen) > datafreespace) { 1748 size_t growsize; 1749 1750 growsize = roundup((datalen + entrylen) - datafreespace, ATTR_BUF_SIZE); 1751 1752 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */ 1753 if (!splitdata && (header->total_size + growsize) > ATTR_MAX_HDR_SIZE) { 1754 growsize = ATTR_MAX_HDR_SIZE - header->total_size; 1755 } 1756 1757 ainfo.filesize += growsize; 1758 error = vnode_setsize(xvp, ainfo.filesize, 0, context); 1759 if (error) { 1760 printf("setxattr: VNOP_TRUNCATE error %d\n", error); 1761 } 1762 if (error) 1763 goto out; 1764 1765 /* 1766 * Move the resource fork out of the way. 1767 */ 1768 if (ainfo.rsrcfork) { 1769 if (ainfo.rsrcfork->length != 0) { 1770 shift_data_down(xvp, 1771 ainfo.rsrcfork->offset, 1772 ainfo.rsrcfork->length, 1773 growsize, context); 1774 } 1775 ainfo.rsrcfork->offset += growsize; 1776 } 1777 ainfo.finderinfo->length += growsize; 1778 header->total_size += growsize; 1779 } 1780 1781 /* Make space for a new entry. */ 1782 if (splitdata) { 1783 shift_data_down(xvp, 1784 header->data_start, 1785 header->data_length, 1786 entrylen, context); 1787 } else { 1788 bcopy((u_int8_t *)header + header->data_start, 1789 (u_int8_t *)header + header->data_start + entrylen, 1790 header->data_length); 1791 } 1792 header->data_start += entrylen; 1793 1794 /* Fix up entry data offsets. */ 1795 lastentry = entry; 1796 for (entry = ainfo.attr_entry; entry != lastentry && ATTR_VALID(entry, ainfo); entry = ATTR_NEXT(entry)) { 1797 entry->offset += entrylen; 1798 } 1799 1800 /* 1801 * If the attribute data area is entirely within 1802 * the header buffer, then just update the buffer, 1803 * otherwise we'll write it separately to the file. 1804 */ 1805 if (splitdata) { 1806 off_t offset; 1807 1808 /* Write new attribute data after the end of existing data. */ 1809 offset = header->data_start + header->data_length; 1810 uio_setoffset(uio, offset); 1811 error = VNOP_WRITE(xvp, uio, 0, context); 1812 uio_setoffset(uio, 0); 1813 if (error) { 1814 printf("setxattr: VNOP_WRITE error %d\n", error); 1815 goto out; 1816 } 1817 } else { 1818 attrdata = (u_int8_t *)header + header->data_start + header->data_length; 1819 1820 error = uiomove((caddr_t)attrdata, datalen, uio); 1821 if (error) { 1822 printf("setxattr: uiomove error %d\n", error); 1823 goto out; 1824 } 1825 } 1826 1827 /* Create the attribute entry. */ 1828 lastentry->length = datalen; 1829 lastentry->offset = header->data_start + header->data_length; 1830 lastentry->namelen = namelen; 1831 lastentry->flags = 0; 1832 bcopy(name, &lastentry->name[0], namelen); 1833 1834 /* Update the attributes header. */ 1835 header->num_attrs++; 1836 header->data_length += datalen; 1837 1838 if (splitdata) { 1839 /* Only write the entries, since the data was written separately. */ 1840 ainfo.iosize = ainfo.attrhdr->data_start; 1841 } else { 1842 /* The entry and data are both in the header; write them together. */ 1843 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; 1844 } 1845 error = write_xattrinfo(&ainfo); 1846 if (error) { 1847 printf("setxattr: write_xattrinfo error %d\n", error); 1848 } 1849 1850out: 1851 rel_xattrinfo(&ainfo); 1852 close_xattrfile(xvp, fileflags, context); 1853 1854 /* Touch the change time if we changed an attribute. */ 1855 if (error == 0) { 1856 struct vnode_attr va; 1857 1858 /* Re-write the mtime to cause a ctime change. */ 1859 VATTR_INIT(&va); 1860 VATTR_WANTED(&va, va_modify_time); 1861 if (vnode_getattr(vp, &va, context) == 0) { 1862 VATTR_INIT(&va); 1863 VATTR_SET(&va, va_modify_time, va.va_modify_time); 1864 (void) vnode_setattr(vp, &va, context); 1865 } 1866 } 1867 return (error); 1868} 1869 1870 1871/* 1872 * Remove an extended attribute. 1873 */ 1874int 1875default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_context_t context) 1876{ 1877 vnode_t xvp = NULL; 1878 attr_info_t ainfo; 1879 attr_header_t *header; 1880 attr_entry_t *entry; 1881 attr_entry_t *oldslot; 1882 u_int8_t *attrdata; 1883 u_int32_t dataoff; 1884 size_t datalen; 1885 size_t entrylen; 1886 int namelen; 1887 int found = 0, lastone = 0; 1888 int i; 1889 int splitdata; 1890 int attrcount = 0; 1891 int isrsrcfork; 1892 int fileflags; 1893 int error; 1894 1895 fileflags = FREAD | FWRITE; 1896 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { 1897 isrsrcfork = 1; 1898 /* 1899 * Open the file locked (exclusive) since the Carbon 1900 * File Manager may have the Apple Double file open 1901 * and could be changing the resource fork. 1902 */ 1903 fileflags |= O_EXLOCK; 1904 } else { 1905 isrsrcfork = 0; 1906 } 1907 1908 if ((error = open_xattrfile(vp, fileflags, &xvp, context))) { 1909 return (error); 1910 } 1911 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { 1912 close_xattrfile(xvp, fileflags, context); 1913 return (error); 1914 } 1915 if (ainfo.attrhdr) 1916 attrcount += ainfo.attrhdr->num_attrs; 1917 if (ainfo.rsrcfork) 1918 ++attrcount; 1919 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) 1920 ++attrcount; 1921 1922 /* Clear the Finder Info. */ 1923 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { 1924 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) { 1925 error = ENOATTR; 1926 goto out; 1927 } 1928 /* On removal of last attribute the ._ file is removed. */ 1929 if (--attrcount == 0) 1930 goto out; 1931 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset; 1932 bzero((caddr_t)attrdata, FINDERINFOSIZE); 1933 ainfo.iosize = sizeof(attr_header_t); 1934 error = write_xattrinfo(&ainfo); 1935 goto out; 1936 } 1937 1938 /* Clear the Resource Fork. */ 1939 if (isrsrcfork) { 1940 if (!vnode_isreg(vp)) { 1941 error = EPERM; 1942 goto out; 1943 } 1944 if (ainfo.rsrcfork == NULL || ainfo.rsrcfork->length == 0) { 1945 error = ENOATTR; 1946 goto out; 1947 } 1948 /* On removal of last attribute the ._ file is removed. */ 1949 if (--attrcount == 0) 1950 goto out; 1951 /* 1952 * XXX 1953 * If the resource fork isn't the last AppleDouble 1954 * entry then the space needs to be reclaimed by 1955 * shifting the entries after the resource fork. 1956 */ 1957 if ((ainfo.rsrcfork->offset + ainfo.rsrcfork->length) == ainfo.filesize) { 1958 ainfo.filesize -= ainfo.rsrcfork->length; 1959 error = vnode_setsize(xvp, ainfo.filesize, 0, context); 1960 } 1961 if (error == 0) { 1962 ainfo.rsrcfork->length = 0; 1963 ainfo.iosize = sizeof(attr_header_t); 1964 error = write_xattrinfo(&ainfo); 1965 } 1966 goto out; 1967 } 1968 1969 if (ainfo.attrhdr == NULL) { 1970 error = ENOATTR; 1971 goto out; 1972 } 1973 namelen = strlen(name) + 1; 1974 header = ainfo.attrhdr; 1975 entry = ainfo.attr_entry; 1976 1977 /* 1978 * See if this attribute exists. 1979 */ 1980 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { 1981 if (strncmp((const char *)entry->name, name, namelen) == 0) { 1982 found = 1; 1983 if ((i+1) == header->num_attrs) 1984 lastone = 1; 1985 break; 1986 } 1987 entry = ATTR_NEXT(entry); 1988 } 1989 if (!found) { 1990 error = ENOATTR; 1991 goto out; 1992 } 1993 /* On removal of last attribute the ._ file is removed. */ 1994 if (--attrcount == 0) 1995 goto out; 1996 1997 datalen = entry->length; 1998 dataoff = entry->offset; 1999 entrylen = ATTR_ENTRY_LENGTH(namelen); 2000 if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE) 2001 splitdata = 1; 2002 else 2003 splitdata = 0; 2004 2005 /* Remove the attribute entry. */ 2006 if (!lastone) { 2007 bcopy((u_int8_t *)entry + entrylen, (u_int8_t *)entry, 2008 ((size_t)header + header->data_start) - ((size_t)entry + entrylen)); 2009 } 2010 2011 /* Adjust the attribute data. */ 2012 if (splitdata) { 2013 shift_data_up(xvp, 2014 header->data_start, 2015 dataoff - header->data_start, 2016 entrylen, 2017 context); 2018 if (!lastone) { 2019 shift_data_up(xvp, 2020 dataoff + datalen, 2021 (header->data_start + header->data_length) - (dataoff + datalen), 2022 datalen + entrylen, 2023 context); 2024 } 2025 /* XXX write zeros to freed space ? */ 2026 ainfo.iosize = ainfo.attrhdr->data_start - entrylen; 2027 } else { 2028 2029 2030 bcopy((u_int8_t *)header + header->data_start, 2031 (u_int8_t *)header + header->data_start - entrylen, 2032 dataoff - header->data_start); 2033 if (!lastone) { 2034 bcopy((u_int8_t *)header + dataoff + datalen, 2035 (u_int8_t *)header + dataoff - entrylen, 2036 (header->data_start + header->data_length) - (dataoff + datalen)); 2037 } 2038 bzero (((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen)); 2039 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; 2040 } 2041 2042 /* Adjust the header values and entry offsets. */ 2043 header->num_attrs--; 2044 header->data_start -= entrylen; 2045 header->data_length -= datalen; 2046 2047 oldslot = entry; 2048 entry = ainfo.attr_entry; 2049 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { 2050 entry->offset -= entrylen; 2051 if (entry >= oldslot) 2052 entry->offset -= datalen; 2053 entry = ATTR_NEXT(entry); 2054 } 2055 error = write_xattrinfo(&ainfo); 2056 if (error) { 2057 printf("removexattr: write_xattrinfo error %d\n", error); 2058 } 2059out: 2060 rel_xattrinfo(&ainfo); 2061 2062 /* When there are no more attributes remove the ._ file. */ 2063 if (attrcount == 0) { 2064 if (fileflags & O_EXLOCK) 2065 (void) unlock_xattrfile(xvp, context); 2066 VNOP_CLOSE(xvp, fileflags, context); 2067 vnode_rele(xvp); 2068 error = remove_xattrfile(xvp, context); 2069 vnode_put(xvp); 2070 } else { 2071 close_xattrfile(xvp, fileflags, context); 2072 } 2073 /* Touch the change time if we changed an attribute. */ 2074 if (error == 0) { 2075 struct vnode_attr va; 2076 2077 /* Re-write the mtime to cause a ctime change. */ 2078 VATTR_INIT(&va); 2079 VATTR_WANTED(&va, va_modify_time); 2080 if (vnode_getattr(vp, &va, context) == 0) { 2081 VATTR_INIT(&va); 2082 VATTR_SET(&va, va_modify_time, va.va_modify_time); 2083 (void) vnode_setattr(vp, &va, context); 2084 } 2085 } 2086 return (error); 2087 2088} 2089 2090 2091/* 2092 * Retrieve the list of extended attribute names. 2093 */ 2094static int 2095default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs_context_t context) 2096{ 2097 vnode_t xvp = NULL; 2098 attr_info_t ainfo; 2099 attr_entry_t *entry; 2100 int i, count; 2101 int error; 2102 2103 /* 2104 * We do not zero "*size" here as we don't want to stomp a size set when 2105 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the 2106 * system call layer, up in listxattr or flistxattr. 2107 */ 2108 2109 if ((error = open_xattrfile(vp, FREAD, &xvp, context))) { 2110 if (error == ENOATTR) 2111 error = 0; 2112 return (error); 2113 } 2114 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { 2115 close_xattrfile(xvp, FREAD, context); 2116 return (error); 2117 } 2118 2119 /* Check for Finder Info. */ 2120 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) { 2121 if (uio == NULL) { 2122 *size += sizeof(XATTR_FINDERINFO_NAME); 2123 } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_FINDERINFO_NAME)) { 2124 error = ERANGE; 2125 goto out; 2126 } else { 2127 error = uiomove(XATTR_FINDERINFO_NAME, 2128 sizeof(XATTR_FINDERINFO_NAME), uio); 2129 if (error) { 2130 error = ERANGE; 2131 goto out; 2132 } 2133 } 2134 } 2135 2136 /* Check for Resource Fork. */ 2137 if (vnode_isreg(vp) && ainfo.rsrcfork) { 2138 if (uio == NULL) { 2139 *size += sizeof(XATTR_RESOURCEFORK_NAME); 2140 } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_RESOURCEFORK_NAME)) { 2141 error = ERANGE; 2142 goto out; 2143 } else { 2144 error = uiomove(XATTR_RESOURCEFORK_NAME, 2145 sizeof(XATTR_RESOURCEFORK_NAME), uio); 2146 if (error) { 2147 error = ERANGE; 2148 goto out; 2149 } 2150 } 2151 } 2152 2153 /* Check for attributes. */ 2154 if (ainfo.attrhdr) { 2155 count = ainfo.attrhdr->num_attrs; 2156 for (i = 0, entry = ainfo.attr_entry; i < count && ATTR_VALID(entry, ainfo); i++) { 2157 if (xattr_protected((const char *)entry->name) || 2158 xattr_validatename((const char *)entry->name) != 0) { 2159 entry = ATTR_NEXT(entry); 2160 continue; 2161 } 2162 if (uio == NULL) { 2163 *size += entry->namelen; 2164 entry = ATTR_NEXT(entry); 2165 continue; 2166 } 2167 if (uio_resid(uio) < entry->namelen) { 2168 error = ERANGE; 2169 break; 2170 } 2171 error = uiomove((caddr_t) entry->name, entry->namelen, uio); 2172 if (error) { 2173 if (error != EFAULT) 2174 error = ERANGE; 2175 break; 2176 } 2177 entry = ATTR_NEXT(entry); 2178 } 2179 } 2180out: 2181 rel_xattrinfo(&ainfo); 2182 close_xattrfile(xvp, FREAD, context); 2183 2184 return (error); 2185} 2186 2187static int 2188open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context) 2189{ 2190 vnode_t xvp = NULLVP; 2191 vnode_t dvp = NULLVP; 2192 struct vnode_attr va; 2193 struct nameidata nd; 2194 char smallname[64]; 2195 char *filename = NULL; 2196 const char *basename = NULL; 2197 size_t len; 2198 errno_t error; 2199 int opened = 0; 2200 int referenced = 0; 2201 2202 if (vnode_isvroot(vp) && vnode_isdir(vp)) { 2203 /* 2204 * For the root directory use "._." to hold the attributes. 2205 */ 2206 filename = &smallname[0]; 2207 snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, "."); 2208 dvp = vp; /* the "._." file resides in the root dir */ 2209 goto lookup; 2210 } 2211 if ( (dvp = vnode_getparent(vp)) == NULLVP) { 2212 error = ENOATTR; 2213 goto out; 2214 } 2215 if ( (basename = vnode_getname(vp)) == NULL) { 2216 error = ENOATTR; 2217 goto out; 2218 } 2219 2220 /* "._" Attribute files cannot have attributes */ 2221 if (vp->v_type == VREG && strlen(basename) > 2 && 2222 basename[0] == '.' && basename[1] == '_') { 2223 error = EPERM; 2224 goto out; 2225 } 2226 filename = &smallname[0]; 2227 len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename); 2228 if (len >= sizeof(smallname)) { 2229 len++; /* snprintf result doesn't include '\0' */ 2230 MALLOC(filename, char *, len, M_TEMP, M_WAITOK); 2231 len = snprintf(filename, len, "%s%s", ATTR_FILE_PREFIX, basename); 2232 } 2233 /* 2234 * Note that the lookup here does not authorize. Since we are looking 2235 * up in the same directory that we already have the file vnode in, 2236 * we must have been given the file vnode legitimately. Read/write 2237 * access has already been authorized in layers above for calls from 2238 * userspace, and the authorization code using this path to read 2239 * file security from the EA must always get access 2240 */ 2241lookup: 2242 NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH, UIO_SYSSPACE, 2243 CAST_USER_ADDR_T(filename), context); 2244 nd.ni_dvp = dvp; 2245 2246 if (fileflags & O_CREAT) { 2247 nd.ni_cnd.cn_nameiop = CREATE; 2248 if (dvp != vp) { 2249 nd.ni_cnd.cn_flags |= LOCKPARENT; 2250 } 2251 if ( (error = namei(&nd))) { 2252 nd.ni_dvp = NULLVP; 2253 error = ENOATTR; 2254 goto out; 2255 } 2256 if ( (xvp = nd.ni_vp) == NULLVP) { 2257 uid_t uid; 2258 gid_t gid; 2259 mode_t umode; 2260 2261 /* 2262 * Pick up uid/gid/mode from target file. 2263 */ 2264 VATTR_INIT(&va); 2265 VATTR_WANTED(&va, va_uid); 2266 VATTR_WANTED(&va, va_gid); 2267 VATTR_WANTED(&va, va_mode); 2268 if (VNOP_GETATTR(vp, &va, context) == 0 && 2269 VATTR_IS_SUPPORTED(&va, va_uid) && 2270 VATTR_IS_SUPPORTED(&va, va_gid) && 2271 VATTR_IS_SUPPORTED(&va, va_mode)) { 2272 uid = va.va_uid; 2273 gid = va.va_gid; 2274 umode = va.va_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); 2275 } else /* fallback values */ { 2276 uid = KAUTH_UID_NONE; 2277 gid = KAUTH_GID_NONE; 2278 umode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; 2279 } 2280 2281 VATTR_INIT(&va); 2282 VATTR_SET(&va, va_type, VREG); 2283 VATTR_SET(&va, va_mode, umode); 2284 if (uid != KAUTH_UID_NONE) 2285 VATTR_SET(&va, va_uid, uid); 2286 if (gid != KAUTH_GID_NONE) 2287 VATTR_SET(&va, va_gid, gid); 2288 2289 error = vn_create(dvp, &nd.ni_vp, &nd.ni_cnd, &va, 2290 VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL, 2291 context); 2292 if (error == 0) 2293 xvp = nd.ni_vp; 2294 } 2295 nameidone(&nd); 2296 if (dvp != vp) { 2297 vnode_put(dvp); /* drop iocount from LOCKPARENT request above */ 2298 } 2299 if (error) 2300 goto out; 2301 } else { 2302 if ((error = namei(&nd))) { 2303 nd.ni_dvp = NULLVP; 2304 error = ENOATTR; 2305 goto out; 2306 } 2307 xvp = nd.ni_vp; 2308 nameidone(&nd); 2309 } 2310 nd.ni_dvp = NULLVP; 2311 2312 if (xvp->v_type != VREG) { 2313 error = ENOATTR; 2314 goto out; 2315 } 2316 /* 2317 * Owners must match. 2318 */ 2319 VATTR_INIT(&va); 2320 VATTR_WANTED(&va, va_uid); 2321 if (VNOP_GETATTR(vp, &va, context) == 0 && VATTR_IS_SUPPORTED(&va, va_uid)) { 2322 uid_t owner = va.va_uid; 2323 2324 VATTR_INIT(&va); 2325 VATTR_WANTED(&va, va_uid); 2326 if (VNOP_GETATTR(xvp, &va, context) == 0 && (owner != va.va_uid)) { 2327 error = ENOATTR; /* don't use this "._" file */ 2328 goto out; 2329 } 2330 } 2331 2332 if ( (error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) { 2333 error = ENOATTR; 2334 goto out; 2335 } 2336 opened = 1; 2337 2338 if ((error = vnode_ref(xvp))) { 2339 goto out; 2340 } 2341 referenced = 1; 2342 2343 /* If create was requested, make sure file header exists. */ 2344 if (fileflags & O_CREAT) { 2345 VATTR_INIT(&va); 2346 VATTR_WANTED(&va, va_data_size); 2347 VATTR_WANTED(&va, va_fileid); 2348 VATTR_WANTED(&va, va_nlink); 2349 if ( (error = vnode_getattr(xvp, &va, context)) != 0) { 2350 error = EPERM; 2351 goto out; 2352 } 2353 2354 /* If the file is empty then add a default header. */ 2355 if (va.va_data_size == 0) { 2356 /* Don't adopt hard-linked "._" files. */ 2357 if (VATTR_IS_SUPPORTED(&va, va_nlink) && va.va_nlink > 1) { 2358 error = EPERM; 2359 goto out; 2360 } 2361 if ( (error = create_xattrfile(xvp, (u_int32_t)va.va_fileid, context))) 2362 goto out; 2363 } 2364 } 2365 /* Apply file locking if requested. */ 2366 if (fileflags & (O_EXLOCK | O_SHLOCK)) { 2367 short locktype; 2368 2369 locktype = (fileflags & O_EXLOCK) ? F_WRLCK : F_RDLCK; 2370 error = lock_xattrfile(xvp, locktype, context); 2371 } 2372out: 2373 if (dvp && (dvp != vp)) { 2374 vnode_put(dvp); 2375 } 2376 if (basename) { 2377 vnode_putname(basename); 2378 } 2379 if (filename && filename != &smallname[0]) { 2380 FREE(filename, M_TEMP); 2381 } 2382 if (error) { 2383 if (xvp != NULLVP) { 2384 if (opened) { 2385 (void) VNOP_CLOSE(xvp, fileflags, context); 2386 } 2387 if (referenced) { 2388 (void) vnode_rele(xvp); 2389 } 2390 (void) vnode_put(xvp); 2391 xvp = NULLVP; 2392 } 2393 if ((error == ENOATTR) && (fileflags & O_CREAT)) { 2394 error = EPERM; 2395 } 2396 } 2397 *xvpp = xvp; /* return a referenced vnode */ 2398 return (error); 2399} 2400 2401static void 2402close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context) 2403{ 2404// if (fileflags & FWRITE) 2405// (void) VNOP_FSYNC(xvp, MNT_WAIT, context); 2406 2407 if (fileflags & (O_EXLOCK | O_SHLOCK)) 2408 (void) unlock_xattrfile(xvp, context); 2409 2410 (void) VNOP_CLOSE(xvp, fileflags, context); 2411 (void) vnode_rele(xvp); 2412 (void) vnode_put(xvp); 2413} 2414 2415static int 2416remove_xattrfile(vnode_t xvp, vfs_context_t context) 2417{ 2418 vnode_t dvp; 2419 struct nameidata nd; 2420 char *path = NULL; 2421 int pathlen; 2422 int error = 0; 2423 2424 MALLOC_ZONE(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); 2425 if (path == NULL) 2426 return ENOMEM; 2427 2428 pathlen = MAXPATHLEN; 2429 error = vn_getpath(xvp, path, &pathlen); 2430 if (error) { 2431 FREE_ZONE(path, MAXPATHLEN, M_NAMEI); 2432 return (error); 2433 } 2434 2435 NDINIT(&nd, DELETE, LOCKPARENT | NOFOLLOW | DONOTAUTH, 2436 UIO_SYSSPACE, CAST_USER_ADDR_T(path), context); 2437 error = namei(&nd); 2438 FREE_ZONE(path, MAXPATHLEN, M_NAMEI); 2439 if (error) { 2440 return (error); 2441 } 2442 dvp = nd.ni_dvp; 2443 xvp = nd.ni_vp; 2444 2445 error = VNOP_REMOVE(dvp, xvp, &nd.ni_cnd, 0, context); 2446 nameidone(&nd); 2447 vnode_put(dvp); 2448 vnode_put(xvp); 2449 2450 return (error); 2451} 2452 2453/* 2454 * Read in and parse the AppleDouble header and entries, and the extended 2455 * attribute header and entries if any. Populates the fields of ainfop 2456 * based on the headers and entries found. 2457 * 2458 * The basic idea is to: 2459 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All 2460 * AppleDouble entries, the extended attribute header, and extended 2461 * attribute entries must lie within this part of the file; the rest of 2462 * the AppleDouble handling code assumes this. Plus it allows us to 2463 * somewhat optimize by doing a smaller number of larger I/Os. 2464 * - Swap and sanity check the AppleDouble header (including the AppleDouble 2465 * entries). 2466 * - Find the Finder Info and Resource Fork entries, if any. 2467 * - If we're going to be writing, try to make sure the Finder Info entry has 2468 * room to store the extended attribute header, plus some space for extended 2469 * attributes. 2470 * - Swap and sanity check the extended attribute header and entries (if any). 2471 */ 2472static int 2473get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context) 2474{ 2475 uio_t auio = NULL; 2476 void * buffer = NULL; 2477 apple_double_header_t *filehdr; 2478 struct vnode_attr va; 2479 size_t iosize; 2480 int i; 2481 int error; 2482 2483 bzero(ainfop, sizeof(attr_info_t)); 2484 ainfop->filevp = xvp; 2485 ainfop->context = context; 2486 VATTR_INIT(&va); 2487 VATTR_WANTED(&va, va_data_size); 2488 VATTR_WANTED(&va, va_fileid); 2489 if ((error = vnode_getattr(xvp, &va, context))) { 2490 goto bail; 2491 } 2492 ainfop->filesize = va.va_data_size; 2493 2494 /* When setting attributes, allow room for the header to grow. */ 2495 if (setting) 2496 iosize = ATTR_MAX_HDR_SIZE; 2497 else 2498 iosize = MIN(ATTR_MAX_HDR_SIZE, ainfop->filesize); 2499 2500 if (iosize == 0) { 2501 error = ENOATTR; 2502 goto bail; 2503 } 2504 ainfop->iosize = iosize; 2505 MALLOC(buffer, void *, iosize, M_TEMP, M_WAITOK); 2506 if (buffer == NULL){ 2507 error = ENOMEM; 2508 goto bail; 2509 } 2510 2511 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); 2512 uio_addiov(auio, (uintptr_t)buffer, iosize); 2513 2514 /* Read the file header. */ 2515 error = VNOP_READ(xvp, auio, 0, context); 2516 if (error) { 2517 goto bail; 2518 } 2519 ainfop->rawsize = iosize - uio_resid(auio); 2520 ainfop->rawdata = (u_int8_t *)buffer; 2521 2522 filehdr = (apple_double_header_t *)buffer; 2523 2524 error = check_and_swap_apple_double_header(ainfop); 2525 if (error) 2526 goto bail; 2527 2528 ainfop->filehdr = filehdr; /* valid AppleDouble header */ 2529 2530 /* rel_xattrinfo is responsible for freeing the header buffer */ 2531 buffer = NULL; 2532 2533 /* Find the Finder Info and Resource Fork entries, if any */ 2534 for (i = 0; i < filehdr->numEntries; ++i) { 2535 if (filehdr->entries[i].type == AD_FINDERINFO && 2536 filehdr->entries[i].length >= FINDERINFOSIZE) { 2537 /* We found the Finder Info entry. */ 2538 ainfop->finderinfo = &filehdr->entries[i]; 2539 2540 /* 2541 * Is the Finder Info "empty" (all zeroes)? If so, 2542 * we'll pretend like the Finder Info extended attribute 2543 * does not exist. 2544 * 2545 * Note: we have to make sure the Finder Info is 2546 * contained within the buffer we have already read, 2547 * to avoid accidentally accessing a bogus address. 2548 * If it is outside the buffer, we just assume the 2549 * Finder Info is non-empty. 2550 */ 2551 if (ainfop->finderinfo->offset + FINDERINFOSIZE <= ainfop->rawsize && 2552 bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) { 2553 ainfop->emptyfinderinfo = 1; 2554 } 2555 } 2556 if (filehdr->entries[i].type == AD_RESOURCE) { 2557 /* 2558 * Ignore zero-length resource forks when getting. If setting, 2559 * we need to remember the resource fork entry so it can be 2560 * updated once the new content has been written. 2561 */ 2562 if (filehdr->entries[i].length == 0 && !setting) 2563 continue; 2564 2565 /* 2566 * Check to see if any "empty" resource fork is ours (i.e. is ignorable). 2567 * 2568 * The "empty" resource headers we created have a system data tag of: 2569 * "This resource fork intentionally left blank " 2570 */ 2571 if (filehdr->entries[i].length == sizeof(rsrcfork_header_t) && !setting) { 2572 uio_t rf_uio; 2573 u_int8_t systemData[64]; 2574 int rf_err; 2575 2576 2577 /* Read the system data which starts at byte 16 */ 2578 rf_uio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); 2579 uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData)); 2580 uio_setoffset(rf_uio, filehdr->entries[i].offset + 16); 2581 rf_err = VNOP_READ(xvp, rf_uio, 0, context); 2582 uio_free(rf_uio); 2583 2584 if (rf_err != 0 || 2585 bcmp(systemData, RF_EMPTY_TAG, sizeof(RF_EMPTY_TAG)) == 0) { 2586 continue; /* skip this resource fork */ 2587 } 2588 } 2589 ainfop->rsrcfork = &filehdr->entries[i]; 2590 if (i != (filehdr->numEntries - 1)) { 2591 printf("get_xattrinfo: resource fork not last entry\n"); 2592 ainfop->readonly = 1; 2593 } 2594 continue; 2595 } 2596 } 2597 2598 /* 2599 * See if this file looks like it is laid out correctly to contain 2600 * extended attributes. If so, then do the following: 2601 * 2602 * - If we're going to be writing, try to make sure the Finder Info 2603 * entry has room to store the extended attribute header, plus some 2604 * space for extended attributes. 2605 * 2606 * - Swap and sanity check the extended attribute header and entries 2607 * (if any). 2608 */ 2609 if (filehdr->numEntries == 2 && 2610 ainfop->finderinfo == &filehdr->entries[0] && 2611 ainfop->rsrcfork == &filehdr->entries[1] && 2612 ainfop->finderinfo->offset == offsetof(apple_double_header_t, finfo)) { 2613 attr_header_t *attrhdr; 2614 attrhdr = (attr_header_t *)filehdr; 2615 /* 2616 * If we're going to be writing, try to make sure the Finder 2617 * Info entry has room to store the extended attribute header, 2618 * plus some space for extended attributes. 2619 */ 2620 if (setting && ainfop->finderinfo->length == FINDERINFOSIZE) { 2621 size_t delta; 2622 size_t writesize; 2623 2624 delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE); 2625 if (ainfop->rsrcfork && filehdr->entries[1].length) { 2626 /* Make some room before existing resource fork. */ 2627 shift_data_down(xvp, 2628 filehdr->entries[1].offset, 2629 filehdr->entries[1].length, 2630 delta, context); 2631 writesize = sizeof(attr_header_t); 2632 } else { 2633 /* Create a new, empty resource fork. */ 2634 rsrcfork_header_t *rsrcforkhdr; 2635 2636 vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context); 2637 2638 /* Steal some space for an empty RF header. */ 2639 delta -= sizeof(rsrcfork_header_t); 2640 2641 bzero(&attrhdr->appledouble.pad[0], delta); 2642 rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta); 2643 2644 /* Fill in Empty Resource Fork Header. */ 2645 init_empty_resource_fork(rsrcforkhdr); 2646 2647 filehdr->entries[1].length = sizeof(rsrcfork_header_t); 2648 writesize = ATTR_BUF_SIZE; 2649 } 2650 filehdr->entries[0].length += delta; 2651 filehdr->entries[1].offset += delta; 2652 2653 /* Fill in Attribute Header. */ 2654 attrhdr->magic = ATTR_HDR_MAGIC; 2655 attrhdr->debug_tag = (u_int32_t)va.va_fileid; 2656 attrhdr->total_size = filehdr->entries[1].offset; 2657 attrhdr->data_start = sizeof(attr_header_t); 2658 attrhdr->data_length = 0; 2659 attrhdr->reserved[0] = 0; 2660 attrhdr->reserved[1] = 0; 2661 attrhdr->reserved[2] = 0; 2662 attrhdr->flags = 0; 2663 attrhdr->num_attrs = 0; 2664 2665 /* Push out new header */ 2666 uio_reset(auio, 0, UIO_SYSSPACE32, UIO_WRITE); 2667 uio_addiov(auio, (uintptr_t)filehdr, writesize); 2668 2669 swap_adhdr(filehdr); /* to big endian */ 2670 swap_attrhdr(attrhdr, ainfop); /* to big endian */ 2671 error = VNOP_WRITE(xvp, auio, 0, context); 2672 swap_adhdr(filehdr); /* back to native */ 2673 /* The attribute header gets swapped below. */ 2674 } 2675 } 2676 /* 2677 * Swap and sanity check the extended attribute header and 2678 * entries (if any). The Finder Info content must be big enough 2679 * to include the extended attribute header; if not, we just 2680 * ignore it. 2681 * 2682 * Note that we're passing the offset + length (i.e. the end) 2683 * of the Finder Info instead of rawsize to validate_attrhdr. 2684 * This ensures that all extended attributes lie within the 2685 * Finder Info content according to the AppleDouble entry. 2686 * 2687 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid 2688 * header was found. 2689 */ 2690 if (ainfop->finderinfo && 2691 ainfop->finderinfo == &filehdr->entries[0] && 2692 ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) { 2693 attr_header_t *attrhdr = (attr_header_t*)filehdr; 2694 2695 if ((error = check_and_swap_attrhdr(attrhdr, ainfop)) == 0) { 2696 ainfop->attrhdr = attrhdr; /* valid attribute header */ 2697 /* First attr_entry starts immediately following attribute header */ 2698 ainfop->attr_entry = (attr_entry_t *)&attrhdr[1]; 2699 } 2700 } 2701 2702 error = 0; 2703bail: 2704 if (auio != NULL) 2705 uio_free(auio); 2706 if (buffer != NULL) 2707 FREE(buffer, M_TEMP); 2708 return (error); 2709} 2710 2711 2712static int 2713create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context) 2714{ 2715 attr_header_t *xah; 2716 rsrcfork_header_t *rsrcforkhdr; 2717 void * buffer; 2718 uio_t auio; 2719 int rsrcforksize; 2720 int error; 2721 2722 MALLOC(buffer, void *, ATTR_BUF_SIZE, M_TEMP, M_WAITOK); 2723 bzero(buffer, ATTR_BUF_SIZE); 2724 2725 xah = (attr_header_t *)buffer; 2726 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_WRITE); 2727 uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE); 2728 rsrcforksize = sizeof(rsrcfork_header_t); 2729 rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize); 2730 2731 /* Fill in Apple Double Header. */ 2732 xah->appledouble.magic = SWAP32 (ADH_MAGIC); 2733 xah->appledouble.version = SWAP32 (ADH_VERSION); 2734 xah->appledouble.numEntries = SWAP16 (2); 2735 xah->appledouble.entries[0].type = SWAP32 (AD_FINDERINFO); 2736 xah->appledouble.entries[0].offset = SWAP32 (offsetof(apple_double_header_t, finfo)); 2737 xah->appledouble.entries[0].length = SWAP32 (ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize); 2738 xah->appledouble.entries[1].type = SWAP32 (AD_RESOURCE); 2739 xah->appledouble.entries[1].offset = SWAP32 (ATTR_BUF_SIZE - rsrcforksize); 2740 xah->appledouble.entries[1].length = SWAP32 (rsrcforksize); 2741 bcopy(ADH_MACOSX, xah->appledouble.filler, sizeof(xah->appledouble.filler)); 2742 2743 /* Fill in Attribute Header. */ 2744 xah->magic = SWAP32 (ATTR_HDR_MAGIC); 2745 xah->debug_tag = SWAP32 (fileid); 2746 xah->total_size = SWAP32 (ATTR_BUF_SIZE - rsrcforksize); 2747 xah->data_start = SWAP32 (sizeof(attr_header_t)); 2748 2749 /* Fill in Empty Resource Fork Header. */ 2750 init_empty_resource_fork(rsrcforkhdr); 2751 2752 /* Push it out. */ 2753 error = VNOP_WRITE(xvp, auio, 0, context); 2754 2755 uio_free(auio); 2756 FREE(buffer, M_TEMP); 2757 2758 return (error); 2759} 2760 2761static void 2762init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr) 2763{ 2764 bzero(rsrcforkhdr, sizeof(rsrcfork_header_t)); 2765 rsrcforkhdr->fh_DataOffset = SWAP32 (RF_FIRST_RESOURCE); 2766 rsrcforkhdr->fh_MapOffset = SWAP32 (RF_FIRST_RESOURCE); 2767 rsrcforkhdr->fh_MapLength = SWAP32 (RF_NULL_MAP_LENGTH); 2768 rsrcforkhdr->mh_DataOffset = SWAP32 (RF_FIRST_RESOURCE); 2769 rsrcforkhdr->mh_MapOffset = SWAP32 (RF_FIRST_RESOURCE); 2770 rsrcforkhdr->mh_MapLength = SWAP32 (RF_NULL_MAP_LENGTH); 2771 rsrcforkhdr->mh_Types = SWAP16 (RF_NULL_MAP_LENGTH - 2 ); 2772 rsrcforkhdr->mh_Names = SWAP16 (RF_NULL_MAP_LENGTH); 2773 rsrcforkhdr->typeCount = SWAP16 (-1); 2774 bcopy(RF_EMPTY_TAG, rsrcforkhdr->systemData, sizeof(RF_EMPTY_TAG)); 2775} 2776 2777static void 2778rel_xattrinfo(attr_info_t *ainfop) 2779{ 2780 FREE(ainfop->filehdr, M_TEMP); 2781 bzero(ainfop, sizeof(attr_info_t)); 2782} 2783 2784static int 2785write_xattrinfo(attr_info_t *ainfop) 2786{ 2787 uio_t auio; 2788 int error; 2789 2790 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_WRITE); 2791 uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize); 2792 2793 swap_adhdr(ainfop->filehdr); 2794 if (ainfop->attrhdr != NULL) { 2795 swap_attrhdr(ainfop->attrhdr, ainfop); 2796 } 2797 2798 error = VNOP_WRITE(ainfop->filevp, auio, 0, ainfop->context); 2799 2800 swap_adhdr(ainfop->filehdr); 2801 if (ainfop->attrhdr != NULL) { 2802 swap_attrhdr(ainfop->attrhdr, ainfop); 2803 } 2804 uio_free(auio); 2805 2806 return (error); 2807} 2808 2809#if BYTE_ORDER == LITTLE_ENDIAN 2810/* 2811 * Endian swap apple double header 2812 */ 2813static void 2814swap_adhdr(apple_double_header_t *adh) 2815{ 2816 int count; 2817 int i; 2818 2819 count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries); 2820 2821 adh->magic = SWAP32 (adh->magic); 2822 adh->version = SWAP32 (adh->version); 2823 adh->numEntries = SWAP16 (adh->numEntries); 2824 2825 for (i = 0; i < count; i++) { 2826 adh->entries[i].type = SWAP32 (adh->entries[i].type); 2827 adh->entries[i].offset = SWAP32 (adh->entries[i].offset); 2828 adh->entries[i].length = SWAP32 (adh->entries[i].length); 2829 } 2830} 2831 2832/* 2833 * Endian swap extended attributes header 2834 */ 2835static void 2836swap_attrhdr(attr_header_t *ah, attr_info_t* info) 2837{ 2838 attr_entry_t *ae; 2839 int count; 2840 int i; 2841 2842 count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs); 2843 2844 ah->magic = SWAP32 (ah->magic); 2845 ah->debug_tag = SWAP32 (ah->debug_tag); 2846 ah->total_size = SWAP32 (ah->total_size); 2847 ah->data_start = SWAP32 (ah->data_start); 2848 ah->data_length = SWAP32 (ah->data_length); 2849 ah->flags = SWAP16 (ah->flags); 2850 ah->num_attrs = SWAP16 (ah->num_attrs); 2851 2852 ae = (attr_entry_t *)(&ah[1]); 2853 for (i = 0; i < count && ATTR_VALID(ae, *info); i++, ae = ATTR_NEXT(ae)) { 2854 ae->offset = SWAP32 (ae->offset); 2855 ae->length = SWAP32 (ae->length); 2856 ae->flags = SWAP16 (ae->flags); 2857 } 2858} 2859#endif 2860 2861/* 2862 * Validate and swap the attributes header contents, and each attribute's 2863 * attr_entry_t. 2864 * 2865 * Note: Assumes the caller has verified that the Finder Info content is large 2866 * enough to contain the attr_header structure itself. Therefore, we can 2867 * swap the header fields before sanity checking them. 2868 */ 2869static int 2870check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop) 2871{ 2872 attr_entry_t *ae; 2873 u_int8_t *buf_end; 2874 u_int32_t end; 2875 int count; 2876 int i; 2877 2878 if (ah == NULL) 2879 return EINVAL; 2880 2881 if (SWAP32(ah->magic) != ATTR_HDR_MAGIC) 2882 return EINVAL; 2883 2884 /* Swap the basic header fields */ 2885 ah->magic = SWAP32(ah->magic); 2886 ah->debug_tag = SWAP32 (ah->debug_tag); 2887 ah->total_size = SWAP32 (ah->total_size); 2888 ah->data_start = SWAP32 (ah->data_start); 2889 ah->data_length = SWAP32 (ah->data_length); 2890 ah->flags = SWAP16 (ah->flags); 2891 ah->num_attrs = SWAP16 (ah->num_attrs); 2892 2893 /* 2894 * Make sure the total_size fits within the Finder Info area, and the 2895 * extended attribute data area fits within total_size. 2896 */ 2897 end = ah->data_start + ah->data_length; 2898 if (ah->total_size > ainfop->finderinfo->offset + ainfop->finderinfo->length || 2899 end < ah->data_start || 2900 end > ah->total_size) { 2901 return EINVAL; 2902 } 2903 2904 /* 2905 * Make sure each of the attr_entry_t's fits within total_size. 2906 */ 2907 buf_end = ainfop->rawdata + ah->total_size; 2908 count = ah->num_attrs; 2909 ae = (attr_entry_t *)(&ah[1]); 2910 2911 for (i=0; i<count; i++) { 2912 /* Make sure the fixed-size part of this attr_entry_t fits. */ 2913 if ((u_int8_t *) &ae[1] > buf_end) 2914 return EINVAL; 2915 2916 /* Make sure the variable-length name fits (+1 is for NUL terminator) */ 2917 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */ 2918 if (&ae->name[ae->namelen+1] > buf_end) 2919 return EINVAL; 2920 2921 /* Swap the attribute entry fields */ 2922 ae->offset = SWAP32(ae->offset); 2923 ae->length = SWAP32(ae->length); 2924 ae->flags = SWAP16(ae->flags); 2925 2926 /* Make sure the attribute content fits. */ 2927 end = ae->offset + ae->length; 2928 if (end < ae->offset || end > ah->total_size) 2929 return EINVAL; 2930 2931 ae = ATTR_NEXT(ae); 2932 } 2933 2934 /* 2935 * TODO: Make sure the contents of attributes don't overlap the header 2936 * and don't overlap each other. The hard part is that we don't know 2937 * what the actual header size is until we have looped over all of the 2938 * variable-sized attribute entries. 2939 * 2940 * XXX Is there any guarantee that attribute entries are stored in 2941 * XXX order sorted by the contents' file offset? If so, that would 2942 * XXX make the pairwise overlap check much easier. 2943 */ 2944 2945 return 0; 2946} 2947 2948// 2949// "start" & "end" are byte offsets in the file. 2950// "to" is the byte offset we want to move the 2951// data to. "to" should be > "start". 2952// 2953// we do the copy backwards to avoid problems if 2954// there's an overlap. 2955// 2956static int 2957shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context) 2958{ 2959 int ret, iolen; 2960 size_t chunk, orig_chunk; 2961 char *buff; 2962 off_t pos; 2963 ucred_t ucred = vfs_context_ucred(context); 2964 proc_t p = vfs_context_proc(context); 2965 2966 if (delta == 0 || len == 0) { 2967 return 0; 2968 } 2969 2970 chunk = 4096; 2971 if (len < chunk) { 2972 chunk = len; 2973 } 2974 orig_chunk = chunk; 2975 2976 if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk)) { 2977 return ENOMEM; 2978 } 2979 2980 for(pos=start+len-chunk; pos >= start; pos-=chunk) { 2981 ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p); 2982 if (iolen != 0) { 2983 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n", 2984 pos, ret, chunk, ret); 2985 break; 2986 } 2987 2988 ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p); 2989 if (iolen != 0) { 2990 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n", 2991 pos+delta, ret, chunk, ret); 2992 break; 2993 } 2994 2995 if ((pos - chunk) < start) { 2996 chunk = pos - start; 2997 2998 if (chunk == 0) { // we're all done 2999 break; 3000 } 3001 } 3002 } 3003 kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk); 3004 3005 return 0; 3006} 3007 3008 3009static int 3010shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context) 3011{ 3012 int ret, iolen; 3013 size_t chunk, orig_chunk; 3014 char *buff; 3015 off_t pos; 3016 off_t end; 3017 ucred_t ucred = vfs_context_ucred(context); 3018 proc_t p = vfs_context_proc(context); 3019 3020 if (delta == 0 || len == 0) { 3021 return 0; 3022 } 3023 3024 chunk = 4096; 3025 if (len < chunk) { 3026 chunk = len; 3027 } 3028 orig_chunk = chunk; 3029 end = start + len; 3030 3031 if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk)) { 3032 return ENOMEM; 3033 } 3034 3035 for(pos = start; pos < end; pos += chunk) { 3036 ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p); 3037 if (iolen != 0) { 3038 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n", 3039 pos, ret, chunk, ret); 3040 break; 3041 } 3042 3043 ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p); 3044 if (iolen != 0) { 3045 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n", 3046 pos+delta, ret, chunk, ret); 3047 break; 3048 } 3049 3050 if ((pos + chunk) > end) { 3051 chunk = end - pos; 3052 3053 if (chunk == 0) { // we're all done 3054 break; 3055 } 3056 } 3057 } 3058 kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk); 3059 3060 return 0; 3061} 3062 3063static int 3064lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context) 3065{ 3066 struct flock lf; 3067 int error; 3068 3069 lf.l_whence = SEEK_SET; 3070 lf.l_start = 0; 3071 lf.l_len = 0; 3072 lf.l_type = locktype; /* F_WRLCK or F_RDLCK */ 3073 /* Note: id is just a kernel address that's not a proc */ 3074 error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_SETLK, &lf, F_FLOCK|F_WAIT, context); 3075 return (error == ENOTSUP ? 0 : error); 3076} 3077 3078static int 3079unlock_xattrfile(vnode_t xvp, vfs_context_t context) 3080{ 3081 struct flock lf; 3082 int error; 3083 3084 lf.l_whence = SEEK_SET; 3085 lf.l_start = 0; 3086 lf.l_len = 0; 3087 lf.l_type = F_UNLCK; 3088 /* Note: id is just a kernel address that's not a proc */ 3089 error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_UNLCK, &lf, F_FLOCK, context); 3090 return (error == ENOTSUP ? 0 : error); 3091} 3092 3093