+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.
+ */
+errno_t
+vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, int flags, vfs_context_t context)
+{
+ int error;
+
+ if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) {
+ error = VNOP_GETNAMEDSTREAM(vp, svpp, name, op, flags, context);
+ } else {
+ if (flags) {
+ error = ENOTSUP;
+ } else {
+ error = default_getnamedstream(vp, svpp, name, op, context);
+ }
+ }
+
+ if (error == 0) {
+ vnode_setasnamedstream_internal(vp, *svpp);
+ }
+
+ return error;
+}
+
+/*
+ * Make a named stream for vnode vp.
+ */
+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) {
+ error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context);
+ } else {
+ error = default_makenamedstream(vp, svpp, name, context);
+ }
+
+ if (error == 0) {
+ vnode_setasnamedstream_internal(vp, *svpp);
+ }
+
+ return error;
+}
+
+/*
+ * Remove a named stream from vnode vp.
+ */
+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) {
+ error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context);
+ } else {
+ error = default_removenamedstream(vp, name, context);
+ }
+
+ return error;
+}
+
+#define NS_IOBUFSIZE (128 * 1024)
+
+/*
+ * 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)
+{
+ vnode_t dvp;
+ struct componentname cn;
+ 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);
+ MAKE_SHADOW_NAME(vp, tmpname);
+ vnode_unlock(svp);
+
+ cn.cn_nameiop = DELETE;
+ cn.cn_flags = ISLASTCN;
+ 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. 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, 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)
+{
+ struct vnode_attr va;
+ uio_t auio = NULL;
+ caddr_t bufptr = NULL;
+ size_t bufsize = 0;
+ size_t offset;
+ 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;
+ }
+ datasize = va.va_data_size;
+ if (datasize == 0) {
+ (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
+ return 0;
+ }
+
+ iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
+ if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize, VM_KERN_MEMORY_FILE)) {
+ return ENOMEM;
+ }
+ 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, 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_SYSSPACE, UIO_READ);
+ uio_addiov(auio, (uintptr_t)bufptr, iosize);
+ error = VNOP_READ(svp, auio, 0, kernelctx);
+ if (error) {
+ break;
+ }
+ /* Since there's no truncate xattr we must remove the resource fork. */
+ if (offset == 0) {
+ error = default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
+ if ((error != 0) && (error != ENOATTR)) {
+ break;
+ }
+ }
+ 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) {
+ break;
+ }
+ offset += iosize;
+ }
+
+ /* close shadowfile */
+ (void) VNOP_CLOSE(svp, 0, kernelctx);
+out:
+ if (bufptr) {
+ kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
+ }
+ if (auio) {
+ uio_free(auio);
+ }
+ 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 = 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;
+}