/*
- * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2012 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <security/mac_framework.h>
#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);
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 error;
- if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
+ if (!XATTR_VNODE_SUPPORTED(vp)) {
return (EPERM);
}
#if NAMEDSTREAMS
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);
}
{
int error;
- if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
+ if (!XATTR_VNODE_SUPPORTED(vp)) {
return (EPERM);
}
#if NAMEDSTREAMS
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);
}
{
int error;
- if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
+ if (!XATTR_VNODE_SUPPORTED(vp)) {
return (EPERM);
}
#if NAMEDSTREAMS
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
{
int error;
- if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
+ if (!XATTR_VNODE_SUPPORTED(vp)) {
return (EPERM);
}
#if NAMEDSTREAMS
/*
* 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);
}
#if NAMEDSTREAMS
+
/*
* Obtain a named stream from vnode vp.
*/
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);
}
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);
/*
* 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);
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);
/*
* 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)
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);
return (0);
}
datasize = va.va_data_size;
- if ((datasize == 0)) {
+ if (datasize == 0) {
(void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
return (0);
}
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;
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;
}
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) {
}
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);
}
+/*
+ * 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)
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));
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);
}
}
- /* 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);
/*
}
}
/* 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);
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.
*/
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) {
/*
* 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);
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;
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;
}
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);
}
}
*svpp = NULLVP;
return (ENOATTR);
}
+
+ /* Supply the context to getshadowfile so it can manipulate the AD file */
error = getshadowfile(vp, svpp, 1, NULL, &creator, 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);
}
}
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;
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;
}
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) ||
* 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)
*/
error = ENOTDIR;
goto out;
}
-#endif
-
+#endif /* NAMEDSTREAMS */
+#if CONFIG_APPLEDOUBLE
/*
* Default Implementation (Non-native EA)
*/
#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
*/
-#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 {
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)
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. */
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 */
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;
/*
* 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)
{
/*
* 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;
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);
(void) vnode_setattr(vp, &va, context);
}
}
+
+ 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;
(void) vnode_setattr(vp, &va, context);
}
}
+
+ post_event_if_success(vp, error, NOTE_ATTRIB);
+
return (error);
}
return (error);
}
if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
+ if (error == ENOATTR)
+ error = 0;
close_xattrfile(xvp, FREAD, context);
return (error);
}
* 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;
}
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) {
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);
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);
}
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);
}
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);
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. */
/* 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);
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 */
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);
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);
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);
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) {
break;
}
- if ((pos - chunk) < start) {
+ if ((pos - (off_t)chunk) < start) {
chunk = pos - start;
if (chunk == 0) { // we're all done
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) {
break;
}
- if ((pos + chunk) > end) {
+ if ((pos + (off_t)chunk) > end) {
chunk = end - pos;
if (chunk == 0) { // we're all done
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;
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 */