X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/cf7d32b81c573a0536dc4da4157f9c26f8d0bed3..f427ee49d309d8fc33ebf3042c3a775f2f530ded:/bsd/vfs/vfs_xattr.c diff --git a/bsd/vfs/vfs_xattr.c b/bsd/vfs/vfs_xattr.c index 5c59cfbc8..00ee6c94c 100644 --- a/bsd/vfs/vfs_xattr.c +++ b/bsd/vfs/vfs_xattr.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include #include @@ -59,16 +59,31 @@ #if NAMEDSTREAMS +static int shadow_sequence; + /* - * Cast to 'unsigned int' loses precision - hope that's OK... + * We use %p to prevent loss of precision for pointers on varying architectures. */ + +#define SHADOW_NAME_FMT ".vfs_rsrc_stream_%p%08x%p" +#define SHADOW_DIR_FMT ".vfs_rsrc_streams_%p%x" +#define SHADOW_DIR_CONTAINER "/var/run" + #define MAKE_SHADOW_NAME(VP, NAME) \ - snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%x%08x%x", (unsigned int)(VP), (VP)->v_id, (unsigned int)(VP)->v_data); + snprintf((NAME), sizeof((NAME)), (SHADOW_NAME_FMT), \ + ((void*)(VM_KERNEL_ADDRPERM(VP))), \ + (VP)->v_id, \ + ((void*)(VM_KERNEL_ADDRPERM((VP)->v_data)))) -static vnode_t shadow_dvp; /* tmp directory to hold stream shadow files */ -static int shadow_vid; -static int shadow_sequence; +/* The full path to the shadow directory */ +#define MAKE_SHADOW_DIRNAME(VP, NAME) \ + snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_CONTAINER "/" SHADOW_DIR_FMT), \ + ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence) +/* The shadow directory as a 'leaf' entry */ +#define MAKE_SHADOW_DIR_LEAF(VP, NAME) \ + snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_FMT), \ + ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence) static int default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context); @@ -78,31 +93,34 @@ static int default_removenamedstream(vnode_t vp, const char *name, vfs_context_ static int getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, int *creator, vfs_context_t context); -static int get_shadow_dir(vnode_t *sdvpp, vfs_context_t context); - -#endif +static int get_shadow_dir(vnode_t *sdvpp); +#endif /* NAMEDSTREAMS */ /* * Default xattr support routines. */ +static int default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, int options, + vfs_context_t context); +static int default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, + vfs_context_t context); static int default_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, - vfs_context_t context); - - + vfs_context_t context); +static int default_removexattr(vnode_t vp, const char *name, int options, + vfs_context_t context); /* * Retrieve the data of an extended attribute. */ int vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, - int options, vfs_context_t context) + int options, vfs_context_t context) { int error; - if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { - return (EPERM); + if (!XATTR_VNODE_SUPPORTED(vp)) { + return EPERM; } #if NAMEDSTREAMS /* getxattr calls are not allowed for streams. */ @@ -119,8 +137,9 @@ vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, if (!(options & XATTR_NOSECURITY)) { #if CONFIG_MACF error = mac_vnode_check_getextattr(context, vp, name, uio); - if (error) + if (error) { goto out; + } #endif /* MAC */ if ((error = xattr_validatename(name))) { goto out; @@ -129,16 +148,16 @@ vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, goto out; } /* The offset can only be non-zero for resource forks. */ - if (uio != NULL && uio_offset(uio) != 0 && - bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { + if (uio != NULL && uio_offset(uio) != 0 && + strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { error = EINVAL; goto out; } } /* The offset can only be non-zero for resource forks. */ - if (uio != NULL && uio_offset(uio) != 0 && - bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { + if (uio != NULL && uio_offset(uio) != 0 && + strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { error = EINVAL; goto out; } @@ -147,12 +166,11 @@ vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { /* * A filesystem may keep some EAs natively and return ENOTSUP for others. - * SMB returns ENOTSUP for finderinfo and resource forks. */ error = default_getxattr(vp, name, uio, size, options, context); } out: - return (error); + return error; } /* @@ -163,8 +181,8 @@ vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t { int error; - if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { - return (EPERM); + if (!XATTR_VNODE_SUPPORTED(vp)) { + return EPERM; } #if NAMEDSTREAMS /* setxattr calls are not allowed for streams. */ @@ -173,25 +191,27 @@ vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t goto out; } #endif - if ((options & (XATTR_REPLACE|XATTR_CREATE)) == (XATTR_REPLACE|XATTR_CREATE)) { - return (EINVAL); + if ((options & (XATTR_REPLACE | XATTR_CREATE)) == (XATTR_REPLACE | XATTR_CREATE)) { + return EINVAL; } if ((error = xattr_validatename(name))) { - return (error); + return error; } - if (!(options & XATTR_NOSECURITY)) { + if (!(options & XATTR_NOSECURITY)) { #if CONFIG_MACF error = mac_vnode_check_setextattr(context, vp, name, uio); - if (error) + if (error) { goto out; + } #endif /* MAC */ error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context); - if (error) + if (error) { goto out; + } } /* The offset can only be non-zero for resource forks. */ - if (uio_offset(uio) != 0 && - bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0 ) { + if (uio_offset(uio) != 0 && + strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { error = EINVAL; goto out; } @@ -209,7 +229,7 @@ vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t */ if (error == EJUSTRETURN) { int native = 0, dufile = 0; - size_t sz; /* not used */ + size_t sz; /* not used */ native = VNOP_GETXATTR(vp, name, NULL, &sz, 0, context) ? 0 : 1; dufile = default_getxattr(vp, name, NULL, &sz, 0, context) ? 0 : 1; @@ -233,17 +253,19 @@ vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { /* * A filesystem may keep some EAs natively and return ENOTSUP for others. - * SMB returns ENOTSUP for finderinfo and resource forks. */ error = default_setxattr(vp, name, uio, options, context); } #if CONFIG_MACF - if ((error == 0) && !(options & XATTR_NOSECURITY) && - (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL)) - mac_vnode_label_update_extattr(vnode_mount(vp), vp, name); + if ((error == 0) && !(options & XATTR_NOSECURITY)) { + mac_vnode_notify_setextattr(context, vp, name, uio); + if (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL) { + mac_vnode_label_update_extattr(vnode_mount(vp), vp, name); + } + } #endif out: - return (error); + return error; } /* @@ -254,8 +276,8 @@ vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context { int error; - if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { - return (EPERM); + if (!XATTR_VNODE_SUPPORTED(vp)) { + return EPERM; } #if NAMEDSTREAMS /* removexattr calls are not allowed for streams. */ @@ -265,23 +287,24 @@ vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context } #endif if ((error = xattr_validatename(name))) { - return (error); + return error; } if (!(options & XATTR_NOSECURITY)) { #if CONFIG_MACF error = mac_vnode_check_deleteextattr(context, vp, name); - if (error) + if (error) { goto out; + } #endif /* MAC */ error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context); - if (error) + if (error) { goto out; + } } error = VNOP_REMOVEXATTR(vp, name, options, context); if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) { /* * A filesystem may keep some EAs natively and return ENOTSUP for others. - * SMB returns ENOTSUP for finderinfo and resource forks. */ error = default_removexattr(vp, name, options, context); #ifdef DUAL_EAS @@ -293,17 +316,21 @@ vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context * default_removexattr should not be considered an error. */ error = default_removexattr(vp, name, options, context); - if (error == ENOATTR) + if (error == ENOATTR) { error = 0; + } #endif /* DUAL_EAS */ } #if CONFIG_MACF - if ((error == 0) && !(options & XATTR_NOSECURITY) && - (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL)) - mac_vnode_label_update_extattr(vnode_mount(vp), vp, name); + if ((error == 0) && !(options & XATTR_NOSECURITY)) { + mac_vnode_notify_deleteextattr(context, vp, name); + if (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL) { + mac_vnode_label_update_extattr(vnode_mount(vp), vp, name); + } + } #endif out: - return (error); + return error; } /* @@ -314,26 +341,28 @@ vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t con { int error; - if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { - return (EPERM); + if (!XATTR_VNODE_SUPPORTED(vp)) { + return EPERM; } #if NAMEDSTREAMS /* listxattr calls are not allowed for streams. */ if (vp->v_flag & VISNAMEDSTREAM) { - return (EPERM); + return EPERM; } #endif if (!(options & XATTR_NOSECURITY)) { #if CONFIG_MACF error = mac_vnode_check_listextattr(context, vp); - if (error) + if (error) { goto out; + } #endif /* MAC */ error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context); - if (error) + if (error) { goto out; + } } error = VNOP_LISTXATTR(vp, uio, size, options, context); @@ -341,31 +370,29 @@ vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t con /* * A filesystem may keep some but not all EAs natively, in which case * the native EA names will have been uiomove-d out (or *size updated) - * and the default_listxattr here will finish the job. Note SMB takes - * advantage of this for its finder-info and resource forks. + * and the default_listxattr here will finish the job. */ error = default_listxattr(vp, uio, size, options, context); } out: - return (error); + return error; } int xattr_validatename(const char *name) { - int namelen; + size_t namelen; if (name == NULL || name[0] == '\0') { - return (EINVAL); - } - namelen = strnlen(name, XATTR_MAXNAMELEN); - if (name[namelen] != '\0') - return (ENAMETOOLONG); - - if (utf8_validatestr((const unsigned char *)name, namelen) != 0) - return (EINVAL); - - return (0); + return EINVAL; + } + namelen = strlen(name); + + if (utf8_validatestr((const unsigned char *)name, namelen) != 0) { + return EINVAL; + } + + return 0; } @@ -375,11 +402,61 @@ xattr_validatename(const char *name) int xattr_protected(const char *attrname) { - return(!strncmp(attrname, "com.apple.system.", 17)); + return !strncmp(attrname, "com.apple.system.", 17); } +static void +vnode_setasnamedstream_internal(vnode_t vp, vnode_t svp) +{ + uint32_t streamflags = VISNAMEDSTREAM; + + if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) { + streamflags |= VISSHADOW; + } + + /* Tag the vnode. */ + vnode_lock_spin(svp); + svp->v_flag |= streamflags; + vnode_unlock(svp); + + /* Tag the parent so we know to flush credentials for streams on setattr */ + vnode_lock_spin(vp); + vp->v_lflag |= VL_HASSTREAMS; + vnode_unlock(vp); + + /* Make the file it's parent. + * Note: This parent link helps us distinguish vnodes for + * shadow stream files from vnodes for resource fork on file + * systems that support namedstream natively (both have + * VISNAMEDSTREAM set) by allowing access to mount structure + * for checking MNTK_NAMED_STREAMS bit at many places in the + * code. + */ + vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_NAMEDSTREAM_PARENT); + + if (vnode_isdyldsharedcache(vp)) { + vnode_lock_spin(svp); + svp->v_flag |= VSHARED_DYLD; + vnode_unlock(svp); + } + + return; +} + +errno_t +vnode_setasnamedstream(vnode_t vp, vnode_t svp) +{ + if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) { + return EINVAL; + } + + vnode_setasnamedstream_internal(vp, svp); + return 0; +} + #if NAMEDSTREAMS + /* * Obtain a named stream from vnode vp. */ @@ -388,77 +465,59 @@ vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperati { int error; - if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) + if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) { error = VNOP_GETNAMEDSTREAM(vp, svpp, name, op, flags, context); - else - error = default_getnamedstream(vp, svpp, name, op, context); + } else { + if (flags) { + error = ENOTSUP; + } else { + error = default_getnamedstream(vp, svpp, name, op, context); + } + } if (error == 0) { - vnode_t svp = *svpp; - - /* Tag the vnode. */ - vnode_lock(svp); - svp->v_flag |= VISNAMEDSTREAM; - vnode_unlock(svp); - /* Make the file its parent. - * Note: This parent link helps us distinguish vnodes for - * shadow stream files from vnodes for resource fork on file - * systems that support named streams natively (both have - * VISNAMEDSTREAM set) by allowing access to mount structure - * for checking MNTK_NAMED_STREAMS bit at many places in the code - */ - vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT); - } + vnode_setasnamedstream_internal(vp, *svpp); + } - return (error); + return error; } /* * Make a named stream for vnode vp. */ -errno_t +errno_t vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t context) { int error; - if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) + if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) { error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context); - else + } else { error = default_makenamedstream(vp, svpp, name, context); + } if (error == 0) { - vnode_t svp = *svpp; - - /* Tag the vnode. */ - vnode_lock(svp); - svp->v_flag |= VISNAMEDSTREAM; - vnode_unlock(svp); - /* Make the file its parent. - * Note: This parent link helps us distinguish vnodes for - * shadow stream files from vnodes for resource fork on file - * systems that support named streams natively (both have - * VISNAMEDSTREAM set) by allowing access to mount structure - * for checking MNTK_NAMED_STREAMS bit at many places in the code - */ - vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT); + vnode_setasnamedstream_internal(vp, *svpp); } - return (error); + + return error; } /* * Remove a named stream from vnode vp. */ -errno_t +errno_t vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t context) { int error; - if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) + if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) { error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context); - else + } else { error = default_removenamedstream(vp, name, context); + } - return (error); + return error; } #define NS_IOBUFSIZE (128 * 1024) @@ -466,23 +525,32 @@ vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vf /* * Release a named stream shadow file. * - * Note: This function is called from two places where we do not need - * to check if the vnode has any references held before deleting the - * shadow file. Once from vclean() when the vnode is being reclaimed - * and we do not hold any references on the vnode. Second time from - * default_getnamedstream() when we get an error during shadow stream - * file initialization so that other processes who are waiting for the - * shadow stream file initialization by the creator will get opportunity + * Note: This function is called from two places where we do not need + * to check if the vnode has any references held before deleting the + * shadow file. Once from vclean() when the vnode is being reclaimed + * and we do not hold any references on the vnode. Second time from + * default_getnamedstream() when we get an error during shadow stream + * file initialization so that other processes who are waiting for the + * shadow stream file initialization by the creator will get opportunity * to create and initialize the file again. */ errno_t -vnode_relenamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) +vnode_relenamedstream(vnode_t vp, vnode_t svp) { vnode_t dvp; struct componentname cn; - char tmpname[48]; + char tmpname[80]; errno_t err; + /* + * We need to use the kernel context here. If we used the supplied + * VFS context we have no clue whether or not it originated from userland + * where it could be subject to a chroot jail. We need to ensure that all + * filesystem access to shadow files is done on the same FS regardless of + * userland process restrictions. + */ + vfs_context_t kernelctx = vfs_context_kernel(); + cache_purge(svp); vnode_lock(svp); @@ -491,28 +559,34 @@ vnode_relenamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) cn.cn_nameiop = DELETE; cn.cn_flags = ISLASTCN; - cn.cn_context = context; + cn.cn_context = kernelctx; cn.cn_pnbuf = tmpname; cn.cn_pnlen = sizeof(tmpname); cn.cn_nameptr = cn.cn_pnbuf; - cn.cn_namelen = strlen(tmpname); + cn.cn_namelen = (int)strlen(tmpname); - /* Obtain the vnode for the shadow files directory. */ - err = get_shadow_dir(&dvp, context); + /* + * Obtain the vnode for the shadow files directory. Make sure to + * use the kernel ctx as described above. + */ + err = get_shadow_dir(&dvp); if (err != 0) { return err; } - - (void) VNOP_REMOVE(dvp, svp, &cn, 0, context); + + (void) VNOP_REMOVE(dvp, svp, &cn, 0, kernelctx); vnode_put(dvp); - return (0); + return 0; } /* * Flush a named stream shadow file. + * + * 'vp' represents the AppleDouble file. + * 'svp' represents the shadow file. */ -errno_t +errno_t vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) { struct vnode_attr va; @@ -523,30 +597,40 @@ vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) size_t iosize; size_t datasize; int error; + /* + * The kernel context must be used for all I/O to the shadow file + * and its namespace operations + */ + vfs_context_t kernelctx = vfs_context_kernel(); + + /* The supplied context is used for access to the AD file itself */ VATTR_INIT(&va); VATTR_WANTED(&va, va_data_size); - if (VNOP_GETATTR(svp, &va, context) != 0 || - !VATTR_IS_SUPPORTED(&va, va_data_size)) { - return (0); + if (VNOP_GETATTR(svp, &va, context) != 0 || + !VATTR_IS_SUPPORTED(&va, va_data_size)) { + return 0; } - datasize = va.va_data_size; - if ((datasize == 0)) { + if (va.va_data_size > UINT32_MAX) { + return EINVAL; + } + datasize = (size_t)va.va_data_size; + if (datasize == 0) { (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context); - return (0); + return 0; } iosize = bufsize = MIN(datasize, NS_IOBUFSIZE); - if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) { - return (ENOMEM); + if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize, VM_KERN_MEMORY_FILE)) { + return ENOMEM; } - auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); + auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); offset = 0; /* * Copy the shadow stream file data into the resource fork. */ - error = VNOP_OPEN(svp, 0, context); + error = VNOP_OPEN(svp, 0, kernelctx); if (error) { printf("vnode_flushnamedstream: err %d opening file\n", error); goto out; @@ -554,9 +638,9 @@ vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) while (offset < datasize) { iosize = MIN(datasize - offset, iosize); - uio_reset(auio, offset, UIO_SYSSPACE32, UIO_READ); + uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ); uio_addiov(auio, (uintptr_t)bufptr, iosize); - error = VNOP_READ(svp, auio, 0, context); + error = VNOP_READ(svp, auio, 0, kernelctx); if (error) { break; } @@ -567,7 +651,7 @@ vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) break; } } - uio_reset(auio, offset, UIO_SYSSPACE32, UIO_WRITE); + uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE); uio_addiov(auio, (uintptr_t)bufptr, iosize); error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context); if (error) { @@ -575,7 +659,9 @@ vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) } offset += iosize; } - (void) VNOP_CLOSE(svp, 0, context); + + /* close shadowfile */ + (void) VNOP_CLOSE(svp, 0, kernelctx); out: if (bufptr) { kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize); @@ -583,24 +669,96 @@ out: if (auio) { uio_free(auio); } - return (error); + return error; } +/* + * Verify that the vnode 'vp' is a vnode that lives in the shadow + * directory. We can't just query the parent pointer directly since + * the shadowfile is hooked up to the actual file it's a stream for. + */ +errno_t +vnode_verifynamedstream(vnode_t vp) +{ + int error; + struct vnode *shadow_dvp = NULL; + struct vnode *shadowfile = NULL; + struct componentname cn; + + /* + * We need to use the kernel context here. If we used the supplied + * VFS context we have no clue whether or not it originated from userland + * where it could be subject to a chroot jail. We need to ensure that all + * filesystem access to shadow files is done on the same FS regardless of + * userland process restrictions. + */ + vfs_context_t kernelctx = vfs_context_kernel(); + char tmpname[80]; + + + /* Get the shadow directory vnode */ + error = get_shadow_dir(&shadow_dvp); + if (error) { + return error; + } + + /* Re-generate the shadow name in the buffer */ + MAKE_SHADOW_NAME(vp, tmpname); + + /* Look up item in shadow dir */ + bzero(&cn, sizeof(cn)); + cn.cn_nameiop = LOOKUP; + cn.cn_flags = ISLASTCN | CN_ALLOWRSRCFORK; + cn.cn_context = kernelctx; + cn.cn_pnbuf = tmpname; + cn.cn_pnlen = sizeof(tmpname); + cn.cn_nameptr = cn.cn_pnbuf; + cn.cn_namelen = (int)strlen(tmpname); + + if (VNOP_LOOKUP(shadow_dvp, &shadowfile, &cn, kernelctx) == 0) { + /* is the pointer the same? */ + if (shadowfile == vp) { + error = 0; + } else { + error = EPERM; + } + /* drop the iocount acquired */ + vnode_put(shadowfile); + } + + /* Drop iocount on shadow dir */ + vnode_put(shadow_dvp); + return error; +} + +/* + * Access or create the shadow file as needed. + * + * 'makestream' with non-zero value means that we need to guarantee we were the + * creator of the shadow file. + * + * 'context' is the user supplied context for the original VFS operation that + * caused us to need a shadow file. + * + * int pointed to by 'creator' is nonzero if we created the shadowfile. + */ static int getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, - int *creator, vfs_context_t context) + int *creator, vfs_context_t context) { vnode_t dvp = NULLVP; vnode_t svp = NULLVP; struct componentname cn; struct vnode_attr va; - char tmpname[48]; + char tmpname[80]; size_t datasize = 0; int error = 0; + int retries = 0; + vfs_context_t kernelctx = vfs_context_kernel(); +retry_create: *creator = 0; - /* Establish a unique file name. */ MAKE_SHADOW_NAME(vp, tmpname); bzero(&cn, sizeof(cn)); @@ -610,7 +768,7 @@ getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, cn.cn_pnbuf = tmpname; cn.cn_pnlen = sizeof(tmpname); cn.cn_nameptr = cn.cn_pnbuf; - cn.cn_namelen = strlen(tmpname); + cn.cn_namelen = (int)strlen(tmpname); /* Pick up uid, gid, mode and date from original file. */ VATTR_INIT(&va); @@ -619,10 +777,10 @@ getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, VATTR_WANTED(&va, va_mode); VATTR_WANTED(&va, va_create_time); VATTR_WANTED(&va, va_modify_time); - if (VNOP_GETATTR(vp, &va, context) != 0 || - !VATTR_IS_SUPPORTED(&va, va_uid) || - !VATTR_IS_SUPPORTED(&va, va_gid) || - !VATTR_IS_SUPPORTED(&va, va_mode)) { + if (VNOP_GETATTR(vp, &va, context) != 0 || + !VATTR_IS_SUPPORTED(&va, va_uid) || + !VATTR_IS_SUPPORTED(&va, va_gid) || + !VATTR_IS_SUPPORTED(&va, va_mode)) { va.va_uid = KAUTH_UID_NONE; va.va_gid = KAUTH_GID_NONE; va.va_mode = S_IRUSR | S_IWUSR; @@ -633,25 +791,28 @@ getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, VATTR_SET(&va, va_flags, UF_HIDDEN); /* Obtain the vnode for the shadow files directory. */ - if (get_shadow_dir(&dvp, context) != 0) { + if (get_shadow_dir(&dvp) != 0) { error = ENOTDIR; goto out; } if (!makestream) { /* See if someone else already has it open. */ - if (VNOP_LOOKUP(dvp, &svp, &cn, context) == 0) { + if (VNOP_LOOKUP(dvp, &svp, &cn, kernelctx) == 0) { /* Double check existence by asking for size. */ VATTR_INIT(&va); VATTR_WANTED(&va, va_data_size); - if (VNOP_GETATTR(svp, &va, context) == 0 && + if (VNOP_GETATTR(svp, &va, context) == 0 && VATTR_IS_SUPPORTED(&va, va_data_size)) { goto out; /* OK to use. */ } } - - /* Otherwise make sure the resource fork data exists. */ + + /* + * Otherwise make sure the resource fork data exists. + * Use the supplied context for accessing the AD file. + */ error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &datasize, - XATTR_NOSECURITY, context); + XATTR_NOSECURITY, context); /* * To maintain binary compatibility with legacy Carbon * emulated resource fork support, if the resource fork @@ -660,14 +821,14 @@ getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, */ if ((error == ENOATTR) && (vn_getxattr(vp, XATTR_FINDERINFO_NAME, NULL, &datasize, - XATTR_NOSECURITY, context) == 0)) { + XATTR_NOSECURITY, context) == 0)) { datasize = 0; error = 0; } else { if (error) { goto out; } - + /* If the resource fork exists, its size is expected to be non-zero. */ if (datasize == 0) { error = ENOATTR; @@ -676,12 +837,34 @@ getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, } } /* Create the shadow stream file. */ - error = VNOP_CREATE(dvp, &svp, &cn, &va, context); + error = VNOP_CREATE(dvp, &svp, &cn, &va, kernelctx); if (error == 0) { + vnode_recycle(svp); *creator = 1; } else if ((error == EEXIST) && !makestream) { - error = VNOP_LOOKUP(dvp, &svp, &cn, context); + error = VNOP_LOOKUP(dvp, &svp, &cn, kernelctx); + } else if ((error == ENOENT) && !makestream) { + /* + * We could have raced with a rmdir on the shadow directory + * post-lookup. Retry from the beginning, 1x only, to + * try and see if we need to re-create the shadow directory + * in get_shadow_dir. + */ + if (retries == 0) { + retries++; + if (dvp) { + vnode_put(dvp); + dvp = NULLVP; + } + if (svp) { + vnode_put(svp); + svp = NULLVP; + } + goto retry_create; + } + /* Otherwise, just error out normally below */ } + out: if (dvp) { vnode_put(dvp); @@ -697,7 +880,7 @@ out: if (rsrcsize) { *rsrcsize = datasize; } - return (error); + return error; } @@ -712,26 +895,39 @@ default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsopera int creator; int error; + /* need the kernel context for accessing the shadowfile */ + vfs_context_t kernelctx = vfs_context_kernel(); + /* * Only the "com.apple.ResourceFork" stream is supported here. */ - if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { + if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { *svpp = NULLVP; - return (ENOATTR); + return ENOATTR; } retry: /* * Obtain a shadow file for the resource fork I/O. + * + * Need to pass along the supplied context so that getshadowfile + * can access the AD file as needed, using it. */ error = getshadowfile(vp, &svp, 0, &datasize, &creator, context); if (error) { *svpp = NULLVP; - return (error); + return error; } /* * The creator of the shadow file provides its file data, - * all other threads should wait until its ready. + * all other threads should wait until its ready. In order to + * prevent a deadlock during error codepaths, we need to check if the + * vnode is being created, or if it has failed out. Regardless of success or + * failure, we set the VISSHADOW bit on the vnode, so we check that + * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't, + * then we can infer the creator isn't done yet. If it's there, but + * VISNAMEDSTREAM is not set, then we can infer it errored out and we should + * try again. */ if (!creator) { vnode_lock(svp); @@ -740,9 +936,18 @@ retry: vnode_unlock(svp); goto out; } else { - /* its not ready, wait for it (sleep using v_parent as channel) */ - msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP, - "getnamedstream", NULL); + /* It's not ready, wait for it (sleep using v_parent as channel) */ + if ((svp->v_flag & VISSHADOW)) { + /* + * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other + * thread is done with this vnode. Just unlock the vnode and try again + */ + vnode_unlock(svp); + } else { + /* Otherwise, sleep if the shadow file is not created yet */ + msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP, + "getnamedstream", NULL); + } vnode_put(svp); svp = NULLVP; goto retry; @@ -754,60 +959,71 @@ retry: */ if (op == NS_OPEN && datasize != 0) { size_t offset; - size_t iosize; + size_t iosize; iosize = bufsize = MIN(datasize, NS_IOBUFSIZE); - if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) { + if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize, VM_KERN_MEMORY_FILE)) { error = ENOMEM; goto out; } - auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); + auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); offset = 0; - error = VNOP_OPEN(svp, 0, context); + /* open the shadow file */ + error = VNOP_OPEN(svp, 0, kernelctx); if (error) { goto out; } while (offset < datasize) { - size_t tmpsize; + size_t tmpsize; iosize = MIN(datasize - offset, iosize); - uio_reset(auio, offset, UIO_SYSSPACE32, UIO_READ); + uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ); uio_addiov(auio, (uintptr_t)bufptr, iosize); + /* use supplied ctx for AD file */ error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize, - XATTR_NOSECURITY, context); + XATTR_NOSECURITY, context); if (error) { break; } - - uio_reset(auio, offset, UIO_SYSSPACE32, UIO_WRITE); + + uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE); uio_addiov(auio, (uintptr_t)bufptr, iosize); - error = VNOP_WRITE(svp, auio, 0, context); + /* kernel context for writing shadowfile */ + error = VNOP_WRITE(svp, auio, 0, kernelctx); if (error) { break; } offset += iosize; } - (void) VNOP_CLOSE(svp, 0, context); + + /* close shadow file */ + (void) VNOP_CLOSE(svp, 0, kernelctx); } out: /* Wake up anyone waiting for svp file content */ if (creator) { if (error == 0) { vnode_lock(svp); - svp->v_flag |= VISNAMEDSTREAM; + /* VISSHADOW would be set later on anyway, so we set it now */ + svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW); wakeup((caddr_t)&svp->v_parent); vnode_unlock(svp); } else { - /* On post create errors, get rid of the shadow file. This - * way, if there is another process waiting for initialization - * of the shadow file by the current process, it will wake up - * and retry by creating and initializing the shadow file again. + /* On post create errors, get rid of the shadow file. This + * way if there is another process waiting for initialization + * of the shadowfile by the current process will wake up and + * retry by creating and initializing the shadow file again. + * Also add the VISSHADOW bit here to indicate we're done operating + * on this vnode. */ - (void) vnode_relenamedstream(vp, svp, context); + (void)vnode_relenamedstream(vp, svp); + vnode_lock(svp); + svp->v_flag |= VISSHADOW; wakeup((caddr_t)&svp->v_parent); + vnode_unlock(svp); } } @@ -825,7 +1041,7 @@ out: } } *svpp = svp; - return (error); + return error; } static int @@ -837,10 +1053,12 @@ default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context /* * Only the "com.apple.ResourceFork" stream is supported here. */ - if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { + if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { *svpp = NULLVP; - return (ENOATTR); + return ENOATTR; } + + /* Supply the context to getshadowfile so it can manipulate the AD file */ error = getshadowfile(vp, svpp, 1, NULL, &creator, context); /* @@ -850,22 +1068,24 @@ default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context vnode_t svp = *svpp; vnode_lock(svp); - svp->v_flag |= VISNAMEDSTREAM; + /* If we're the creator, mark it as a named stream */ + svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW); /* Wakeup any waiters on the v_parent channel */ wakeup((caddr_t)&svp->v_parent); vnode_unlock(svp); } - return (error); + + return error; } -static int +static int default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context) { /* * Only the "com.apple.ResourceFork" stream is supported here. */ - if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { - return (ENOATTR); + if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) { + return ENOATTR; } /* * XXX - what about other opened instances? @@ -874,43 +1094,65 @@ default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context) } static int -get_shadow_dir(vnode_t *sdvpp, vfs_context_t context) +get_shadow_dir(vnode_t *sdvpp) { vnode_t dvp = NULLVP; vnode_t sdvp = NULLVP; struct componentname cn; struct vnode_attr va; - char tmpname[48]; + char tmpname[80]; uint32_t tmp_fsid; int error; + vfs_context_t kernelctx = vfs_context_kernel(); - /* Check if we've already created it. */ - if (shadow_dvp != NULLVP) { - if ((error = vnode_getwithvid(shadow_dvp, shadow_vid))) { - shadow_dvp = NULLVP; - } else { - *sdvpp = shadow_dvp; - return (0); - } + bzero(tmpname, sizeof(tmpname)); + MAKE_SHADOW_DIRNAME(rootvnode, tmpname); + /* + * Look up the shadow directory to ensure that it still exists. + * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues + * in caching it when multiple threads may be trying to manipulate the pointers. + * + * Make sure to use the kernel context. We want a singular view of + * the shadow dir regardless of chrooted processes. + */ + error = vnode_lookup(tmpname, 0, &sdvp, kernelctx); + if (error == 0) { + /* + * If we get here, then we have successfully looked up the shadow dir, + * and it has an iocount from the lookup. Return the vp in the output argument. + */ + *sdvpp = sdvp; + return 0; } + /* In the failure case, no iocount is acquired */ + sdvp = NULLVP; + bzero(tmpname, sizeof(tmpname)); - /* Obtain the vnode for "/tmp" directory. */ - if (vnode_lookup("/tmp", 0, &dvp, context) != 0) { + /* + * Obtain the vnode for "/var/run" directory using the kernel + * context. + * + * This is defined in the SHADOW_DIR_CONTAINER macro + */ + if (vnode_lookup(SHADOW_DIR_CONTAINER, 0, &dvp, kernelctx) != 0) { error = ENOTSUP; goto out; } - /* Create the shadow stream directory. */ - snprintf(tmpname, sizeof(tmpname), ".vfs_rsrc_streams_%x%x", - (unsigned int)rootvnode, shadow_sequence); + /* + * Create the shadow stream directory. + * 'dvp' below suggests the parent directory so + * we only need to provide the leaf entry name + */ + MAKE_SHADOW_DIR_LEAF(rootvnode, tmpname); bzero(&cn, sizeof(cn)); cn.cn_nameiop = LOOKUP; cn.cn_flags = ISLASTCN; - cn.cn_context = context; + cn.cn_context = kernelctx; cn.cn_pnbuf = tmpname; cn.cn_pnlen = sizeof(tmpname); cn.cn_nameptr = cn.cn_pnbuf; - cn.cn_namelen = strlen(tmpname); + cn.cn_namelen = (int)strlen(tmpname); /* * owned by root, only readable by root, hidden @@ -923,30 +1165,23 @@ get_shadow_dir(vnode_t *sdvpp, vfs_context_t context) VATTR_SET(&va, va_flags, UF_HIDDEN); va.va_vaflags = VA_EXCLUSIVE; - error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, context); - + error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, kernelctx); + /* * There can be only one winner for an exclusive create. */ - if (error == 0) { - /* Take a long term ref to keep this dir around. */ - error = vnode_ref(sdvp); - if (error == 0) { - shadow_dvp = sdvp; - shadow_vid = sdvp->v_id; - } - } else if (error == EEXIST) { + if (error == EEXIST) { /* loser has to look up directory */ - error = VNOP_LOOKUP(dvp, &sdvp, &cn, context); + error = VNOP_LOOKUP(dvp, &sdvp, &cn, kernelctx); if (error == 0) { /* Make sure its in fact a directory */ if (sdvp->v_type != VDIR) { goto baddir; } - /* Obtain the fsid for /tmp directory */ + /* Obtain the fsid for /var/run directory */ VATTR_INIT(&va); VATTR_WANTED(&va, va_fsid); - if (VNOP_GETATTR(dvp, &va, context) != 0 || + if (VNOP_GETATTR(dvp, &va, kernelctx) != 0 || !VATTR_IS_SUPPORTED(&va, va_fsid)) { goto baddir; } @@ -963,18 +1198,18 @@ get_shadow_dir(vnode_t *sdvpp, vfs_context_t context) va.va_dirlinkcount = 1; va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE; - if (VNOP_GETATTR(sdvp, &va, context) != 0 || - !VATTR_IS_SUPPORTED(&va, va_uid) || - !VATTR_IS_SUPPORTED(&va, va_gid) || - !VATTR_IS_SUPPORTED(&va, va_mode) || + if (VNOP_GETATTR(sdvp, &va, kernelctx) != 0 || + !VATTR_IS_SUPPORTED(&va, va_uid) || + !VATTR_IS_SUPPORTED(&va, va_gid) || + !VATTR_IS_SUPPORTED(&va, va_mode) || !VATTR_IS_SUPPORTED(&va, va_fsid)) { goto baddir; } /* - * Make sure its what we want: - * - owned by root + * Make sure its what we want: + * - owned by root * - not writable by anyone - * - on same file system as /tmp + * - on same file system as /var/run * - not a hard-linked directory * - no ACLs (they might grant write access) */ @@ -982,7 +1217,7 @@ get_shadow_dir(vnode_t *sdvpp, vfs_context_t context) (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) || (va.va_fsid != tmp_fsid) || (va.va_dirlinkcount != 1) || - (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) { + (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) { goto baddir; } } @@ -999,7 +1234,7 @@ out: } } *sdvpp = sdvp; - return (error); + return error; baddir: /* This is not the dir we're looking for, move along */ @@ -1007,62 +1242,61 @@ baddir: error = ENOTDIR; goto out; } -#endif - +#endif /* NAMEDSTREAMS */ +#if CONFIG_APPLEDOUBLE /* - * Default Implementation (Non-native EA) + * Default Implementation (Non-native EA) */ /* - Typical "._" AppleDouble Header File layout: - ------------------------------------------------------------ - MAGIC 0x00051607 - VERSION 0x00020000 - FILLER 0 - COUNT 2 - .-- AD ENTRY[0] Finder Info Entry (must be first) - .--+-- AD ENTRY[1] Resource Fork Entry (must be last) - | '-> FINDER INFO - | ///////////// Fixed Size Data (32 bytes) - | EXT ATTR HDR - | ///////////// - | ATTR ENTRY[0] --. - | ATTR ENTRY[1] --+--. - | ATTR ENTRY[2] --+--+--. - | ... | | | - | ATTR ENTRY[N] --+--+--+--. - | ATTR DATA 0 <-' | | | - | //////////// | | | - | ATTR DATA 1 <----' | | - | ///////////// | | - | ATTR DATA 2 <-------' | - | ///////////// | - | ... | - | ATTR DATA N <----------' - | ///////////// - | Attribute Free Space - | - '----> RESOURCE FORK - ///////////// Variable Sized Data - ///////////// - ///////////// - ///////////// - ///////////// - ///////////// - ... - ///////////// - - ------------------------------------------------------------ - - NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are - stored as part of the Finder Info. The length in the Finder - Info AppleDouble entry includes the length of the extended - attribute header, attribute entries, and attribute data. -*/ - + * Typical "._" AppleDouble Header File layout: + * ------------------------------------------------------------ + * MAGIC 0x00051607 + * VERSION 0x00020000 + * FILLER 0 + * COUNT 2 + * .-- AD ENTRY[0] Finder Info Entry (must be first) + * .--+-- AD ENTRY[1] Resource Fork Entry (must be last) + * | '-> FINDER INFO + * | ///////////// Fixed Size Data (32 bytes) + * | EXT ATTR HDR + * | ///////////// + * | ATTR ENTRY[0] --. + * | ATTR ENTRY[1] --+--. + * | ATTR ENTRY[2] --+--+--. + * | ... | | | + * | ATTR ENTRY[N] --+--+--+--. + * | ATTR DATA 0 <-' | | | + * | //////////// | | | + * | ATTR DATA 1 <----' | | + * | ///////////// | | + * | ATTR DATA 2 <-------' | + * | ///////////// | + * | ... | + * | ATTR DATA N <----------' + * | ///////////// + * | Attribute Free Space + * | + * '----> RESOURCE FORK + * ///////////// Variable Sized Data + * ///////////// + * ///////////// + * ///////////// + * ///////////// + * ///////////// + * ... + * ///////////// + * + * ------------------------------------------------------------ + * + * NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are + * stored as part of the Finder Info. The length in the Finder + * Info AppleDouble entry includes the length of the extended + * attribute header, attribute entries, and attribute data. + */ /* * On Disk Data Structures @@ -1082,7 +1316,7 @@ baddir: */ #define AD_DATA 1 /* Data fork */ #define AD_RESOURCE 2 /* Resource fork */ -#define AD_REALNAME 3 /* Fileƕs name on home file system */ +#define AD_REALNAME 3 /* File's name on home file system */ #define AD_COMMENT 4 /* Standard Mac comment */ #define AD_ICONBW 5 /* Mac black & white icon */ #define AD_ICONCOLOR 6 /* Mac color icon */ @@ -1094,7 +1328,7 @@ baddir: #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */ #define AD_AFPNAME 13 /* Short name on AFP server */ #define AD_AFPINFO 14 /* AFP file info, attrib., etc */ -#define AD_AFPDIRID 15 /* AFP directory ID */ +#define AD_AFPDIRID 15 /* AFP directory ID */ #define AD_ATTRIBUTES AD_FINDERINFO @@ -1104,7 +1338,7 @@ baddir: #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */ /* Implementation Limits */ -#define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */ +#define ATTR_MAX_SIZE AD_XATTR_MAXSIZE #define ATTR_MAX_HDR_SIZE 65536 /* * Note: ATTR_MAX_HDR_SIZE is the largest attribute header @@ -1124,26 +1358,24 @@ baddir: */ -#pragma options align=mac68k - -#define FINDERINFOSIZE 32 +#define FINDERINFOSIZE 32 typedef struct apple_double_entry { - u_int32_t type; /* entry type: see list, 0 invalid */ + u_int32_t type; /* entry type: see list, 0 invalid */ u_int32_t offset; /* entry data offset from the beginning of the file. */ - u_int32_t length; /* entry data length in bytes. */ -} apple_double_entry_t; + u_int32_t length; /* entry data length in bytes. */ +} __attribute__((aligned(2), packed)) apple_double_entry_t; typedef struct apple_double_header { u_int32_t magic; /* == ADH_MAGIC */ - u_int32_t version; /* format version: 2 = 0x00020000 */ + u_int32_t version; /* format version: 2 = 0x00020000 */ u_int32_t filler[4]; - u_int16_t numEntries; /* number of entries which follow */ + u_int16_t numEntries; /* number of entries which follow */ apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */ u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */ u_int8_t pad[2]; /* get better alignment inside attr_header */ -} apple_double_header_t; +} __attribute__((aligned(2), packed)) apple_double_header_t; #define ADHDRSIZE (4+4+16+2) @@ -1154,7 +1386,7 @@ typedef struct attr_entry { u_int16_t flags; u_int8_t namelen; u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */ -} attr_entry_t; +} __attribute__((aligned(2), packed)) attr_entry_t; /* Header + entries must fit into 64K. Data may extend beyond 64K. */ @@ -1162,13 +1394,13 @@ typedef struct attr_header { apple_double_header_t appledouble; u_int32_t magic; /* == ATTR_HDR_MAGIC */ u_int32_t debug_tag; /* for debugging == file id of owning file */ - u_int32_t total_size; /* file offset of end of attribute header + entries + data */ + u_int32_t total_size; /* file offset of end of attribute header + entries + data */ u_int32_t data_start; /* file offset to attribute data area */ u_int32_t data_length; /* length of attribute data area */ u_int32_t reserved[3]; u_int16_t flags; u_int16_t num_attrs; -} attr_header_t; +} __attribute__((aligned(2), packed)) attr_header_t; /* Empty Resource Fork Header */ @@ -1190,14 +1422,12 @@ typedef struct rsrcfork_header { u_int16_t mh_Types; u_int16_t mh_Names; u_int16_t typeCount; -} rsrcfork_header_t; +} __attribute__((aligned(2), packed)) rsrcfork_header_t; #define RF_FIRST_RESOURCE 256 #define RF_NULL_MAP_LENGTH 30 #define RF_EMPTY_TAG "This resource fork intentionally left blank " -#pragma options align=reset - /* Runtime information about the attribute file. */ typedef struct attr_info { vfs_context_t context; @@ -1221,7 +1451,7 @@ typedef struct attr_info { #define ATTR_ALIGN 3L /* Use four-byte alignment */ #define ATTR_ENTRY_LENGTH(namelen) \ - ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN)) + ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN)) #define ATTR_NEXT(ae) \ (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen)) @@ -1262,8 +1492,8 @@ static int unlock_xattrfile(vnode_t xvp, vfs_context_t context); #if BYTE_ORDER == LITTLE_ENDIAN - static void swap_adhdr(apple_double_header_t *adh); - static void swap_attrhdr(attr_header_t *ah, attr_info_t* info); +static void swap_adhdr(apple_double_header_t *adh); +static void swap_attrhdr(attr_header_t *ah, attr_info_t* info); #else #define swap_adhdr(x) @@ -1288,26 +1518,28 @@ static int shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs * NOTE: Does not attempt to validate the extended attributes header that * may be embedded in the Finder Info entry. */ -static int check_and_swap_apple_double_header(attr_info_t *ainfop) +static int +check_and_swap_apple_double_header(attr_info_t *ainfop) { int i, j; u_int32_t header_end; u_int32_t entry_end; size_t rawsize; apple_double_header_t *header; - + rawsize = ainfop->rawsize; header = (apple_double_header_t *) ainfop->rawdata; - + /* Is the file big enough to contain an AppleDouble header? */ - if (rawsize < offsetof(apple_double_header_t, entries)) + if (rawsize < offsetof(apple_double_header_t, entries)) { return ENOATTR; - + } + /* Swap the AppleDouble header fields to native order */ header->magic = SWAP32(header->magic); header->version = SWAP32(header->version); header->numEntries = SWAP16(header->numEntries); - + /* Sanity check the AppleDouble header fields */ if (header->magic != ADH_MAGIC || header->version != ADH_VERSION || @@ -1315,36 +1547,36 @@ static int check_and_swap_apple_double_header(attr_info_t *ainfop) header->numEntries > 15) { return ENOATTR; } - + /* Calculate where the entries[] array ends */ header_end = offsetof(apple_double_header_t, entries) + - header->numEntries * sizeof(apple_double_entry_t); - + header->numEntries * sizeof(apple_double_entry_t); + /* Is the file big enough to contain the AppleDouble entries? */ if (rawsize < header_end) { - return ENOATTR; + return ENOATTR; } - + /* Swap and sanity check each AppleDouble entry */ - for (i=0; inumEntries; i++) { + for (i = 0; i < header->numEntries; i++) { /* Swap the per-entry fields to native order */ header->entries[i].type = SWAP32(header->entries[i].type); header->entries[i].offset = SWAP32(header->entries[i].offset); header->entries[i].length = SWAP32(header->entries[i].length); - + entry_end = header->entries[i].offset + header->entries[i].length; - + /* * Does the entry's content start within the header itself, * did the addition overflow, or does the entry's content * extend past the end of the file? */ if (header->entries[i].offset < header_end || - entry_end < header->entries[i].offset || + entry_end < header->entries[i].offset || entry_end > ainfop->filesize) { return ENOATTR; } - + /* * Does the current entry's content overlap with a previous * entry's content? @@ -1354,15 +1586,15 @@ static int check_and_swap_apple_double_header(attr_info_t *ainfop) * But we have already ensured N < 16, and N is almost always 2. * So there's no point in using a more complex algorithm. */ - - for (j=0; j header->entries[j].offset && header->entries[j].offset + header->entries[j].length > header->entries[i].offset) { return ENOATTR; } } } - + return 0; } @@ -1371,24 +1603,24 @@ static int check_and_swap_apple_double_header(attr_info_t *ainfop) /* * Retrieve the data of an extended attribute. */ -int +static int default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, - __unused int options, vfs_context_t context) + __unused int options, vfs_context_t context) { vnode_t xvp = NULL; attr_info_t ainfo; attr_header_t *header; attr_entry_t *entry; u_int8_t *attrdata; - size_t datalen; - int namelen; + u_int32_t datalen; + size_t namelen; int isrsrcfork; int fileflags; int i; int error; fileflags = FREAD; - if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { + if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { isrsrcfork = 1; /* * Open the file locked (shared) since the Carbon @@ -1401,16 +1633,15 @@ default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, } if ((error = open_xattrfile(vp, fileflags, &xvp, context))) { - return (error); + return error; } if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { close_xattrfile(xvp, fileflags, context); - return (error); + return error; } /* Get the Finder Info. */ - if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { - + if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) { error = ENOATTR; } else if (uio == NULL) { @@ -1438,12 +1669,13 @@ default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, } else { uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset); error = VNOP_READ(xvp, uio, 0, context); - if (error == 0) + if (error == 0) { uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset); + } } goto out; } - + if (ainfo.attrhdr == NULL || ainfo.attr_entry == NULL) { error = ENOATTR; goto out; @@ -1461,7 +1693,7 @@ default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, */ for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { if (strncmp((const char *)entry->name, name, namelen) == 0) { - datalen = (size_t)entry->length; + datalen = entry->length; if (uio == NULL) { *size = datalen; error = 0; @@ -1483,17 +1715,17 @@ default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, } entry = ATTR_NEXT(entry); } -out: +out: rel_xattrinfo(&ainfo); close_xattrfile(xvp, fileflags, context); - return (error); + return error; } /* * Set the data of an extended attribute. */ -int +static int default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context) { vnode_t xvp = NULL; @@ -1512,9 +1744,15 @@ default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_conte int fileflags; int error; char finfo[FINDERINFOSIZE]; - + datalen = uio_resid(uio); - namelen = strlen(name) + 1; + if (datalen > XATTR_MAXSIZE) { + return EINVAL; + } + namelen = (int)strlen(name) + 1; + if (namelen > UINT8_MAX) { + return EINVAL; + } entrylen = ATTR_ENTRY_LENGTH(namelen); /* @@ -1532,7 +1770,7 @@ default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_conte * * NOTE: this copies the Finder Info data into the "finfo" local. */ - if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { + if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { /* * TODO: check the XATTR_CREATE and XATTR_REPLACE flags. * That means we probably have to open_xattrfile and get_xattrinfo. @@ -1540,18 +1778,20 @@ default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_conte if (uio_offset(uio) != 0 || datalen != FINDERINFOSIZE) { return EINVAL; } - error = uiomove(finfo, datalen, uio); - if (error) + error = uiomove(finfo, (int)datalen, uio); + if (error) { return error; - if ((options & (XATTR_CREATE|XATTR_REPLACE)) == 0 && + } + if ((options & (XATTR_CREATE | XATTR_REPLACE)) == 0 && bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) { error = default_removexattr(vp, name, 0, context); - if (error == ENOATTR) + if (error == ENOATTR) { error = 0; + } return error; } } - + start: /* * Open the file locked since setting an attribute @@ -1559,15 +1799,15 @@ start: */ fileflags = FREAD | FWRITE | O_EXLOCK; if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xvp, context))) { - return (error); + return error; } if ((error = get_xattrinfo(xvp, ATTR_SETTING, &ainfo, context))) { close_xattrfile(xvp, fileflags, context); - return (error); + return error; } /* Set the Finder Info. */ - if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { + if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { if (ainfo.finderinfo && !ainfo.emptyfinderinfo) { /* attr exists and "create" was specified? */ if (options & XATTR_CREATE) { @@ -1597,8 +1837,9 @@ start: rel_xattrinfo(&ainfo); close_xattrfile(xvp, fileflags, context); error = default_removexattr(vp, name, 0, context); - if (error == ENOATTR) + if (error == ENOATTR) { error = 0; + } return error; } if (ainfo.finderinfo) { @@ -1613,34 +1854,52 @@ start: } /* Write the Resource Fork. */ - if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { - u_int32_t endoffset; + if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { + off_t endoffset; if (!vnode_isreg(vp)) { error = EPERM; goto out; } - if (ainfo.rsrcfork && ainfo.rsrcfork->length) { - /* attr exists and "create" was specified? */ - if (options & XATTR_CREATE) { - error = EEXIST; - goto out; + /* Make sure we have a rsrc fork pointer.. */ + if (ainfo.rsrcfork == NULL) { + error = ENOATTR; + goto out; + } + if (ainfo.rsrcfork) { + if (ainfo.rsrcfork->length != 0) { + if (options & XATTR_CREATE) { + /* attr exists, and create specified ? */ + error = EEXIST; + goto out; + } + } else { + /* Zero length AD rsrc fork */ + if (options & XATTR_REPLACE) { + /* attr doesn't exist (0-length), but replace specified ? */ + error = ENOATTR; + goto out; + } } } else { - /* attr doesn't exists and "replace" was specified? */ - if (options & XATTR_REPLACE) { - error = ENOATTR; - goto out; - } + /* We can't do much if we somehow didn't get an AD rsrc pointer */ + error = ENOATTR; + goto out; } + endoffset = uio_resid(uio) + uio_offset(uio); /* new size */ + if (endoffset > UINT32_MAX || endoffset < 0) { + error = EINVAL; + goto out; + } uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset); error = VNOP_WRITE(xvp, uio, 0, context); - if (error) + if (error) { goto out; + } uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset); if (endoffset > ainfo.rsrcfork->length) { - ainfo.rsrcfork->length = endoffset; + ainfo.rsrcfork->length = (u_int32_t)endoffset; ainfo.iosize = sizeof(attr_header_t); error = write_xattrinfo(&ainfo); goto out; @@ -1649,7 +1908,7 @@ start: } if (datalen > ATTR_MAX_SIZE) { - return (E2BIG); /* EINVAL instead ? */ + return E2BIG; /* EINVAL instead ? */ } if (ainfo.attrhdr == NULL) { @@ -1660,11 +1919,12 @@ start: entry = ainfo.attr_entry; /* Check if data area crosses the maximum header size. */ - if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE) + if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE) { splitdata = 1; /* do data I/O separately */ - else + } else { splitdata = 0; - + } + /* * See if attribute already exists. */ @@ -1691,9 +1951,10 @@ start: } } else { attrdata = (u_int8_t *)header + entry->offset; - error = uiomove((caddr_t)attrdata, datalen, uio); - if (error) + error = uiomove((caddr_t)attrdata, (int)datalen, uio); + if (error) { goto out; + } ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; error = write_xattrinfo(&ainfo); if (error) { @@ -1710,13 +1971,12 @@ start: close_xattrfile(xvp, fileflags, context); error = default_removexattr(vp, name, options, context); if (error) { - return (error); + return error; } /* Clear XATTR_REPLACE option since we just removed the attribute. */ options &= ~XATTR_REPLACE; goto start; /* start over */ } - } if (options & XATTR_REPLACE) { @@ -1736,7 +1996,7 @@ start: size_t growsize; growsize = roundup((datalen + entrylen) - datafreespace, ATTR_BUF_SIZE); - + /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */ if (!splitdata && (header->total_size + growsize) > ATTR_MAX_HDR_SIZE) { growsize = ATTR_MAX_HDR_SIZE - header->total_size; @@ -1747,8 +2007,9 @@ start: if (error) { printf("setxattr: VNOP_TRUNCATE error %d\n", error); } - if (error) + if (error) { goto out; + } /* * Move the resource fork out of the way. @@ -1756,9 +2017,9 @@ start: if (ainfo.rsrcfork) { if (ainfo.rsrcfork->length != 0) { shift_data_down(xvp, - ainfo.rsrcfork->offset, - ainfo.rsrcfork->length, - growsize, context); + ainfo.rsrcfork->offset, + ainfo.rsrcfork->length, + growsize, context); } ainfo.rsrcfork->offset += growsize; } @@ -1769,13 +2030,13 @@ start: /* Make space for a new entry. */ if (splitdata) { shift_data_down(xvp, - header->data_start, - header->data_length, - entrylen, context); + header->data_start, + header->data_length, + entrylen, context); } else { bcopy((u_int8_t *)header + header->data_start, - (u_int8_t *)header + header->data_start + entrylen, - header->data_length); + (u_int8_t *)header + header->data_start + entrylen, + header->data_length); } header->data_start += entrylen; @@ -1784,7 +2045,7 @@ start: for (entry = ainfo.attr_entry; entry != lastentry && ATTR_VALID(entry, ainfo); entry = ATTR_NEXT(entry)) { entry->offset += entrylen; } - + /* * If the attribute data area is entirely within * the header buffer, then just update the buffer, @@ -1804,8 +2065,8 @@ start: } } else { attrdata = (u_int8_t *)header + header->data_start + header->data_length; - - error = uiomove((caddr_t)attrdata, datalen, uio); + + error = uiomove((caddr_t)attrdata, (int)datalen, uio); if (error) { printf("setxattr: uiomove error %d\n", error); goto out; @@ -1813,9 +2074,9 @@ start: } /* Create the attribute entry. */ - lastentry->length = datalen; + lastentry->length = (u_int32_t)datalen; lastentry->offset = header->data_start + header->data_length; - lastentry->namelen = namelen; + lastentry->namelen = (u_int8_t)namelen; lastentry->flags = 0; bcopy(name, &lastentry->name[0], namelen); @@ -1827,7 +2088,7 @@ start: /* Only write the entries, since the data was written separately. */ ainfo.iosize = ainfo.attrhdr->data_start; } else { - /* The entry and data are both in the header; write them together. */ + /* The entry and data are both in the header; write them together. */ ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; } error = write_xattrinfo(&ainfo); @@ -1835,7 +2096,7 @@ start: printf("setxattr: write_xattrinfo error %d\n", error); } -out: +out: rel_xattrinfo(&ainfo); close_xattrfile(xvp, fileflags, context); @@ -1852,14 +2113,17 @@ out: (void) vnode_setattr(vp, &va, context); } } - return (error); + + post_event_if_success(vp, error, NOTE_ATTRIB); + + return error; } /* * Remove an extended attribute. */ -int +static int default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_context_t context) { vnode_t xvp = NULL; @@ -1881,7 +2145,7 @@ default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_cont int error; fileflags = FREAD | FWRITE; - if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { + if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) { isrsrcfork = 1; /* * Open the file locked (exclusive) since the Carbon @@ -1894,28 +2158,32 @@ default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_cont } if ((error = open_xattrfile(vp, fileflags, &xvp, context))) { - return (error); + return error; } if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { close_xattrfile(xvp, fileflags, context); - return (error); + return error; } - if (ainfo.attrhdr) + if (ainfo.attrhdr) { attrcount += ainfo.attrhdr->num_attrs; - if (ainfo.rsrcfork) + } + if (ainfo.rsrcfork) { ++attrcount; - if (ainfo.finderinfo && !ainfo.emptyfinderinfo) + } + if (ainfo.finderinfo && !ainfo.emptyfinderinfo) { ++attrcount; + } /* Clear the Finder Info. */ - if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { + if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) { if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) { error = ENOATTR; goto out; } /* On removal of last attribute the ._ file is removed. */ - if (--attrcount == 0) + if (--attrcount == 0) { goto out; + } attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset; bzero((caddr_t)attrdata, FINDERINFOSIZE); ainfo.iosize = sizeof(attr_header_t); @@ -1934,8 +2202,9 @@ default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_cont goto out; } /* On removal of last attribute the ._ file is removed. */ - if (--attrcount == 0) + if (--attrcount == 0) { goto out; + } /* * XXX * If the resource fork isn't the last AppleDouble @@ -1958,7 +2227,7 @@ default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_cont error = ENOATTR; goto out; } - namelen = strlen(name) + 1; + namelen = (int)strlen(name) + 1; header = ainfo.attrhdr; entry = ainfo.attr_entry; @@ -1968,8 +2237,9 @@ default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_cont for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { if (strncmp((const char *)entry->name, name, namelen) == 0) { found = 1; - if ((i+1) == header->num_attrs) + if ((i + 1) == header->num_attrs) { lastone = 1; + } break; } entry = ATTR_NEXT(entry); @@ -1979,51 +2249,51 @@ default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_cont goto out; } /* On removal of last attribute the ._ file is removed. */ - if (--attrcount == 0) + if (--attrcount == 0) { goto out; + } datalen = entry->length; dataoff = entry->offset; entrylen = ATTR_ENTRY_LENGTH(namelen); - if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE) + if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE) { splitdata = 1; - else + } else { splitdata = 0; + } /* Remove the attribute entry. */ if (!lastone) { bcopy((u_int8_t *)entry + entrylen, (u_int8_t *)entry, - ((size_t)header + header->data_start) - ((size_t)entry + entrylen)); + ((size_t)header + header->data_start) - ((size_t)entry + entrylen)); } /* Adjust the attribute data. */ if (splitdata) { shift_data_up(xvp, - header->data_start, - dataoff - header->data_start, - entrylen, - context); + header->data_start, + dataoff - header->data_start, + entrylen, + context); if (!lastone) { shift_data_up(xvp, - dataoff + datalen, - (header->data_start + header->data_length) - (dataoff + datalen), - datalen + entrylen, - context); + dataoff + datalen, + (header->data_start + header->data_length) - (dataoff + datalen), + datalen + entrylen, + context); } /* XXX write zeros to freed space ? */ ainfo.iosize = ainfo.attrhdr->data_start - entrylen; } else { - - bcopy((u_int8_t *)header + header->data_start, - (u_int8_t *)header + header->data_start - entrylen, - dataoff - header->data_start); + (u_int8_t *)header + header->data_start - entrylen, + dataoff - header->data_start); if (!lastone) { bcopy((u_int8_t *)header + dataoff + datalen, - (u_int8_t *)header + dataoff - entrylen, - (header->data_start + header->data_length) - (dataoff + datalen)); + (u_int8_t *)header + dataoff - entrylen, + (header->data_start + header->data_length) - (dataoff + datalen)); } - bzero (((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen)); + bzero(((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen)); ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length; } @@ -2036,8 +2306,9 @@ default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_cont entry = ainfo.attr_entry; for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) { entry->offset -= entrylen; - if (entry >= oldslot) + if (entry >= oldslot) { entry->offset -= datalen; + } entry = ATTR_NEXT(entry); } error = write_xattrinfo(&ainfo); @@ -2049,8 +2320,9 @@ out: /* When there are no more attributes remove the ._ file. */ if (attrcount == 0) { - if (fileflags & O_EXLOCK) + if (fileflags & O_EXLOCK) { (void) unlock_xattrfile(xvp, context); + } VNOP_CLOSE(xvp, fileflags, context); vnode_rele(xvp); error = remove_xattrfile(xvp, context); @@ -2071,8 +2343,10 @@ out: (void) vnode_setattr(vp, &va, context); } } - return (error); - + + post_event_if_success(vp, error, NOTE_ATTRIB); + + return error; } @@ -2095,13 +2369,17 @@ default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs */ if ((error = open_xattrfile(vp, FREAD, &xvp, context))) { - if (error == ENOATTR) + if (error == ENOATTR) { error = 0; - return (error); + } + return error; } if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { + if (error == ENOATTR) { + error = 0; + } close_xattrfile(xvp, FREAD, context); - return (error); + return error; } /* Check for Finder Info. */ @@ -2113,7 +2391,7 @@ default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs goto out; } else { error = uiomove(XATTR_FINDERINFO_NAME, - sizeof(XATTR_FINDERINFO_NAME), uio); + sizeof(XATTR_FINDERINFO_NAME), uio); if (error) { error = ERANGE; goto out; @@ -2130,7 +2408,7 @@ default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs goto out; } else { error = uiomove(XATTR_RESOURCEFORK_NAME, - sizeof(XATTR_RESOURCEFORK_NAME), uio); + sizeof(XATTR_RESOURCEFORK_NAME), uio); if (error) { error = ERANGE; goto out; @@ -2143,7 +2421,9 @@ default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs count = ainfo.attrhdr->num_attrs; for (i = 0, entry = ainfo.attr_entry; i < count && ATTR_VALID(entry, ainfo); i++) { if (xattr_protected((const char *)entry->name) || - xattr_validatename((const char *)entry->name) != 0) { + ((entry->namelen < XATTR_MAXNAMELEN) && + (entry->name[entry->namelen] == '\0') && + (xattr_validatename((const char *)entry->name) != 0))) { entry = ATTR_NEXT(entry); continue; } @@ -2158,18 +2438,19 @@ default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs } error = uiomove((caddr_t) entry->name, entry->namelen, uio); if (error) { - if (error != EFAULT) + if (error != EFAULT) { error = ERANGE; + } break; - } + } entry = ATTR_NEXT(entry); } } -out: +out: rel_xattrinfo(&ainfo); close_xattrfile(xvp, FREAD, context); - return (error); + return error; } static int @@ -2177,8 +2458,8 @@ open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context) { vnode_t xvp = NULLVP; vnode_t dvp = NULLVP; - struct vnode_attr va; - struct nameidata nd; + struct vnode_attr *va = NULL; + struct nameidata *nd = NULL; char smallname[64]; char *filename = NULL; const char *basename = NULL; @@ -2196,11 +2477,11 @@ open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context) dvp = vp; /* the "._." file resides in the root dir */ goto lookup; } - if ( (dvp = vnode_getparent(vp)) == NULLVP) { + if ((dvp = vnode_getparent(vp)) == NULLVP) { error = ENOATTR; goto out; } - if ( (basename = vnode_getname(vp)) == NULL) { + if ((basename = vnode_getname(vp)) == NULL) { error = ENOATTR; goto out; } @@ -2215,7 +2496,7 @@ open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context) len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename); if (len >= sizeof(smallname)) { len++; /* snprintf result doesn't include '\0' */ - MALLOC(filename, char *, len, M_TEMP, M_WAITOK); + filename = kheap_alloc(KHEAP_TEMP, len, Z_WAITOK); len = snprintf(filename, len, "%s%s", ATTR_FILE_PREFIX, basename); } /* @@ -2227,75 +2508,88 @@ open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context) * file security from the EA must always get access */ lookup: - NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH, UIO_SYSSPACE, - CAST_USER_ADDR_T(filename), context); - nd.ni_dvp = dvp; + nd = kheap_alloc(KHEAP_TEMP, sizeof(struct nameidata), Z_WAITOK); + NDINIT(nd, LOOKUP, OP_OPEN, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH, + UIO_SYSSPACE, CAST_USER_ADDR_T(filename), context); + nd->ni_dvp = dvp; + + va = kheap_alloc(KHEAP_TEMP, sizeof(struct vnode_attr), Z_WAITOK); if (fileflags & O_CREAT) { - nd.ni_cnd.cn_nameiop = CREATE; + nd->ni_cnd.cn_nameiop = CREATE; +#if CONFIG_TRIGGERS + nd->ni_op = OP_LINK; +#endif if (dvp != vp) { - nd.ni_cnd.cn_flags |= LOCKPARENT; + nd->ni_cnd.cn_flags |= LOCKPARENT; } - if ( (error = namei(&nd))) { - nd.ni_dvp = NULLVP; + if ((error = namei(nd))) { + nd->ni_dvp = NULLVP; error = ENOATTR; goto out; } - if ( (xvp = nd.ni_vp) == NULLVP) { + if ((xvp = nd->ni_vp) == NULLVP) { uid_t uid; gid_t gid; mode_t umode; - + /* * Pick up uid/gid/mode from target file. */ - VATTR_INIT(&va); - VATTR_WANTED(&va, va_uid); - VATTR_WANTED(&va, va_gid); - VATTR_WANTED(&va, va_mode); - if (VNOP_GETATTR(vp, &va, context) == 0 && - VATTR_IS_SUPPORTED(&va, va_uid) && - VATTR_IS_SUPPORTED(&va, va_gid) && - VATTR_IS_SUPPORTED(&va, va_mode)) { - uid = va.va_uid; - gid = va.va_gid; - umode = va.va_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); - } else /* fallback values */ { + VATTR_INIT(va); + VATTR_WANTED(va, va_uid); + VATTR_WANTED(va, va_gid); + VATTR_WANTED(va, va_mode); + if (VNOP_GETATTR(vp, va, context) == 0 && + VATTR_IS_SUPPORTED(va, va_uid) && + VATTR_IS_SUPPORTED(va, va_gid) && + VATTR_IS_SUPPORTED(va, va_mode)) { + uid = va->va_uid; + gid = va->va_gid; + umode = va->va_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + } else { /* fallback values */ uid = KAUTH_UID_NONE; gid = KAUTH_GID_NONE; - umode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; + umode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; } - VATTR_INIT(&va); - VATTR_SET(&va, va_type, VREG); - VATTR_SET(&va, va_mode, umode); - if (uid != KAUTH_UID_NONE) - VATTR_SET(&va, va_uid, uid); - if (gid != KAUTH_GID_NONE) - VATTR_SET(&va, va_gid, gid); - - error = vn_create(dvp, &nd.ni_vp, &nd.ni_cnd, &va, - VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL, - context); - if (error == 0) - xvp = nd.ni_vp; - } - nameidone(&nd); + VATTR_INIT(va); + VATTR_SET(va, va_type, VREG); + VATTR_SET(va, va_mode, umode); + if (uid != KAUTH_UID_NONE) { + VATTR_SET(va, va_uid, uid); + } + if (gid != KAUTH_GID_NONE) { + VATTR_SET(va, va_gid, gid); + } + + error = vn_create(dvp, &nd->ni_vp, nd, va, + VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL, + 0, NULL, + context); + if (error) { + error = ENOATTR; + } else { + xvp = nd->ni_vp; + } + } + nameidone(nd); if (dvp != vp) { vnode_put(dvp); /* drop iocount from LOCKPARENT request above */ } - if (error) - goto out; + if (error) { + goto out; + } } else { - if ((error = namei(&nd))) { - nd.ni_dvp = NULLVP; + if ((error = namei(nd))) { + nd->ni_dvp = NULLVP; error = ENOATTR; - goto out; + goto out; } - xvp = nd.ni_vp; - nameidone(&nd); + xvp = nd->ni_vp; + nameidone(nd); } - nd.ni_dvp = NULLVP; + nd->ni_dvp = NULLVP; if (xvp->v_type != VREG) { error = ENOATTR; @@ -2304,20 +2598,20 @@ lookup: /* * Owners must match. */ - VATTR_INIT(&va); - VATTR_WANTED(&va, va_uid); - if (VNOP_GETATTR(vp, &va, context) == 0 && VATTR_IS_SUPPORTED(&va, va_uid)) { - uid_t owner = va.va_uid; - - VATTR_INIT(&va); - VATTR_WANTED(&va, va_uid); - if (VNOP_GETATTR(xvp, &va, context) == 0 && (owner != va.va_uid)) { + VATTR_INIT(va); + VATTR_WANTED(va, va_uid); + if (VNOP_GETATTR(vp, va, context) == 0 && VATTR_IS_SUPPORTED(va, va_uid)) { + uid_t owner = va->va_uid; + + VATTR_INIT(va); + VATTR_WANTED(va, va_uid); + if (VNOP_GETATTR(xvp, va, context) == 0 && (owner != va->va_uid)) { error = ENOATTR; /* don't use this "._" file */ goto out; } } - - if ( (error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) { + + if ((error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) { error = ENOATTR; goto out; } @@ -2330,48 +2624,49 @@ lookup: /* If create was requested, make sure file header exists. */ if (fileflags & O_CREAT) { - VATTR_INIT(&va); - VATTR_WANTED(&va, va_data_size); - VATTR_WANTED(&va, va_fileid); - VATTR_WANTED(&va, va_nlink); - if ( (error = vnode_getattr(xvp, &va, context)) != 0) { + VATTR_INIT(va); + VATTR_WANTED(va, va_data_size); + VATTR_WANTED(va, va_fileid); + VATTR_WANTED(va, va_nlink); + if ((error = vnode_getattr(xvp, va, context)) != 0) { error = EPERM; goto out; } - + /* If the file is empty then add a default header. */ - if (va.va_data_size == 0) { + if (va->va_data_size == 0) { /* Don't adopt hard-linked "._" files. */ - if (VATTR_IS_SUPPORTED(&va, va_nlink) && va.va_nlink > 1) { + if (VATTR_IS_SUPPORTED(va, va_nlink) && va->va_nlink > 1) { error = EPERM; goto out; } - if ( (error = create_xattrfile(xvp, (u_int32_t)va.va_fileid, context))) + if ((error = create_xattrfile(xvp, (u_int32_t)va->va_fileid, context))) { goto out; + } } } - /* Apply file locking if requested. */ + /* Apply file locking if requested. */ if (fileflags & (O_EXLOCK | O_SHLOCK)) { short locktype; locktype = (fileflags & O_EXLOCK) ? F_WRLCK : F_RDLCK; error = lock_xattrfile(xvp, locktype, context); + if (error) { + error = ENOATTR; + } } out: - if (dvp && (dvp != vp)) { - vnode_put(dvp); - } - if (basename) { - vnode_putname(basename); - } - if (filename && filename != &smallname[0]) { - FREE(filename, M_TEMP); - } if (error) { if (xvp != NULLVP) { if (opened) { (void) VNOP_CLOSE(xvp, fileflags, context); } + + if (fileflags & O_CREAT) { + /* Delete the xattr file if we encountered any errors */ + (void) remove_xattrfile(xvp, context); + } + if (referenced) { (void) vnode_rele(xvp); } @@ -2382,8 +2677,21 @@ out: error = EPERM; } } + /* Release resources after error-handling */ + kheap_free(KHEAP_TEMP, nd, sizeof(struct nameidata)); + kheap_free(KHEAP_TEMP, va, sizeof(struct vnode_attr)); + if (dvp && (dvp != vp)) { + vnode_put(dvp); + } + if (basename) { + vnode_putname(basename); + } + if (filename && filename != &smallname[0]) { + kheap_free(KHEAP_TEMP, filename, len); + } + *xvpp = xvp; /* return a referenced vnode */ - return (error); + return error; } static void @@ -2392,8 +2700,9 @@ close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context) // if (fileflags & FWRITE) // (void) VNOP_FSYNC(xvp, MNT_WAIT, context); - if (fileflags & (O_EXLOCK | O_SHLOCK)) + if (fileflags & (O_EXLOCK | O_SHLOCK)) { (void) unlock_xattrfile(xvp, context); + } (void) VNOP_CLOSE(xvp, fileflags, context); (void) vnode_rele(xvp); @@ -2409,23 +2718,20 @@ remove_xattrfile(vnode_t xvp, vfs_context_t context) int pathlen; int error = 0; - MALLOC_ZONE(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); - if (path == NULL) - return ENOMEM; - + path = zalloc(ZV_NAMEI); pathlen = MAXPATHLEN; error = vn_getpath(xvp, path, &pathlen); if (error) { - FREE_ZONE(path, MAXPATHLEN, M_NAMEI); - return (error); + zfree(ZV_NAMEI, path); + return error; } - NDINIT(&nd, DELETE, LOCKPARENT | NOFOLLOW | DONOTAUTH, - UIO_SYSSPACE, CAST_USER_ADDR_T(path), context); + NDINIT(&nd, DELETE, OP_UNLINK, LOCKPARENT | NOFOLLOW | DONOTAUTH, + UIO_SYSSPACE, CAST_USER_ADDR_T(path), context); error = namei(&nd); - FREE_ZONE(path, MAXPATHLEN, M_NAMEI); + zfree(ZV_NAMEI, path); if (error) { - return (error); + return error; } dvp = nd.ni_dvp; xvp = nd.ni_vp; @@ -2435,7 +2741,7 @@ remove_xattrfile(vnode_t xvp, vfs_context_t context) vnode_put(dvp); vnode_put(xvp); - return (error); + return error; } /* @@ -2464,7 +2770,7 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte void * buffer = NULL; apple_double_header_t *filehdr; struct vnode_attr va; - size_t iosize; + size_t iosize = 0; int i; int error; @@ -2480,23 +2786,24 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte ainfop->filesize = va.va_data_size; /* When setting attributes, allow room for the header to grow. */ - if (setting) + if (setting) { iosize = ATTR_MAX_HDR_SIZE; - else + } else { iosize = MIN(ATTR_MAX_HDR_SIZE, ainfop->filesize); + } if (iosize == 0) { error = ENOATTR; goto bail; } ainfop->iosize = iosize; - MALLOC(buffer, void *, iosize, M_TEMP, M_WAITOK); - if (buffer == NULL){ + buffer = kheap_alloc(KHEAP_DATA_BUFFERS, iosize, Z_WAITOK); + if (buffer == NULL) { error = ENOMEM; goto bail; } - auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); + auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); uio_addiov(auio, (uintptr_t)buffer, iosize); /* Read the file header. */ @@ -2506,13 +2813,14 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte } ainfop->rawsize = iosize - uio_resid(auio); ainfop->rawdata = (u_int8_t *)buffer; - + filehdr = (apple_double_header_t *)buffer; error = check_and_swap_apple_double_header(ainfop); - if (error) + if (error) { goto bail; - + } + ainfop->filehdr = filehdr; /* valid AppleDouble header */ /* rel_xattrinfo is responsible for freeing the header buffer */ @@ -2524,21 +2832,27 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte filehdr->entries[i].length >= FINDERINFOSIZE) { /* We found the Finder Info entry. */ ainfop->finderinfo = &filehdr->entries[i]; - - /* - * Is the Finder Info "empty" (all zeroes)? If so, - * we'll pretend like the Finder Info extended attribute - * does not exist. + + /* At this point check_and_swap_apple_double_header() call above + * verified that all apple double entires are valid: + * they point somewhere within the file. * - * Note: we have to make sure the Finder Info is - * contained within the buffer we have already read, - * to avoid accidentally accessing a bogus address. - * If it is outside the buffer, we just assume the - * Finder Info is non-empty. + * Now for finderinfo make sure that the fixed portion + * is within the buffer we read in. */ - if (ainfop->finderinfo->offset + FINDERINFOSIZE <= ainfop->rawsize && - bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) { - ainfop->emptyfinderinfo = 1; + if (((ainfop->finderinfo->offset + FINDERINFOSIZE) > ainfop->finderinfo->offset) && + ((ainfop->finderinfo->offset + FINDERINFOSIZE) <= ainfop->rawsize)) { + /* + * Is the Finder Info "empty" (all zeroes)? If so, + * we'll pretend like the Finder Info extended attribute + * does not exist. + */ + if (bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) { + ainfop->emptyfinderinfo = 1; + } + } else { + error = ENOATTR; + goto bail; } } if (filehdr->entries[i].type == AD_RESOURCE) { @@ -2547,9 +2861,10 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte * we need to remember the resource fork entry so it can be * updated once the new content has been written. */ - if (filehdr->entries[i].length == 0 && !setting) + if (filehdr->entries[i].length == 0 && !setting) { continue; - + } + /* * Check to see if any "empty" resource fork is ours (i.e. is ignorable). * @@ -2563,7 +2878,7 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte /* Read the system data which starts at byte 16 */ - rf_uio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ); + rf_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData)); uio_setoffset(rf_uio, filehdr->entries[i].offset + 16); rf_err = VNOP_READ(xvp, rf_uio, 0, context); @@ -2582,7 +2897,7 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte continue; } } - + /* * See if this file looks like it is laid out correctly to contain * extended attributes. If so, then do the following: @@ -2608,36 +2923,36 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte if (setting && ainfop->finderinfo->length == FINDERINFOSIZE) { size_t delta; size_t writesize; - + delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE); if (ainfop->rsrcfork && filehdr->entries[1].length) { /* Make some room before existing resource fork. */ shift_data_down(xvp, - filehdr->entries[1].offset, - filehdr->entries[1].length, - delta, context); + filehdr->entries[1].offset, + filehdr->entries[1].length, + delta, context); writesize = sizeof(attr_header_t); } else { /* Create a new, empty resource fork. */ rsrcfork_header_t *rsrcforkhdr; - + vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context); - + /* Steal some space for an empty RF header. */ delta -= sizeof(rsrcfork_header_t); - + bzero(&attrhdr->appledouble.pad[0], delta); rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta); - + /* Fill in Empty Resource Fork Header. */ init_empty_resource_fork(rsrcforkhdr); - + filehdr->entries[1].length = sizeof(rsrcfork_header_t); writesize = ATTR_BUF_SIZE; } filehdr->entries[0].length += delta; filehdr->entries[1].offset += delta; - + /* Fill in Attribute Header. */ attrhdr->magic = ATTR_HDR_MAGIC; attrhdr->debug_tag = (u_int32_t)va.va_fileid; @@ -2649,15 +2964,15 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte attrhdr->reserved[2] = 0; attrhdr->flags = 0; attrhdr->num_attrs = 0; - + /* Push out new header */ - uio_reset(auio, 0, UIO_SYSSPACE32, UIO_WRITE); + uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE); uio_addiov(auio, (uintptr_t)filehdr, writesize); - - swap_adhdr(filehdr); /* to big endian */ - swap_attrhdr(attrhdr, ainfop); /* to big endian */ + + swap_adhdr(filehdr); /* to big endian */ + swap_attrhdr(attrhdr, ainfop); /* to big endian */ error = VNOP_WRITE(xvp, auio, 0, context); - swap_adhdr(filehdr); /* back to native */ + swap_adhdr(filehdr); /* back to native */ /* The attribute header gets swapped below. */ } } @@ -2676,8 +2991,8 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte * header was found. */ if (ainfop->finderinfo && - ainfop->finderinfo == &filehdr->entries[0] && - ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) { + ainfop->finderinfo == &filehdr->entries[0] && + ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) { attr_header_t *attrhdr = (attr_header_t*)filehdr; if ((error = check_and_swap_attrhdr(attrhdr, ainfop)) == 0) { @@ -2689,11 +3004,11 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte error = 0; bail: - if (auio != NULL) + if (auio != NULL) { uio_free(auio); - if (buffer != NULL) - FREE(buffer, M_TEMP); - return (error); + } + kheap_free(KHEAP_DATA_BUFFERS, buffer, iosize); + return error; } @@ -2707,65 +3022,70 @@ create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context) int rsrcforksize; int error; - MALLOC(buffer, void *, ATTR_BUF_SIZE, M_TEMP, M_WAITOK); + buffer = kheap_alloc(KHEAP_TEMP, ATTR_BUF_SIZE, Z_WAITOK); bzero(buffer, ATTR_BUF_SIZE); xah = (attr_header_t *)buffer; - auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_WRITE); + auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE); uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE); rsrcforksize = sizeof(rsrcfork_header_t); rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize); /* Fill in Apple Double Header. */ - xah->appledouble.magic = SWAP32 (ADH_MAGIC); - xah->appledouble.version = SWAP32 (ADH_VERSION); - xah->appledouble.numEntries = SWAP16 (2); - xah->appledouble.entries[0].type = SWAP32 (AD_FINDERINFO); - xah->appledouble.entries[0].offset = SWAP32 (offsetof(apple_double_header_t, finfo)); - xah->appledouble.entries[0].length = SWAP32 (ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize); - xah->appledouble.entries[1].type = SWAP32 (AD_RESOURCE); - xah->appledouble.entries[1].offset = SWAP32 (ATTR_BUF_SIZE - rsrcforksize); - xah->appledouble.entries[1].length = SWAP32 (rsrcforksize); + xah->appledouble.magic = SWAP32(ADH_MAGIC); + xah->appledouble.version = SWAP32(ADH_VERSION); + xah->appledouble.numEntries = SWAP16(2); + xah->appledouble.entries[0].type = SWAP32(AD_FINDERINFO); + xah->appledouble.entries[0].offset = SWAP32(offsetof(apple_double_header_t, finfo)); + xah->appledouble.entries[0].length = SWAP32(ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize); + xah->appledouble.entries[1].type = SWAP32(AD_RESOURCE); + xah->appledouble.entries[1].offset = SWAP32(ATTR_BUF_SIZE - rsrcforksize); + xah->appledouble.entries[1].length = SWAP32(rsrcforksize); bcopy(ADH_MACOSX, xah->appledouble.filler, sizeof(xah->appledouble.filler)); /* Fill in Attribute Header. */ - xah->magic = SWAP32 (ATTR_HDR_MAGIC); - xah->debug_tag = SWAP32 (fileid); - xah->total_size = SWAP32 (ATTR_BUF_SIZE - rsrcforksize); - xah->data_start = SWAP32 (sizeof(attr_header_t)); + xah->magic = SWAP32(ATTR_HDR_MAGIC); + xah->debug_tag = SWAP32(fileid); + xah->total_size = SWAP32(ATTR_BUF_SIZE - rsrcforksize); + xah->data_start = SWAP32(sizeof(attr_header_t)); /* Fill in Empty Resource Fork Header. */ init_empty_resource_fork(rsrcforkhdr); /* Push it out. */ - error = VNOP_WRITE(xvp, auio, 0, context); + error = VNOP_WRITE(xvp, auio, IO_UNIT, context); + + /* Did we write out the full uio? */ + if (uio_resid(auio) > 0) { + error = ENOSPC; + } uio_free(auio); - FREE(buffer, M_TEMP); + kheap_free(KHEAP_TEMP, buffer, ATTR_BUF_SIZE); - return (error); + return error; } static void init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr) { bzero(rsrcforkhdr, sizeof(rsrcfork_header_t)); - rsrcforkhdr->fh_DataOffset = SWAP32 (RF_FIRST_RESOURCE); - rsrcforkhdr->fh_MapOffset = SWAP32 (RF_FIRST_RESOURCE); - rsrcforkhdr->fh_MapLength = SWAP32 (RF_NULL_MAP_LENGTH); - rsrcforkhdr->mh_DataOffset = SWAP32 (RF_FIRST_RESOURCE); - rsrcforkhdr->mh_MapOffset = SWAP32 (RF_FIRST_RESOURCE); - rsrcforkhdr->mh_MapLength = SWAP32 (RF_NULL_MAP_LENGTH); - rsrcforkhdr->mh_Types = SWAP16 (RF_NULL_MAP_LENGTH - 2 ); - rsrcforkhdr->mh_Names = SWAP16 (RF_NULL_MAP_LENGTH); - rsrcforkhdr->typeCount = SWAP16 (-1); + rsrcforkhdr->fh_DataOffset = SWAP32(RF_FIRST_RESOURCE); + rsrcforkhdr->fh_MapOffset = SWAP32(RF_FIRST_RESOURCE); + rsrcforkhdr->fh_MapLength = SWAP32(RF_NULL_MAP_LENGTH); + rsrcforkhdr->mh_DataOffset = SWAP32(RF_FIRST_RESOURCE); + rsrcforkhdr->mh_MapOffset = SWAP32(RF_FIRST_RESOURCE); + rsrcforkhdr->mh_MapLength = SWAP32(RF_NULL_MAP_LENGTH); + rsrcforkhdr->mh_Types = SWAP16(RF_NULL_MAP_LENGTH - 2 ); + rsrcforkhdr->mh_Names = SWAP16(RF_NULL_MAP_LENGTH); + rsrcforkhdr->typeCount = SWAP16(-1); bcopy(RF_EMPTY_TAG, rsrcforkhdr->systemData, sizeof(RF_EMPTY_TAG)); } static void rel_xattrinfo(attr_info_t *ainfop) { - FREE(ainfop->filehdr, M_TEMP); + kheap_free_addr(KHEAP_DATA_BUFFERS, ainfop->filehdr); bzero(ainfop, sizeof(attr_info_t)); } @@ -2775,7 +3095,7 @@ write_xattrinfo(attr_info_t *ainfop) uio_t auio; int error; - auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_WRITE); + auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE); uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize); swap_adhdr(ainfop->filehdr); @@ -2789,14 +3109,14 @@ write_xattrinfo(attr_info_t *ainfop) if (ainfop->attrhdr != NULL) { swap_attrhdr(ainfop->attrhdr, ainfop); } - uio_free(auio); + uio_free(auio); - return (error); + return error; } #if BYTE_ORDER == LITTLE_ENDIAN /* - * Endian swap apple double header + * Endian swap apple double header */ static void swap_adhdr(apple_double_header_t *adh) @@ -2806,19 +3126,19 @@ swap_adhdr(apple_double_header_t *adh) count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries); - adh->magic = SWAP32 (adh->magic); - adh->version = SWAP32 (adh->version); - adh->numEntries = SWAP16 (adh->numEntries); + adh->magic = SWAP32(adh->magic); + adh->version = SWAP32(adh->version); + adh->numEntries = SWAP16(adh->numEntries); for (i = 0; i < count; i++) { - adh->entries[i].type = SWAP32 (adh->entries[i].type); - adh->entries[i].offset = SWAP32 (adh->entries[i].offset); - adh->entries[i].length = SWAP32 (adh->entries[i].length); + adh->entries[i].type = SWAP32(adh->entries[i].type); + adh->entries[i].offset = SWAP32(adh->entries[i].offset); + adh->entries[i].length = SWAP32(adh->entries[i].length); } } /* - * Endian swap extended attributes header + * Endian swap extended attributes header */ static void swap_attrhdr(attr_header_t *ah, attr_info_t* info) @@ -2829,19 +3149,19 @@ swap_attrhdr(attr_header_t *ah, attr_info_t* info) count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs); - ah->magic = SWAP32 (ah->magic); - ah->debug_tag = SWAP32 (ah->debug_tag); - ah->total_size = SWAP32 (ah->total_size); - ah->data_start = SWAP32 (ah->data_start); - ah->data_length = SWAP32 (ah->data_length); - ah->flags = SWAP16 (ah->flags); - ah->num_attrs = SWAP16 (ah->num_attrs); + ah->magic = SWAP32(ah->magic); + ah->debug_tag = SWAP32(ah->debug_tag); + ah->total_size = SWAP32(ah->total_size); + ah->data_start = SWAP32(ah->data_start); + ah->data_length = SWAP32(ah->data_length); + ah->flags = SWAP16(ah->flags); + ah->num_attrs = SWAP16(ah->num_attrs); ae = (attr_entry_t *)(&ah[1]); for (i = 0; i < count && ATTR_VALID(ae, *info); i++, ae = ATTR_NEXT(ae)) { - ae->offset = SWAP32 (ae->offset); - ae->length = SWAP32 (ae->length); - ae->flags = SWAP16 (ae->flags); + ae->offset = SWAP32(ae->offset); + ae->length = SWAP32(ae->length); + ae->flags = SWAP16(ae->flags); } } #endif @@ -2863,20 +3183,22 @@ check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop) int count; int i; - if (ah == NULL) + if (ah == NULL) { return EINVAL; + } - if (SWAP32(ah->magic) != ATTR_HDR_MAGIC) + if (SWAP32(ah->magic) != ATTR_HDR_MAGIC) { return EINVAL; - + } + /* Swap the basic header fields */ - ah->magic = SWAP32(ah->magic); - ah->debug_tag = SWAP32 (ah->debug_tag); - ah->total_size = SWAP32 (ah->total_size); - ah->data_start = SWAP32 (ah->data_start); - ah->data_length = SWAP32 (ah->data_length); - ah->flags = SWAP16 (ah->flags); - ah->num_attrs = SWAP16 (ah->num_attrs); + ah->magic = SWAP32(ah->magic); + ah->debug_tag = SWAP32(ah->debug_tag); + ah->total_size = SWAP32(ah->total_size); + ah->data_start = SWAP32(ah->data_start); + ah->data_length = SWAP32(ah->data_length); + ah->flags = SWAP16(ah->flags); + ah->num_attrs = SWAP16(ah->num_attrs); /* * Make sure the total_size fits within the Finder Info area, and the @@ -2888,47 +3210,49 @@ check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop) end > ah->total_size) { return EINVAL; } - + /* * Make sure each of the attr_entry_t's fits within total_size. */ buf_end = ainfop->rawdata + ah->total_size; count = ah->num_attrs; ae = (attr_entry_t *)(&ah[1]); - - for (i=0; i buf_end) + if ((u_int8_t *) &ae[1] > buf_end) { return EINVAL; - + } + /* Make sure the variable-length name fits (+1 is for NUL terminator) */ - /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */ - if (&ae->name[ae->namelen+1] > buf_end) + if (&ae->name[ae->namelen + 1] > buf_end) { return EINVAL; - + } + + /* Make sure that namelen is matching name's real length, namelen included NUL */ + if (strnlen((const char *)ae->name, ae->namelen) != ae->namelen - 1) { + return EINVAL; + } + + /* Swap the attribute entry fields */ - ae->offset = SWAP32(ae->offset); - ae->length = SWAP32(ae->length); - ae->flags = SWAP16(ae->flags); - - /* Make sure the attribute content fits. */ + ae->offset = SWAP32(ae->offset); + ae->length = SWAP32(ae->length); + ae->flags = SWAP16(ae->flags); + + /* Make sure the attribute content fits and points to the data part */ end = ae->offset + ae->length; - if (end < ae->offset || end > ah->total_size) + if (end < ae->offset || end > ah->total_size) { return EINVAL; - + } + + /* Make sure entry points to data section and not header */ + if (ae->offset < ah->data_start) { + return EINVAL; + } + ae = ATTR_NEXT(ae); } - - /* - * TODO: Make sure the contents of attributes don't overlap the header - * and don't overlap each other. The hard part is that we don't know - * what the actual header size is until we have looped over all of the - * variable-sized attribute entries. - * - * XXX Is there any guarantee that attribute entries are stored in - * XXX order sorted by the contents' file offset? If so, that would - * XXX make the pairwise overlap check much easier. - */ return 0; } @@ -2948,41 +3272,41 @@ shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t size_t chunk, orig_chunk; char *buff; off_t pos; - ucred_t ucred = vfs_context_ucred(context); + kauth_cred_t ucred = vfs_context_ucred(context); proc_t p = vfs_context_proc(context); - + if (delta == 0 || len == 0) { return 0; } - + chunk = 4096; if (len < chunk) { chunk = len; } orig_chunk = chunk; - if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk)) { + if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk, VM_KERN_MEMORY_FILE)) { return ENOMEM; } - for(pos=start+len-chunk; pos >= start; pos-=chunk) { - ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p); + for (pos = start + len - chunk; pos >= start; pos -= chunk) { + ret = vn_rdwr(UIO_READ, xvp, buff, (int)chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p); if (iolen != 0) { printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n", - pos, ret, chunk, ret); + pos, ret, chunk, ret); break; } - - ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p); + + ret = vn_rdwr(UIO_WRITE, xvp, buff, (int)chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p); if (iolen != 0) { printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n", - pos+delta, ret, chunk, ret); + pos + delta, ret, chunk, ret); break; } - - if ((pos - chunk) < start) { + + if ((pos - (off_t)chunk) < start) { chunk = pos - start; - + if (chunk == 0) { // we're all done break; } @@ -3002,13 +3326,13 @@ shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t c char *buff; off_t pos; off_t end; - ucred_t ucred = vfs_context_ucred(context); + kauth_cred_t ucred = vfs_context_ucred(context); proc_t p = vfs_context_proc(context); - + if (delta == 0 || len == 0) { return 0; } - + chunk = 4096; if (len < chunk) { chunk = len; @@ -3016,28 +3340,28 @@ shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t c orig_chunk = chunk; end = start + len; - if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk)) { + if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk, VM_KERN_MEMORY_FILE)) { return ENOMEM; } - for(pos = start; pos < end; pos += chunk) { - ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p); + for (pos = start; pos < end; pos += chunk) { + ret = vn_rdwr(UIO_READ, xvp, buff, (int)chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p); if (iolen != 0) { printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n", - pos, ret, chunk, ret); + pos, ret, chunk, ret); break; } - - ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED|IO_NOAUTH, ucred, &iolen, p); + + ret = vn_rdwr(UIO_WRITE, xvp, buff, (int)chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p); if (iolen != 0) { printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n", - pos+delta, ret, chunk, ret); + pos + delta, ret, chunk, ret); break; } - - if ((pos + chunk) > end) { + + if ((pos + (off_t)chunk) > end) { chunk = end - pos; - + if (chunk == 0) { // we're all done break; } @@ -3059,11 +3383,11 @@ lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context) lf.l_len = 0; lf.l_type = locktype; /* F_WRLCK or F_RDLCK */ /* Note: id is just a kernel address that's not a proc */ - error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_SETLK, &lf, F_FLOCK|F_WAIT, context); - return (error == ENOTSUP ? 0 : error); + error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_SETLK, &lf, F_FLOCK | F_WAIT, context, NULL); + return error == ENOTSUP ? 0 : error; } -static int +int unlock_xattrfile(vnode_t xvp, vfs_context_t context) { struct flock lf; @@ -3074,7 +3398,41 @@ unlock_xattrfile(vnode_t xvp, vfs_context_t context) lf.l_len = 0; lf.l_type = F_UNLCK; /* Note: id is just a kernel address that's not a proc */ - error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_UNLCK, &lf, F_FLOCK, context); - return (error == ENOTSUP ? 0 : error); + error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_UNLCK, &lf, F_FLOCK, context, NULL); + return error == ENOTSUP ? 0 : error; +} + +#else /* CONFIG_APPLEDOUBLE */ + + +static int +default_getxattr(__unused vnode_t vp, __unused const char *name, + __unused uio_t uio, __unused size_t *size, __unused int options, + __unused vfs_context_t context) +{ + return ENOTSUP; +} + +static int +default_setxattr(__unused vnode_t vp, __unused const char *name, + __unused uio_t uio, __unused int options, __unused vfs_context_t context) +{ + return ENOTSUP; +} + +static int +default_listxattr(__unused vnode_t vp, + __unused uio_t uio, __unused size_t *size, __unused int options, + __unused vfs_context_t context) +{ + return ENOTSUP; +} + +static int +default_removexattr(__unused vnode_t vp, __unused const char *name, + __unused int options, __unused vfs_context_t context) +{ + return ENOTSUP; } +#endif /* CONFIG_APPLEDOUBLE */