X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/4a3eedf9ecc9bbe3f3a5c6ce5e53ad199d639d32..39236c6e673c41db228275375ab7fdb0f837b292:/bsd/vfs/vfs_xattr.c diff --git a/bsd/vfs/vfs_xattr.c b/bsd/vfs/vfs_xattr.c index 30e62d8ce..dc2e09d32 100644 --- a/bsd/vfs/vfs_xattr.c +++ b/bsd/vfs/vfs_xattr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -56,19 +56,37 @@ #include #endif +#if !CONFIG_APPLEDOUBLE +#define PANIC_ON_NOAPPLEDOUBLE 1 +#endif #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,19 +96,22 @@ 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. @@ -101,7 +122,7 @@ vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, { int error; - if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) { + if (!XATTR_VNODE_SUPPORTED(vp)) { return (EPERM); } #if NAMEDSTREAMS @@ -147,7 +168,6 @@ 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); } @@ -163,7 +183,7 @@ 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)) { + if (!XATTR_VNODE_SUPPORTED(vp)) { return (EPERM); } #if NAMEDSTREAMS @@ -233,7 +253,6 @@ 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); } @@ -254,7 +273,7 @@ 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)) { + if (!XATTR_VNODE_SUPPORTED(vp)) { return (EPERM); } #if NAMEDSTREAMS @@ -281,7 +300,6 @@ vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t 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 @@ -314,7 +332,7 @@ 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)) { + if (!XATTR_VNODE_SUPPORTED(vp)) { return (EPERM); } #if NAMEDSTREAMS @@ -341,8 +359,7 @@ 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); } @@ -380,6 +397,7 @@ xattr_protected(const char *attrname) #if NAMEDSTREAMS + /* * Obtain a named stream from vnode vp. */ @@ -394,13 +412,31 @@ vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperati error = default_getnamedstream(vp, svpp, name, op, context); if (error == 0) { + uint32_t streamflags = VISNAMEDSTREAM; vnode_t svp = *svpp; + + if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) { + streamflags |= VISSHADOW; + } /* Tag the vnode. */ - vnode_lock(svp); - svp->v_flag |= VISNAMEDSTREAM; + vnode_lock_spin(svp); + svp->v_flag |= streamflags; vnode_unlock(svp); - /* Make the file it's parent. */ + + /* 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_PARENT); } @@ -421,13 +457,32 @@ vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vf error = default_makenamedstream(vp, svpp, name, context); if (error == 0) { + uint32_t streamflags = VISNAMEDSTREAM; vnode_t svp = *svpp; /* Tag the vnode. */ - vnode_lock(svp); - svp->v_flag |= 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); - /* Make the file it's parent. */ + + /* 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_PARENT); } return (error); @@ -453,18 +508,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 + * 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(); - if (vnode_isinuse(svp, 1)) { - return (EBUSY); - } cache_purge(svp); vnode_lock(svp); @@ -473,22 +542,22 @@ 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); - /* 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; } - /* Check for busy svp one last time. */ - if (vnode_isinuse(svp, 1) == 0) { - (void) VNOP_REMOVE(dvp, svp, &cn, 0, context); - (void) vnode_recycle(svp); - } + + (void) VNOP_REMOVE(dvp, svp, &cn, 0, kernelctx); vnode_put(dvp); return (0); @@ -496,6 +565,9 @@ vnode_relenamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) /* * Flush a named stream shadow file. + * + * 'vp' represents the AppleDouble file. + * 'svp' represents the shadow file. */ errno_t vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) @@ -508,6 +580,13 @@ 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); @@ -516,7 +595,7 @@ vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) return (0); } datasize = va.va_data_size; - if ((datasize == 0)) { + if (datasize == 0) { (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context); return (0); } @@ -525,13 +604,13 @@ vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context) if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) { 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; @@ -539,9 +618,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; } @@ -552,7 +631,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) { @@ -560,7 +639,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); @@ -572,6 +653,75 @@ out: } +/* + * 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 = 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) @@ -580,12 +730,14 @@ getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, 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)); @@ -618,13 +770,13 @@ 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); @@ -634,7 +786,10 @@ getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, } } - /* 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); /* @@ -661,12 +816,36 @@ 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); + } + else if ((error == EEXIST) && !makestream) { + 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,6 +876,9 @@ 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. */ @@ -707,6 +889,9 @@ default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsopera 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) { @@ -716,7 +901,14 @@ retry: /* * 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); @@ -725,9 +917,19 @@ 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; @@ -747,10 +949,11 @@ retry: 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; } @@ -759,37 +962,50 @@ retry: 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); 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 shadow file. */ - (void)vnode_relenamedstream(vp, svp, context); - + /* 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); + vnode_lock (svp); + svp->v_flag |= VISSHADOW; wakeup((caddr_t)&svp->v_parent); + vnode_unlock(svp); } } @@ -823,6 +1039,8 @@ default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context *svpp = NULLVP; return (ENOATTR); } + + /* Supply the context to getshadowfile so it can manipulate the AD file */ error = getshadowfile(vp, svpp, 1, NULL, &creator, context); /* @@ -832,11 +1050,14 @@ 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); } @@ -856,39 +1077,60 @@ 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; - - /* 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); - } + vfs_context_t kernelctx = vfs_context_kernel(); + + 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; @@ -905,30 +1147,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; } @@ -945,7 +1180,7 @@ 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 || + 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) || @@ -956,7 +1191,7 @@ get_shadow_dir(vnode_t *sdvpp, vfs_context_t context) * 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) */ @@ -989,10 +1224,10 @@ baddir: error = ENOTDIR; goto out; } -#endif - +#endif /* NAMEDSTREAMS */ +#if CONFIG_APPLEDOUBLE /* * Default Implementation (Non-native EA) */ @@ -1086,7 +1321,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 @@ -1106,15 +1341,13 @@ baddir: */ -#pragma options align=mac68k - #define FINDERINFOSIZE 32 typedef struct apple_double_entry { 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; +} __attribute__((aligned(2), packed)) apple_double_entry_t; typedef struct apple_double_header { @@ -1125,7 +1358,7 @@ typedef struct apple_double_header { 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) @@ -1136,7 +1369,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. */ @@ -1150,7 +1383,7 @@ typedef struct attr_header { 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 */ @@ -1172,14 +1405,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; @@ -1353,7 +1584,7 @@ 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) { @@ -1475,7 +1706,7 @@ out: /* * 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; @@ -1602,19 +1833,34 @@ start: 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 { - /* attr doesn't exists and "replace" was specified? */ - if (options & XATTR_REPLACE) { - error = ENOATTR; - 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 { + /* 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 */ uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset); error = VNOP_WRITE(xvp, uio, 0, context); @@ -1834,6 +2080,9 @@ out: (void) vnode_setattr(vp, &va, context); } } + + post_event_if_success(vp, error, NOTE_ATTRIB); + return (error); } @@ -1841,7 +2090,7 @@ out: /* * 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; @@ -2053,6 +2302,9 @@ out: (void) vnode_setattr(vp, &va, context); } } + + post_event_if_success(vp, error, NOTE_ATTRIB); + return (error); } @@ -2082,6 +2334,8 @@ default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs return (error); } if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) { + if (error == ENOATTR) + error = 0; close_xattrfile(xvp, FREAD, context); return (error); } @@ -2209,12 +2463,15 @@ 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); + NDINIT(&nd, LOOKUP, OP_OPEN, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH, + UIO_SYSSPACE, CAST_USER_ADDR_T(filename), context); nd.ni_dvp = dvp; if (fileflags & O_CREAT) { nd.ni_cnd.cn_nameiop = CREATE; +#if CONFIG_TRIGGERS + nd.ni_op = OP_LINK; +#endif if (dvp != vp) { nd.ni_cnd.cn_flags |= LOCKPARENT; } @@ -2256,11 +2513,14 @@ lookup: if (gid != KAUTH_GID_NONE) VATTR_SET(&va, va_gid, gid); - error = vn_create(dvp, &nd.ni_vp, &nd.ni_cnd, &va, + error = vn_create(dvp, &nd.ni_vp, &nd, &va, VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL, + 0, NULL, context); - if (error == 0) - xvp = nd.ni_vp; + if (error) + error = ENOATTR; + else + xvp = nd.ni_vp; } nameidone(&nd); if (dvp != vp) { @@ -2269,10 +2529,10 @@ lookup: 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); @@ -2338,22 +2598,21 @@ lookup: 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); } @@ -2364,6 +2623,17 @@ out: error = EPERM; } } + /* Release resources after error-handling */ + if (dvp && (dvp != vp)) { + vnode_put(dvp); + } + if (basename) { + vnode_putname(basename); + } + if (filename && filename != &smallname[0]) { + FREE(filename, M_TEMP); + } + *xvpp = xvp; /* return a referenced vnode */ return (error); } @@ -2402,7 +2672,7 @@ remove_xattrfile(vnode_t xvp, vfs_context_t context) return (error); } - NDINIT(&nd, DELETE, LOCKPARENT | NOFOLLOW | DONOTAUTH, + 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); @@ -2478,7 +2748,7 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte 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. */ @@ -2545,7 +2815,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); @@ -2633,7 +2903,7 @@ get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t conte 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 */ @@ -2693,7 +2963,7 @@ create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context) 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); @@ -2720,7 +2990,12 @@ create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context) 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); @@ -2757,7 +3032,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); @@ -2930,7 +3205,7 @@ 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) { @@ -2962,7 +3237,7 @@ shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t break; } - if ((pos - chunk) < start) { + if ((pos - (off_t)chunk) < start) { chunk = pos - start; if (chunk == 0) { // we're all done @@ -2984,7 +3259,7 @@ 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) { @@ -3017,7 +3292,7 @@ shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t c break; } - if ((pos + chunk) > end) { + if ((pos + (off_t)chunk) > end) { chunk = end - pos; if (chunk == 0) { // we're all done @@ -3041,11 +3316,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); + 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; @@ -3056,7 +3331,55 @@ 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); + error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_UNLCK, &lf, F_FLOCK, context, NULL); return (error == ENOTSUP ? 0 : error); } +#else /* CONFIG_APPLEDOUBLE */ + +#undef panic +#define panic printf + +static int +default_getxattr(vnode_t vp, const char *name, + __unused uio_t uio, __unused size_t *size, __unused int options, + __unused vfs_context_t context) +{ +#if PANIC_ON_NOAPPLEDOUBLE + panic("%s: no AppleDouble support, vp %p name %s", __func__, vp, name); +#endif + return (ENOTSUP); +} + +static int +default_setxattr(vnode_t vp, const char *name, + __unused uio_t uio, __unused int options, __unused vfs_context_t context) +{ +#if PANIC_ON_NOAPPLEDOUBLE + panic("%s: no AppleDouble support, vp %p name %s", __func__, vp, name); +#endif + return (ENOTSUP); +} + +static int +default_listxattr(vnode_t vp, + __unused uio_t uio, __unused size_t *size, __unused int options, + __unused vfs_context_t context) +{ +#if PANIC_ON_NOAPPLEDOUBLE + panic("%s: no AppleDouble support, vp %p name %s", __func__, vp, "."); +#endif + return (ENOTSUP); +} + +static int +default_removexattr(vnode_t vp, const char *name, + __unused int options, __unused vfs_context_t context) +{ +#if PANIC_ON_NOAPPLEDOUBLE + panic("%s: no AppleDouble support, vp %p name %s", __func__, vp, name); +#endif + return (ENOTSUP); +} + +#endif /* CONFIG_APPLEDOUBLE */