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