summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/linux/sharedfolders/dirops.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/linux/sharedfolders/dirops.c')
-rw-r--r--src/VBox/Additions/linux/sharedfolders/dirops.c1427
1 files changed, 1427 insertions, 0 deletions
diff --git a/src/VBox/Additions/linux/sharedfolders/dirops.c b/src/VBox/Additions/linux/sharedfolders/dirops.c
new file mode 100644
index 00000000..fb8c8d59
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/dirops.c
@@ -0,0 +1,1427 @@
+/* $Id: dirops.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, directory inode and file operations.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "vfsmod.h"
+#include <iprt/err.h>
+
+#if RTLNX_VER_MAX(4,7,0)
+# define d_in_lookup(a_pDirEntry) (d_unhashed(a_pDirEntry))
+#endif
+
+
+
+/**
+ * Open a directory (implements file_operations::open).
+ *
+ * @returns 0 on success, negative errno otherwise.
+ * @param inode inode
+ * @param file file
+ */
+static int vbsf_dir_open(struct inode *inode, struct file *file)
+{
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(inode->i_sb);
+ struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(inode);
+ struct dentry *dentry = VBSF_GET_F_DENTRY(file);
+ struct vbsf_dir_info *sf_d;
+ int rc;
+
+ SFLOGFLOW(("vbsf_dir_open: inode=%p file=%p %s\n", inode, file, sf_i && sf_i->path ? sf_i->path->String.ach : NULL));
+ AssertReturn(pSuperInfo, -EINVAL);
+ AssertReturn(sf_i, -EINVAL);
+ AssertReturn(!file->private_data, 0);
+
+ /*
+ * Allocate and initialize our directory info structure.
+ * We delay buffer allocation until vbsf_getdent is actually used.
+ */
+ sf_d = kmalloc(sizeof(*sf_d), GFP_KERNEL);
+ if (sf_d) {
+ VBOXSFCREATEREQ *pReq;
+ RT_ZERO(*sf_d);
+ sf_d->u32Magic = VBSF_DIR_INFO_MAGIC;
+ sema_init(&sf_d->Lock, 1);
+
+ /*
+ * Try open the directory.
+ */
+ pReq = (VBOXSFCREATEREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF(VBOXSFCREATEREQ, StrPath.String) + sf_i->path->u16Size);
+ if (pReq) {
+ RT_BCOPY_UNFORTIFIED(&pReq->StrPath, sf_i->path, SHFLSTRING_HEADER_SIZE + sf_i->path->u16Size);
+ RT_ZERO(pReq->CreateParms);
+ pReq->CreateParms.Handle = SHFL_HANDLE_NIL;
+ pReq->CreateParms.CreateFlags = SHFL_CF_DIRECTORY
+ | SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW
+ | SHFL_CF_ACCESS_READ;
+
+ LogFunc(("calling VbglR0SfHostReqCreate on folder %s, flags %#x\n",
+ sf_i->path->String.utf8, pReq->CreateParms.CreateFlags));
+ rc = VbglR0SfHostReqCreate(pSuperInfo->map.root, pReq);
+ if (RT_SUCCESS(rc)) {
+ if (pReq->CreateParms.Result == SHFL_FILE_EXISTS) {
+ Assert(pReq->CreateParms.Handle != SHFL_HANDLE_NIL);
+
+ /*
+ * Update the inode info with fresh stats and increase the TTL for the
+ * dentry cache chain that got us here.
+ */
+ vbsf_update_inode(inode, sf_i, &pReq->CreateParms.Info, pSuperInfo,
+ true /*fLocked*/ /** @todo inode locking */, 0 /*fSetAttrs*/);
+ vbsf_dentry_chain_increase_ttl(dentry);
+
+ sf_d->Handle.hHost = pReq->CreateParms.Handle;
+ sf_d->Handle.cRefs = 1;
+ sf_d->Handle.fFlags = VBSF_HANDLE_F_READ | VBSF_HANDLE_F_DIR | VBSF_HANDLE_F_MAGIC;
+ vbsf_handle_append(sf_i, &sf_d->Handle);
+
+ file->private_data = sf_d;
+ VbglR0PhysHeapFree(pReq);
+ SFLOGFLOW(("vbsf_dir_open(%p,%p): returns 0; hHost=%#llx\n", inode, file, sf_d->Handle.hHost));
+ return 0;
+
+ }
+ Assert(pReq->CreateParms.Handle == SHFL_HANDLE_NIL);
+
+ /*
+ * Directory does not exist, so we probably got some invalid
+ * dir cache and inode info.
+ */
+ /** @todo do more to invalidate dentry and inode here. */
+ vbsf_dentry_invalidate_ttl(dentry);
+ sf_i->force_restat = true;
+ rc = -ENOENT;
+ } else
+ rc = -EPERM;
+ VbglR0PhysHeapFree(pReq);
+ } else {
+ LogRelMaxFunc(64, ("failed to allocate %zu bytes for '%s'\n",
+ RT_UOFFSETOF(VBOXSFCREATEREQ, StrPath.String) + sf_i->path->u16Size, sf_i->path->String.ach));
+ rc = -ENOMEM;
+ }
+ sf_d->u32Magic = VBSF_DIR_INFO_MAGIC_DEAD;
+ kfree(sf_d);
+ } else
+ rc = -ENOMEM;
+ SFLOGFLOW(("vbsf_dir_open(%p,%p): returns %d\n", inode, file, rc));
+ return rc;
+}
+
+
+/**
+ * This is called when reference count of [file] goes to zero. Notify
+ * the host that it can free whatever is associated with this directory
+ * and deallocate our own internal buffers
+ *
+ * @param inode inode
+ * @param file file
+ * returns 0 on success, Linux error code otherwise
+ */
+static int vbsf_dir_release(struct inode *inode, struct file *file)
+{
+ struct vbsf_dir_info *sf_d = (struct vbsf_dir_info *)file->private_data;
+
+ SFLOGFLOW(("vbsf_dir_release(%p,%p): sf_d=%p hHost=%#llx\n", inode, file, sf_d, sf_d ? sf_d->Handle.hHost : SHFL_HANDLE_NIL));
+
+ if (sf_d) {
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(inode->i_sb);
+
+ /* Invalidate the non-handle part. */
+ sf_d->u32Magic = VBSF_DIR_INFO_MAGIC_DEAD;
+ sf_d->cEntriesLeft = 0;
+ sf_d->cbValid = 0;
+ sf_d->pEntry = NULL;
+ sf_d->fNoMoreFiles = false;
+ if (sf_d->pBuf) {
+ kfree(sf_d->pBuf);
+ sf_d->pBuf = NULL;
+ }
+
+ /* Closes the handle and frees the structure when the last reference is released. */
+ vbsf_handle_release(&sf_d->Handle, pSuperInfo, "vbsf_dir_release");
+ }
+
+ return 0;
+}
+
+
+/**
+ * Translate RTFMODE into DT_xxx (in conjunction to rtDirType()).
+ * returns d_type
+ * @param fMode file mode
+ */
+DECLINLINE(int) vbsf_get_d_type(RTFMODE fMode)
+{
+ switch (fMode & RTFS_TYPE_MASK) {
+ case RTFS_TYPE_FIFO: return DT_FIFO;
+ case RTFS_TYPE_DEV_CHAR: return DT_CHR;
+ case RTFS_TYPE_DIRECTORY: return DT_DIR;
+ case RTFS_TYPE_DEV_BLOCK: return DT_BLK;
+ case RTFS_TYPE_FILE: return DT_REG;
+ case RTFS_TYPE_SYMLINK: return DT_LNK;
+ case RTFS_TYPE_SOCKET: return DT_SOCK;
+ case RTFS_TYPE_WHITEOUT: return DT_WHT;
+ }
+ return DT_UNKNOWN;
+}
+
+
+/**
+ * Refills the buffer with more entries.
+ *
+ * @returns 0 on success, negative errno on error,
+ */
+static int vbsf_dir_read_more(struct vbsf_dir_info *sf_d, struct vbsf_super_info *pSuperInfo, bool fRestart)
+{
+ int rc;
+ VBOXSFLISTDIRREQ *pReq;
+
+ /*
+ * Don't call the host again if we've reached the end of the
+ * directory entries already.
+ */
+ if (sf_d->fNoMoreFiles) {
+ if (!fRestart) {
+ SFLOGFLOW(("vbsf_dir_read_more: no more files\n"));
+ return 0;
+ }
+ sf_d->fNoMoreFiles = false;
+ }
+
+ /*
+ * Make sure we've got some kind of buffers.
+ */
+ if (sf_d->pBuf) {
+ /* Likely, except for the first time. */
+ } else {
+ sf_d->pBuf = (PSHFLDIRINFO)kmalloc(pSuperInfo->cbDirBuf, GFP_KERNEL);
+ if (sf_d->pBuf)
+ sf_d->cbBuf = pSuperInfo->cbDirBuf;
+ else {
+ sf_d->pBuf = (PSHFLDIRINFO)kmalloc(_4K, GFP_KERNEL);
+ if (!sf_d->pBuf) {
+ LogRelMax(10, ("vbsf_dir_read_more: Failed to allocate buffer!\n"));
+ return -ENOMEM;
+ }
+ sf_d->cbBuf = _4K;
+ }
+ }
+
+ /*
+ * Allocate a request buffer.
+ */
+ pReq = (VBOXSFLISTDIRREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
+ if (pReq) {
+ rc = VbglR0SfHostReqListDirContig2x(pSuperInfo->map.root, pReq, sf_d->Handle.hHost, NULL, NIL_RTGCPHYS64,
+ fRestart ? SHFL_LIST_RESTART : SHFL_LIST_NONE,
+ sf_d->pBuf, virt_to_phys(sf_d->pBuf), sf_d->cbBuf);
+ if (RT_SUCCESS(rc)) {
+ sf_d->pEntry = sf_d->pBuf;
+ sf_d->cbValid = pReq->Parms.cb32Buffer.u.value32;
+ sf_d->cEntriesLeft = pReq->Parms.c32Entries.u.value32;
+ sf_d->fNoMoreFiles = pReq->Parms.f32More.u.value32 == 0;
+ } else {
+ sf_d->pEntry = sf_d->pBuf;
+ sf_d->cbValid = 0;
+ sf_d->cEntriesLeft = 0;
+ if (rc == VERR_NO_MORE_FILES) {
+ sf_d->fNoMoreFiles = true;
+ rc = 0;
+ } else {
+ /* In theory we could end up here with a buffer overflow, but
+ with a 4KB minimum buffer size that's very unlikely with the
+ typical filename length of today's file systems (2019). */
+ LogRelMax(16, ("vbsf_dir_read_more: VbglR0SfHostReqListDirContig2x -> %Rrc\n", rc));
+ rc = -EPROTO;
+ }
+ }
+ VbglR0PhysHeapFree(pReq);
+ } else
+ rc = -ENOMEM;
+ SFLOGFLOW(("vbsf_dir_read_more: returns %d; cbValid=%#x cEntriesLeft=%#x fNoMoreFiles=%d\n",
+ rc, sf_d->cbValid, sf_d->cEntriesLeft, sf_d->fNoMoreFiles));
+ return rc;
+}
+
+
+/**
+ * Helper function for when we need to convert the name, avoids wasting stack in
+ * the UTF-8 code path.
+ */
+DECL_NO_INLINE(static, bool) vbsf_dir_emit_nls(
+# if RTLNX_VER_MIN(3,11,0)
+ struct dir_context *ctx,
+# else
+ void *opaque, filldir_t filldir, loff_t offPos,
+# endif
+ const char *pszSrcName, uint16_t cchSrcName, ino_t d_ino, int d_type,
+ struct vbsf_super_info *pSuperInfo)
+{
+ char szDstName[NAME_MAX];
+ int rc = vbsf_nlscpy(pSuperInfo, szDstName, sizeof(szDstName), pszSrcName, cchSrcName);
+ if (rc == 0) {
+#if RTLNX_VER_MIN(3,11,0)
+ return dir_emit(ctx, szDstName, strlen(szDstName), d_ino, d_type);
+#else
+ return filldir(opaque, szDstName, strlen(szDstName), offPos, d_ino, d_type) == 0;
+#endif
+ }
+
+ /* Assuming this is a buffer overflow issue, just silently skip it. */
+ SFLOGFLOW(("vbsf_dir_emit_nls: vbsf_nlscopy failed with %d for '%s'\n", rc, pszSrcName));
+ return true;
+}
+
+
+/**
+ * This is called when vfs wants to populate internal buffers with
+ * directory [dir]s contents. [opaque] is an argument to the
+ * [filldir]. [filldir] magically modifies it's argument - [opaque]
+ * and takes following additional arguments (which i in turn get from
+ * the host via vbsf_getdent):
+ *
+ * name : name of the entry (i must also supply it's length huh?)
+ * type : type of the entry (FILE | DIR | etc) (i ellect to use DT_UNKNOWN)
+ * pos : position/index of the entry
+ * ino : inode number of the entry (i fake those)
+ *
+ * [dir] contains:
+ * f_pos : cursor into the directory listing
+ * private_data : mean of communication with the host side
+ *
+ * Extract elements from the directory listing (incrementing f_pos
+ * along the way) and feed them to [filldir] until:
+ *
+ * a. there are no more entries (i.e. vbsf_getdent set done to 1)
+ * b. failure to compute fake inode number
+ * c. filldir returns an error (see comment on that)
+ */
+#if RTLNX_VER_MIN(3,11,0)
+static int vbsf_dir_iterate(struct file *dir, struct dir_context *ctx)
+#else
+static int vbsf_dir_read(struct file *dir, void *opaque, filldir_t filldir)
+#endif
+{
+#if RTLNX_VER_MIN(3,11,0)
+ loff_t offPos = ctx->pos;
+#else
+ loff_t offPos = dir->f_pos;
+#endif
+ struct vbsf_dir_info *sf_d = (struct vbsf_dir_info *)dir->private_data;
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(VBSF_GET_F_DENTRY(dir)->d_sb);
+ int rc;
+
+ /*
+ * Lock the directory info structures.
+ */
+ if (RT_LIKELY(down_interruptible(&sf_d->Lock) == 0)) {
+ /* likely */
+ } else
+ return -ERESTARTSYS;
+
+ /*
+ * Any seek performed in the mean time?
+ */
+ if (offPos == sf_d->offPos) {
+ /* likely */
+ } else {
+ /* Restart the search if iPos is lower than the current buffer position. */
+ loff_t offCurEntry = sf_d->offPos;
+ if (offPos < offCurEntry) {
+ rc = vbsf_dir_read_more(sf_d, pSuperInfo, true /*fRestart*/);
+ if (rc == 0)
+ offCurEntry = 0;
+ else {
+ up(&sf_d->Lock);
+ return rc;
+ }
+ }
+
+ /* Skip ahead to offPos. */
+ while (offCurEntry < offPos) {
+ uint32_t cEntriesLeft = sf_d->cEntriesLeft;
+ if ((uint64_t)(offPos - offCurEntry) >= cEntriesLeft) {
+ /* Skip the current buffer and read the next: */
+ offCurEntry += cEntriesLeft;
+ sf_d->offPos = offCurEntry;
+ sf_d->cEntriesLeft = 0;
+ rc = vbsf_dir_read_more(sf_d, pSuperInfo, false /*fRestart*/);
+ if (rc != 0 || sf_d->cEntriesLeft == 0) {
+ up(&sf_d->Lock);
+ return rc;
+ }
+ } else {
+ do
+ {
+ PSHFLDIRINFO pEntry = sf_d->pEntry;
+ pEntry = (PSHFLDIRINFO)&pEntry->name.String.utf8[pEntry->name.u16Length];
+ AssertLogRelBreakStmt( cEntriesLeft == 1
+ || (uintptr_t)pEntry - (uintptr_t)sf_d->pBuf
+ <= sf_d->cbValid - RT_UOFFSETOF(SHFLDIRINFO, name.String),
+ sf_d->cEntriesLeft = 0);
+ sf_d->cEntriesLeft = --cEntriesLeft;
+ sf_d->offPos = ++offCurEntry;
+ } while (offPos < sf_d->offPos);
+ }
+ }
+ }
+
+ /*
+ * Handle '.' and '..' specially so we get the inode numbers right.
+ * We'll skip any '.' or '..' returned by the host (included in pos,
+ * however, to simplify the above skipping code).
+ */
+ if (offPos < 2) {
+#if RTLNX_VER_MIN(3,11,0)
+ if (offPos == 0) {
+ if (dir_emit_dot(dir, ctx))
+ dir->f_pos = ctx->pos = sf_d->offPos = offPos = 1;
+ else {
+ up(&sf_d->Lock);
+ return 0;
+ }
+ }
+ if (offPos == 1) {
+ if (dir_emit_dotdot(dir, ctx))
+ dir->f_pos = ctx->pos = sf_d->offPos = offPos = 2;
+ else {
+ up(&sf_d->Lock);
+ return 0;
+ }
+ }
+#else
+ if (offPos == 0) {
+ rc = filldir(opaque, ".", 1, 0, VBSF_GET_F_DENTRY(dir)->d_inode->i_ino, DT_DIR);
+ if (!rc)
+ dir->f_pos = sf_d->offPos = offPos = 1;
+ else {
+ up(&sf_d->Lock);
+ return 0;
+ }
+ }
+ if (offPos == 1) {
+# if RTLNX_VER_MIN(2,5,5)
+ rc = filldir(opaque, "..", 2, 1, parent_ino(VBSF_GET_F_DENTRY(dir)), DT_DIR);
+# else
+ rc = filldir(opaque, "..", 2, 1, VBSF_GET_F_DENTRY(dir)->d_parent->d_inode->i_ino, DT_DIR);
+# endif
+ if (!rc)
+ dir->f_pos = sf_d->offPos = offPos = 2;
+ else {
+ up(&sf_d->Lock);
+ return 0;
+ }
+ }
+#endif
+ }
+
+ /*
+ * Produce stuff.
+ */
+ Assert(offPos == sf_d->offPos);
+ for (;;) {
+ PSHFLDIRINFO pBuf;
+ PSHFLDIRINFO pEntry;
+
+ /*
+ * Do we need to read more?
+ */
+ uint32_t cbValid = sf_d->cbValid;
+ uint32_t cEntriesLeft = sf_d->cEntriesLeft;
+ if (!cEntriesLeft) {
+ rc = vbsf_dir_read_more(sf_d, pSuperInfo, false /*fRestart*/);
+ if (rc == 0) {
+ cEntriesLeft = sf_d->cEntriesLeft;
+ if (!cEntriesLeft) {
+ up(&sf_d->Lock);
+ return 0;
+ }
+ cbValid = sf_d->cbValid;
+ } else {
+ up(&sf_d->Lock);
+ return rc;
+ }
+ }
+
+ /*
+ * Feed entries to the caller.
+ */
+ pBuf = sf_d->pBuf;
+ pEntry = sf_d->pEntry;
+ do {
+ /*
+ * Validate the entry in case the host is messing with us.
+ * We're ASSUMING the host gives us a zero terminated string (UTF-8) here.
+ */
+ uintptr_t const offEntryInBuf = (uintptr_t)pEntry - (uintptr_t)pBuf;
+ uint16_t cbSrcName;
+ uint16_t cchSrcName;
+ AssertLogRelMsgBreak(offEntryInBuf + RT_UOFFSETOF(SHFLDIRINFO, name.String) <= cbValid,
+ ("%#llx + %#x vs %#x\n", offEntryInBuf, RT_UOFFSETOF(SHFLDIRINFO, name.String), cbValid));
+ cbSrcName = pEntry->name.u16Size;
+ cchSrcName = pEntry->name.u16Length;
+ AssertLogRelBreak(offEntryInBuf + RT_UOFFSETOF(SHFLDIRINFO, name.String) + cbSrcName <= cbValid);
+ AssertLogRelBreak(cchSrcName < cbSrcName);
+ AssertLogRelBreak(pEntry->name.String.ach[cchSrcName] == '\0');
+
+ /*
+ * Filter out '.' and '..' entires.
+ */
+ if ( cchSrcName > 2
+ || pEntry->name.String.ach[0] != '.'
+ || ( cchSrcName == 2
+ && pEntry->name.String.ach[1] != '.')) {
+ int const d_type = vbsf_get_d_type(pEntry->Info.Attr.fMode);
+ ino_t const d_ino = (ino_t)offPos + 0xbeef; /* very fake */
+ bool fContinue;
+ if (pSuperInfo->fNlsIsUtf8) {
+#if RTLNX_VER_MIN(3,11,0)
+ fContinue = dir_emit(ctx, pEntry->name.String.ach, cchSrcName, d_ino, d_type);
+#else
+ fContinue = filldir(opaque, pEntry->name.String.ach, cchSrcName, offPos, d_ino, d_type) == 0;
+#endif
+ } else {
+#if RTLNX_VER_MIN(3,11,0)
+ fContinue = vbsf_dir_emit_nls(ctx, pEntry->name.String.ach, cchSrcName, d_ino, d_type, pSuperInfo);
+#else
+ fContinue = vbsf_dir_emit_nls(opaque, filldir, offPos, pEntry->name.String.ach, cchSrcName,
+ d_ino, d_type, pSuperInfo);
+#endif
+ }
+ if (fContinue) {
+ /* likely */
+ } else {
+ sf_d->cEntriesLeft = cEntriesLeft;
+ sf_d->pEntry = pEntry;
+ sf_d->offPos = offPos;
+ up(&sf_d->Lock);
+ return 0;
+ }
+ }
+
+ /*
+ * Advance to the next entry.
+ */
+ pEntry = (PSHFLDIRINFO)((uintptr_t)pEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) + cbSrcName);
+ offPos += 1;
+ dir->f_pos = offPos;
+#if RTLNX_VER_MIN(3,11,0)
+ ctx->pos = offPos;
+#endif
+ cEntriesLeft -= 1;
+ } while (cEntriesLeft > 0);
+
+ /* Done with all available entries. */
+ sf_d->offPos = offPos + cEntriesLeft;
+ sf_d->pEntry = pBuf;
+ sf_d->cEntriesLeft = 0;
+ }
+}
+
+
+/**
+ * Directory file operations.
+ */
+struct file_operations vbsf_dir_fops = {
+ .open = vbsf_dir_open,
+#if RTLNX_VER_MIN(4,7,0)
+ .iterate_shared = vbsf_dir_iterate,
+#elif RTLNX_VER_MIN(3,11,0)
+ .iterate = vbsf_dir_iterate,
+#else
+ .readdir = vbsf_dir_read,
+#endif
+ .release = vbsf_dir_release,
+ .read = generic_read_dir,
+#if RTLNX_VER_MIN(2,6,37)
+ .llseek = generic_file_llseek
+#endif
+};
+
+
+
+/*********************************************************************************************************************************
+* Directory Inode Operations *
+*********************************************************************************************************************************/
+
+/**
+ * Worker for vbsf_inode_lookup(), vbsf_create_worker() and
+ * vbsf_inode_instantiate().
+ */
+static struct inode *vbsf_create_inode(struct inode *parent, struct dentry *dentry, PSHFLSTRING path,
+ PSHFLFSOBJINFO pObjInfo, struct vbsf_super_info *pSuperInfo, bool fInstantiate)
+{
+ /*
+ * Allocate memory for our additional inode info and create an inode.
+ */
+ struct vbsf_inode_info *sf_new_i = (struct vbsf_inode_info *)kmalloc(sizeof(*sf_new_i), GFP_KERNEL);
+ if (sf_new_i) {
+ ino_t iNodeNo = iunique(parent->i_sb, 16);
+#if RTLNX_VER_MIN(2,4,25)
+ struct inode *pInode = iget_locked(parent->i_sb, iNodeNo);
+#else
+ struct inode *pInode = iget(parent->i_sb, iNodeNo);
+#endif
+ if (pInode) {
+ /*
+ * Initialize the two structures.
+ */
+#ifdef VBOX_STRICT
+ sf_new_i->u32Magic = SF_INODE_INFO_MAGIC;
+#endif
+ sf_new_i->path = path;
+ sf_new_i->force_restat = false;
+ sf_new_i->ts_up_to_date = jiffies;
+ RTListInit(&sf_new_i->HandleList);
+ sf_new_i->handle = SHFL_HANDLE_NIL;
+
+ VBSF_SET_INODE_INFO(pInode, sf_new_i);
+ vbsf_init_inode(pInode, sf_new_i, pObjInfo, pSuperInfo);
+
+ /*
+ * Before we unlock the new inode, we may need to call d_instantiate.
+ */
+ if (fInstantiate)
+ d_instantiate(dentry, pInode);
+#if RTLNX_VER_MIN(2,4,25)
+ unlock_new_inode(pInode);
+#endif
+ return pInode;
+
+ }
+ LogFunc(("iget failed\n"));
+ kfree(sf_new_i);
+ } else
+ LogRelFunc(("could not allocate memory for new inode info\n"));
+ return NULL;
+}
+
+
+/** Helper for vbsf_create_worker() and vbsf_inode_lookup() that wraps
+ * d_add() and setting d_op. */
+DECLINLINE(void) vbsf_d_add_inode(struct dentry *dentry, struct inode *pNewInode)
+{
+#if RTLNX_VER_MIN(2,6,38)
+ Assert(dentry->d_op == &vbsf_dentry_ops); /* (taken from the superblock) */
+#else
+ dentry->d_op = &vbsf_dentry_ops;
+#endif
+ d_add(dentry, pNewInode);
+}
+
+
+/**
+ * This is called when vfs failed to locate dentry in the cache. The
+ * job of this function is to allocate inode and link it to dentry.
+ * [dentry] contains the name to be looked in the [parent] directory.
+ * Failure to locate the name is not a "hard" error, in this case NULL
+ * inode is added to [dentry] and vfs should proceed trying to create
+ * the entry via other means. NULL(or "positive" pointer) ought to be
+ * returned in case of success and "negative" pointer on error
+ */
+static struct dentry *vbsf_inode_lookup(struct inode *parent, struct dentry *dentry
+#if RTLNX_VER_MIN(3,6,0)
+ , unsigned int flags
+#elif RTLNX_VER_MIN(2,6,0)
+ , struct nameidata *nd
+#endif
+ )
+{
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(parent->i_sb);
+ struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(parent);
+ SHFLSTRING *path;
+ struct dentry *dret;
+ int rc;
+
+#if RTLNX_VER_MIN(3,6,0)
+ SFLOGFLOW(("vbsf_inode_lookup: parent=%p dentry=%p flags=%#x\n", parent, dentry, flags));
+#elif RTLNX_VER_MIN(2,6,0)
+ SFLOGFLOW(("vbsf_inode_lookup: parent=%p dentry=%p nd=%p{.flags=%#x}\n", parent, dentry, nd, nd ? nd->flags : 0));
+#else
+ SFLOGFLOW(("vbsf_inode_lookup: parent=%p dentry=%p\n", parent, dentry));
+#endif
+
+ Assert(pSuperInfo);
+ Assert(sf_i && sf_i->u32Magic == SF_INODE_INFO_MAGIC);
+
+ /*
+ * Build the path. We'll associate the path with dret's inode on success.
+ */
+ rc = vbsf_path_from_dentry(pSuperInfo, sf_i, dentry, &path, __func__);
+ if (rc == 0) {
+ /*
+ * Do a lookup on the host side.
+ */
+ VBOXSFCREATEREQ *pReq = (VBOXSFCREATEREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq) + path->u16Size);
+ if (pReq) {
+ struct inode *pInode = NULL;
+
+ RT_ZERO(*pReq);
+ RT_BCOPY_UNFORTIFIED(&pReq->StrPath, path, SHFLSTRING_HEADER_SIZE + path->u16Size);
+ pReq->CreateParms.Handle = SHFL_HANDLE_NIL;
+ pReq->CreateParms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
+
+ SFLOG2(("vbsf_inode_lookup: Calling VbglR0SfHostReqCreate on %s\n", path->String.utf8));
+ rc = VbglR0SfHostReqCreate(pSuperInfo->map.root, pReq);
+ if (RT_SUCCESS(rc)) {
+ if (pReq->CreateParms.Result == SHFL_FILE_EXISTS) {
+ /*
+ * Create an inode for the result. Since this also confirms
+ * the existence of all parent dentries, we increase their TTL.
+ */
+ pInode = vbsf_create_inode(parent, dentry, path, &pReq->CreateParms.Info, pSuperInfo, false /*fInstantiate*/);
+ if (rc == 0) {
+ path = NULL; /* given to the inode */
+ dret = dentry;
+ } else
+ dret = (struct dentry *)ERR_PTR(-ENOMEM);
+ vbsf_dentry_chain_increase_parent_ttl(dentry);
+ } else if ( pReq->CreateParms.Result == SHFL_FILE_NOT_FOUND
+ || pReq->CreateParms.Result == SHFL_PATH_NOT_FOUND /*this probably should happen*/) {
+ dret = dentry;
+ } else {
+ AssertMsgFailed(("%d\n", pReq->CreateParms.Result));
+ dret = (struct dentry *)ERR_PTR(-EPROTO);
+ }
+ } else if (rc == VERR_INVALID_NAME) {
+ SFLOGFLOW(("vbsf_inode_lookup: VERR_INVALID_NAME\n"));
+ dret = dentry; /* this can happen for names like 'foo*' on a Windows host */
+ } else if (rc == VERR_FILENAME_TOO_LONG) {
+ SFLOG(("vbsf_inode_lookup: VbglR0SfHostReqCreate failed on %s: VERR_FILENAME_TOO_LONG\n", path->String.utf8));
+ dret = (struct dentry *)ERR_PTR(-ENAMETOOLONG);
+ } else {
+ SFLOG(("vbsf_inode_lookup: VbglR0SfHostReqCreate failed on %s: %Rrc\n", path->String.utf8, rc));
+ dret = (struct dentry *)ERR_PTR(-EPROTO);
+ }
+ VbglR0PhysHeapFree(pReq);
+
+ /*
+ * When dret is set to dentry we got something to insert,
+ * though it may be negative (pInode == NULL).
+ */
+ if (dret == dentry) {
+ vbsf_dentry_set_update_jiffies(dentry, jiffies);
+ vbsf_d_add_inode(dentry, pInode);
+ dret = NULL;
+ }
+ } else {
+ SFLOGFLOW(("vbsf_inode_lookup: -ENOMEM (phys heap)\n"));
+ dret = (struct dentry *)ERR_PTR(-ENOMEM);
+ }
+ if (path)
+ kfree(path);
+ } else {
+ SFLOG(("vbsf_inode_lookup: vbsf_path_from_dentry failed: %d\n", rc));
+ dret = (struct dentry *)ERR_PTR(rc);
+ }
+ return dret;
+}
+
+
+/**
+ * This should allocate memory for vbsf_inode_info, compute a unique inode
+ * number, get an inode from vfs, initialize inode info, instantiate
+ * dentry.
+ *
+ * @param parent inode entry of the directory
+ * @param dentry directory cache entry
+ * @param path path name. Consumed on success.
+ * @param info file information
+ * @param handle handle
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int vbsf_inode_instantiate(struct inode *parent, struct dentry *dentry, PSHFLSTRING path,
+ PSHFLFSOBJINFO info, SHFLHANDLE handle)
+{
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(parent->i_sb);
+ struct inode *pInode = vbsf_create_inode(parent, dentry, path, info, pSuperInfo, true /*fInstantiate*/);
+ if (pInode) {
+ /* Store this handle if we leave the handle open. */
+ struct vbsf_inode_info *sf_new_i = VBSF_GET_INODE_INFO(pInode);
+ sf_new_i->handle = handle;
+ return 0;
+ }
+ return -ENOMEM;
+}
+
+
+/**
+ * Create a new regular file / directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @param fCreateFlags SHFL_CF_XXX.
+ * @param fStashHandle Whether the resulting handle should be stashed in
+ * the inode for a subsequent open call.
+ * @param fDoLookup Whether we're doing a lookup and need to d_add the
+ * inode we create to dentry.
+ * @param phHostFile Where to return the handle to the create file/dir.
+ * @param pfCreated Where to indicate whether the file/dir was created
+ * or not. Optional.
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int vbsf_create_worker(struct inode *parent, struct dentry *dentry, umode_t mode, uint32_t fCreateFlags,
+ bool fStashHandle, bool fDoLookup, SHFLHANDLE *phHostFile, bool *pfCreated)
+
+{
+#ifdef SFLOG_ENABLED
+ const char * const pszPrefix = S_ISDIR(mode) ? "vbsf_create_worker/dir:" : "vbsf_create_worker/file:";
+#endif
+ struct vbsf_inode_info *sf_parent_i = VBSF_GET_INODE_INFO(parent);
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(parent->i_sb);
+ PSHFLSTRING path;
+ int rc;
+
+ if (pfCreated)
+ *pfCreated = false;
+ AssertReturn(sf_parent_i, -EINVAL);
+ AssertReturn(pSuperInfo, -EINVAL);
+
+ /*
+ * Build a path. We'll donate this to the inode on success.
+ */
+ rc = vbsf_path_from_dentry(pSuperInfo, sf_parent_i, dentry, &path, __func__);
+ if (rc == 0) {
+ /*
+ * Allocate, initialize and issue the SHFL_CREATE request.
+ */
+ /** @todo combine with vbsf_path_from_dentry? */
+ union CreateAuxReq
+ {
+ VBOXSFCREATEREQ Create;
+ VBOXSFCLOSEREQ Close;
+ } *pReq = (union CreateAuxReq *)VbglR0PhysHeapAlloc(RT_UOFFSETOF(VBOXSFCREATEREQ, StrPath.String) + path->u16Size);
+ if (pReq) {
+ RT_BCOPY_UNFORTIFIED(&pReq->Create.StrPath, path, SHFLSTRING_HEADER_SIZE + path->u16Size);
+ RT_ZERO(pReq->Create.CreateParms);
+ pReq->Create.CreateParms.Handle = SHFL_HANDLE_NIL;
+ pReq->Create.CreateParms.CreateFlags = fCreateFlags;
+ pReq->Create.CreateParms.Info.Attr.fMode = (S_ISDIR(mode) ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE)
+ | sf_access_permissions_to_vbox(mode);
+ pReq->Create.CreateParms.Info.Attr.enmAdditional = SHFLFSOBJATTRADD_NOTHING;
+
+ SFLOGFLOW(("%s calling VbglR0SfHostReqCreate(%s, %#x)\n", pszPrefix, path->String.ach, pReq->Create.CreateParms.CreateFlags));
+ rc = VbglR0SfHostReqCreate(pSuperInfo->map.root, &pReq->Create);
+ if (RT_SUCCESS(rc)) {
+ SFLOGFLOW(("%s VbglR0SfHostReqCreate returned %Rrc Result=%d Handle=%#llx\n",
+ pszPrefix, rc, pReq->Create.CreateParms.Result, pReq->Create.CreateParms.Handle));
+
+ /*
+ * Work the dentry cache and inode restatting.
+ */
+ if ( pReq->Create.CreateParms.Result == SHFL_FILE_CREATED
+ || pReq->Create.CreateParms.Result == SHFL_FILE_REPLACED) {
+ vbsf_dentry_chain_increase_parent_ttl(dentry);
+ sf_parent_i->force_restat = 1;
+ } else if ( pReq->Create.CreateParms.Result == SHFL_FILE_EXISTS
+ || pReq->Create.CreateParms.Result == SHFL_FILE_NOT_FOUND)
+ vbsf_dentry_chain_increase_parent_ttl(dentry);
+
+ /*
+ * If we got a handle back, we're good. Create an inode for it and return.
+ */
+ if (pReq->Create.CreateParms.Handle != SHFL_HANDLE_NIL) {
+ struct inode *pNewInode = vbsf_create_inode(parent, dentry, path, &pReq->Create.CreateParms.Info, pSuperInfo,
+ !fDoLookup /*fInstantiate*/);
+ if (pNewInode) {
+ struct vbsf_inode_info *sf_new_i = VBSF_GET_INODE_INFO(pNewInode);
+ if (phHostFile) {
+ *phHostFile = pReq->Create.CreateParms.Handle;
+ pReq->Create.CreateParms.Handle = SHFL_HANDLE_NIL;
+ } else if (fStashHandle) {
+ sf_new_i->handle = pReq->Create.CreateParms.Handle;
+ pReq->Create.CreateParms.Handle = SHFL_HANDLE_NIL;
+ }
+ if (pfCreated)
+ *pfCreated = pReq->Create.CreateParms.Result == SHFL_FILE_CREATED;
+ if (fDoLookup)
+ vbsf_d_add_inode(dentry, pNewInode);
+ path = NULL;
+ } else {
+ SFLOGFLOW(("%s vbsf_create_inode failed: -ENOMEM (path %s)\n", pszPrefix, rc, path->String.ach));
+ rc = -ENOMEM;
+ }
+ } else if (pReq->Create.CreateParms.Result == SHFL_FILE_EXISTS) {
+ /*
+ * For atomic_open (at least), we should create an inode and
+ * convert the dentry from a negative to a positive one.
+ */
+ SFLOGFLOW(("%s SHFL_FILE_EXISTS for %s\n", pszPrefix, sf_parent_i->path->String.ach));
+ if (fDoLookup) {
+ struct inode *pNewInode = vbsf_create_inode(parent, dentry, path, &pReq->Create.CreateParms.Info,
+ pSuperInfo, false /*fInstantiate*/);
+ if (pNewInode)
+ vbsf_d_add_inode(dentry, pNewInode);
+ path = NULL;
+ }
+ rc = -EEXIST;
+ } else if (pReq->Create.CreateParms.Result == SHFL_FILE_NOT_FOUND) {
+ SFLOGFLOW(("%s SHFL_FILE_NOT_FOUND for %s\n", pszPrefix, sf_parent_i->path->String.ach));
+ rc = -ENOENT;
+ } else if (pReq->Create.CreateParms.Result == SHFL_PATH_NOT_FOUND) {
+ SFLOGFLOW(("%s SHFL_PATH_NOT_FOUND for %s\n", pszPrefix, sf_parent_i->path->String.ach));
+ rc = -ENOENT;
+ } else {
+ AssertMsgFailed(("result=%d creating '%s'\n", pReq->Create.CreateParms.Result, sf_parent_i->path->String.ach));
+ rc = -EPERM;
+ }
+ } else {
+ int const vrc = rc;
+ rc = -RTErrConvertToErrno(vrc);
+ SFLOGFLOW(("%s SHFL_FN_CREATE(%s) failed vrc=%Rrc rc=%d\n", pszPrefix, path->String.ach, vrc, rc));
+ }
+
+ /* Cleanups. */
+ if (pReq->Create.CreateParms.Handle != SHFL_HANDLE_NIL) {
+ AssertCompile(RTASSERT_OFFSET_OF(VBOXSFCREATEREQ, CreateParms.Handle) > sizeof(VBOXSFCLOSEREQ)); /* no aliasing issues */
+ int rc2 = VbglR0SfHostReqClose(pSuperInfo->map.root, &pReq->Close, pReq->Create.CreateParms.Handle);
+ if (RT_FAILURE(rc2))
+ SFLOGFLOW(("%s VbglR0SfHostReqCloseSimple failed rc=%Rrc\n", pszPrefix, rc2));
+ }
+ VbglR0PhysHeapFree(pReq);
+ } else
+ rc = -ENOMEM;
+ if (path)
+ kfree(path);
+ }
+ return rc;
+}
+
+
+#if RTLNX_VER_MIN(3,16,0)
+/**
+ * More atomic way of handling creation.
+ *
+ * Older kernels would first to a lookup that created the file, followed by
+ * an open call. We've got this horrid vbsf_inode_info::handle member because
+ * of that approach. The call combines the lookup and open.
+ */
+static int vbsf_inode_atomic_open(struct inode *pDirInode, struct dentry *dentry, struct file *file, unsigned fOpen,
+ umode_t fMode
+# if RTLNX_VER_MAX(4,19,0)
+ , int *opened
+# endif
+ )
+{
+ SFLOGFLOW(("vbsf_inode_atomic_open: pDirInode=%p dentry=%p file=%p fOpen=%#x, fMode=%#x\n", pDirInode, dentry, file, fOpen, fMode));
+ int rc;
+
+ /* Code assumes negative dentry. */
+ Assert(dentry->d_inode == NULL);
+
+ /** @todo see if we can do this for non-create calls too, as it may save us a
+ * host call to revalidate the dentry. (Can't see anyone else doing
+ * this, so playing it safe for now.) */
+ if (fOpen & O_CREAT) {
+ /*
+ * Prepare our file info structure.
+ */
+ struct vbsf_reg_info *sf_r = kmalloc(sizeof(*sf_r), GFP_KERNEL);
+ if (sf_r) {
+ bool fCreated = false;
+ uint32_t fCreateFlags;
+
+ RTListInit(&sf_r->Handle.Entry);
+ sf_r->Handle.cRefs = 1;
+ sf_r->Handle.fFlags = !(fOpen & O_DIRECTORY)
+ ? VBSF_HANDLE_F_FILE | VBSF_HANDLE_F_MAGIC
+ : VBSF_HANDLE_F_DIR | VBSF_HANDLE_F_MAGIC;
+ sf_r->Handle.hHost = SHFL_HANDLE_NIL;
+
+ /*
+ * Try create it.
+ */
+ /* vbsf_create_worker uses the type from fMode, so match it up to O_DIRECTORY. */
+ AssertMsg(!(fMode & S_IFMT) || (fMode & S_IFMT) == (fOpen & O_DIRECTORY ? S_IFDIR : S_IFREG), ("0%o\n", fMode));
+ if (!(fOpen & O_DIRECTORY))
+ fMode = (fMode & ~S_IFMT) | S_IFREG;
+ else
+ fMode = (fMode & ~S_IFMT) | S_IFDIR;
+
+ fCreateFlags = vbsf_linux_oflags_to_vbox(fOpen, &sf_r->Handle.fFlags, __FUNCTION__);
+
+ rc = vbsf_create_worker(pDirInode, dentry, fMode, fCreateFlags, false /*fStashHandle*/, true /*fDoLookup*/,
+ &sf_r->Handle.hHost, &fCreated);
+ if (rc == 0) {
+ struct inode *inode = dentry->d_inode;
+ struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(inode);
+
+ /*
+ * Set FMODE_CREATED according to the action taken by SHFL_CREATE
+ * and call finish_open() to do the remaining open() work.
+ */
+# if RTLNX_VER_MIN(4,19,0)
+ if (fCreated)
+ file->f_mode |= FMODE_CREATED;
+ rc = finish_open(file, dentry, generic_file_open);
+# else
+ if (fCreated)
+ *opened |= FILE_CREATED;
+ rc = finish_open(file, dentry, generic_file_open, opened);
+# endif
+ if (rc == 0) {
+ /*
+ * Now that the file is fully opened, associate sf_r with it
+ * and link the handle to the inode.
+ */
+ vbsf_handle_append(sf_i, &sf_r->Handle);
+ file->private_data = sf_r;
+ SFLOGFLOW(("vbsf_inode_atomic_open: create succeeded; hHost=%#llx path='%s'\n",
+ rc, sf_r->Handle.hHost, sf_i->path->String.ach));
+ sf_r = NULL; /* don't free it */
+ } else {
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(pDirInode->i_sb);
+ SFLOGFLOW(("vbsf_inode_atomic_open: finish_open failed: %d (path='%s'\n", rc, sf_i->path->String.ach));
+ VbglR0SfHostReqCloseSimple(pSuperInfo->map.root, sf_r->Handle.hHost);
+ sf_r->Handle.hHost = SHFL_HANDLE_NIL;
+ }
+ } else
+ SFLOGFLOW(("vbsf_inode_atomic_open: vbsf_create_worker failed: %d\n", rc));
+ if (sf_r)
+ kfree(sf_r);
+ } else {
+ LogRelMaxFunc(64, ("could not allocate reg info\n"));
+ rc = -ENOMEM;
+ }
+ }
+ /*
+ * Not creating anything.
+ * Do we need to do a lookup or should we just fail?
+ */
+ else if (d_in_lookup(dentry)) {
+ struct dentry *pResult = vbsf_inode_lookup(pDirInode, dentry, 0 /*fFlags*/);
+ if (!IS_ERR(pResult))
+ rc = finish_no_open(file, pResult);
+ else
+ rc = PTR_ERR(pResult);
+ SFLOGFLOW(("vbsf_inode_atomic_open: open -> %d (%p)\n", rc, pResult));
+ } else {
+ SFLOGFLOW(("vbsf_inode_atomic_open: open -> -ENOENT\n"));
+ rc = -ENOENT;
+ }
+ return rc;
+}
+#endif /* 3.6.0 */
+
+
+/**
+ * Create a new regular file.
+ *
+ * @param idmap idmap of the mount.
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @param excl Possible O_EXCL...
+ * @returns 0 on success, Linux error code otherwise
+ */
+#if RTLNX_VER_MIN(6,3,0) || defined(DOXYGEN_RUNNING)
+static int vbsf_inode_create(struct mnt_idmap *idmap, struct inode *parent, struct dentry *dentry, umode_t mode, bool excl)
+#elif RTLNX_VER_MIN(5,12,0)
+static int vbsf_inode_create(struct user_namespace *ns, struct inode *parent, struct dentry *dentry, umode_t mode, bool excl)
+#elif RTLNX_VER_MIN(3,6,0)
+static int vbsf_inode_create(struct inode *parent, struct dentry *dentry, umode_t mode, bool excl)
+#elif RTLNX_VER_MIN(3,3,0)
+static int vbsf_inode_create(struct inode *parent, struct dentry *dentry, umode_t mode, struct nameidata *nd)
+#elif RTLNX_VER_MIN(2,5,75)
+static int vbsf_inode_create(struct inode *parent, struct dentry *dentry, int mode, struct nameidata *nd)
+#else
+static int vbsf_inode_create(struct inode *parent, struct dentry *dentry, int mode)
+#endif
+{
+ uint32_t fCreateFlags = SHFL_CF_ACT_CREATE_IF_NEW
+ | SHFL_CF_ACT_FAIL_IF_EXISTS
+ | SHFL_CF_ACCESS_READWRITE;
+#if RTLNX_VER_RANGE(2,5,75, 3,6,0)
+ /* Clear the RD flag if write-only access requested. Otherwise assume we
+ need write access to create stuff. */
+ if (!(nd->intent.open.flags & 1) ) {
+ fCreateFlags &= SHFL_CF_ACCESS_READWRITE;
+ fCreateFlags |= SHFL_CF_ACCESS_WRITE;
+ }
+ /* (file since 2.6.15) */
+#endif
+ TRACE();
+ AssertMsg(!(mode & S_IFMT) || (mode & S_IFMT) == S_IFREG, ("0%o\n", mode));
+ return vbsf_create_worker(parent, dentry, (mode & ~S_IFMT) | S_IFREG, fCreateFlags,
+ true /*fStashHandle*/, false /*fDoLookup*/, NULL /*phHandle*/, NULL /*fCreated*/);
+}
+
+
+/**
+ * Create a new directory.
+ *
+ * @param idmap idmap of the mount.
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @returns 0 on success, Linux error code otherwise
+ */
+#if RTLNX_VER_MIN(6,3,0) || defined(DOXYGEN_RUNNING)
+static int vbsf_inode_mkdir(struct mnt_idmap *idmap, struct inode *parent, struct dentry *dentry, umode_t mode)
+#elif RTLNX_VER_MIN(5,12,0)
+static int vbsf_inode_mkdir(struct user_namespace *ns, struct inode *parent, struct dentry *dentry, umode_t mode)
+#elif RTLNX_VER_MIN(3,3,0)
+static int vbsf_inode_mkdir(struct inode *parent, struct dentry *dentry, umode_t mode)
+#else
+static int vbsf_inode_mkdir(struct inode *parent, struct dentry *dentry, int mode)
+#endif
+{
+ TRACE();
+ AssertMsg(!(mode & S_IFMT) || (mode & S_IFMT) == S_IFDIR, ("0%o\n", mode));
+ return vbsf_create_worker(parent, dentry, (mode & ~S_IFMT) | S_IFDIR,
+ SHFL_CF_ACT_CREATE_IF_NEW
+ | SHFL_CF_ACT_FAIL_IF_EXISTS
+ | SHFL_CF_ACCESS_READWRITE
+ | SHFL_CF_DIRECTORY,
+ false /*fStashHandle*/, false /*fDoLookup*/, NULL /*phHandle*/, NULL /*fCreated*/);
+}
+
+
+/**
+ * Remove a regular file / directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param fDirectory true if directory, false otherwise
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int vbsf_unlink_worker(struct inode *parent, struct dentry *dentry, int fDirectory)
+{
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(parent->i_sb);
+ struct vbsf_inode_info *sf_parent_i = VBSF_GET_INODE_INFO(parent);
+ SHFLSTRING *path;
+ int rc;
+
+ TRACE();
+
+ rc = vbsf_path_from_dentry(pSuperInfo, sf_parent_i, dentry, &path, __func__);
+ if (!rc) {
+ VBOXSFREMOVEREQ *pReq = (VBOXSFREMOVEREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF(VBOXSFREMOVEREQ, StrPath.String)
+ + path->u16Size);
+ if (pReq) {
+ RT_BCOPY_UNFORTIFIED(&pReq->StrPath, path, SHFLSTRING_HEADER_SIZE + path->u16Size);
+ uint32_t fFlags = fDirectory ? SHFL_REMOVE_DIR : SHFL_REMOVE_FILE;
+ if (dentry->d_inode && ((dentry->d_inode->i_mode & S_IFLNK) == S_IFLNK))
+ fFlags |= SHFL_REMOVE_SYMLINK;
+
+ rc = VbglR0SfHostReqRemove(pSuperInfo->map.root, pReq, fFlags);
+
+ if (dentry->d_inode) {
+ struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(dentry->d_inode);
+ sf_i->force_restat = true;
+ }
+
+ if (RT_SUCCESS(rc)) {
+ sf_parent_i->force_restat = true; /* directory access/change time changed */
+ rc = 0;
+ } else if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) {
+ /* Probably deleted on the host while the guest had it cached, so don't complain: */
+ LogFunc(("(%d): VbglR0SfRemove(%s) failed rc=%Rrc; calling d_drop on %p\n",
+ fDirectory, path->String.ach, rc, dentry));
+ sf_parent_i->force_restat = true;
+ d_drop(dentry);
+ rc = 0;
+ } else {
+ LogFunc(("(%d): VbglR0SfRemove(%s) failed rc=%Rrc\n", fDirectory, path->String.ach, rc));
+ rc = -RTErrConvertToErrno(rc);
+ }
+ VbglR0PhysHeapFree(pReq);
+ } else
+ rc = -ENOMEM;
+ kfree(path);
+ }
+ return rc;
+}
+
+
+/**
+ * Remove a regular file.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int vbsf_inode_unlink(struct inode *parent, struct dentry *dentry)
+{
+ TRACE();
+ return vbsf_unlink_worker(parent, dentry, false /*fDirectory*/);
+}
+
+
+/**
+ * Remove a directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int vbsf_inode_rmdir(struct inode *parent, struct dentry *dentry)
+{
+ TRACE();
+ return vbsf_unlink_worker(parent, dentry, true /*fDirectory*/);
+}
+
+
+/**
+ * Rename a regular file / directory.
+ *
+ * @param idmap idmap of the mount.
+ * @param old_parent inode of the old parent directory
+ * @param old_dentry old directory cache entry
+ * @param new_parent inode of the new parent directory
+ * @param new_dentry new directory cache entry
+ * @param flags flags
+ * @returns 0 on success, Linux error code otherwise
+ */
+#if RTLNX_VER_MIN(6,3,0) || defined(DOXYGEN_RUNNING)
+static int vbsf_inode_rename(struct mnt_idmap *idmap,
+ struct inode *old_parent, struct dentry *old_dentry,
+ struct inode *new_parent, struct dentry *new_dentry, unsigned flags)
+#elif RTLNX_VER_MIN(5,12,0)
+static int vbsf_inode_rename(struct user_namespace *ns,
+ struct inode *old_parent, struct dentry *old_dentry,
+ struct inode *new_parent, struct dentry *new_dentry, unsigned flags)
+#else
+static int vbsf_inode_rename(struct inode *old_parent, struct dentry *old_dentry,
+ struct inode *new_parent, struct dentry *new_dentry, unsigned flags)
+#endif
+{
+ /*
+ * Deal with flags.
+ */
+ int rc;
+ uint32_t fRename = (old_dentry->d_inode->i_mode & S_IFDIR ? SHFL_RENAME_DIR : SHFL_RENAME_FILE)
+ | SHFL_RENAME_REPLACE_IF_EXISTS;
+#if RTLNX_VER_MIN(3,15,0)
+ if (!(flags & ~RENAME_NOREPLACE)) {
+ if (flags & RENAME_NOREPLACE)
+ fRename &= ~SHFL_RENAME_REPLACE_IF_EXISTS;
+#endif
+ /*
+ * Check that they are on the same mount.
+ */
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(old_parent->i_sb);
+ if (pSuperInfo == VBSF_GET_SUPER_INFO(new_parent->i_sb)) {
+ /*
+ * Build the new path.
+ */
+ struct vbsf_inode_info *sf_new_parent_i = VBSF_GET_INODE_INFO(new_parent);
+ PSHFLSTRING pNewPath;
+ rc = vbsf_path_from_dentry(pSuperInfo, sf_new_parent_i, new_dentry, &pNewPath, __func__);
+ if (rc == 0) {
+ /*
+ * Create and issue the rename request.
+ */
+ VBOXSFRENAMEWITHSRCBUFREQ *pReq;
+ pReq = (VBOXSFRENAMEWITHSRCBUFREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF(VBOXSFRENAMEWITHSRCBUFREQ, StrDstPath.String)
+ + pNewPath->u16Size);
+ if (pReq) {
+ struct vbsf_inode_info *sf_file_i = VBSF_GET_INODE_INFO(old_dentry->d_inode);
+ PSHFLSTRING pOldPath = sf_file_i->path;
+
+ RT_BCOPY_UNFORTIFIED(&pReq->StrDstPath, pNewPath, SHFLSTRING_HEADER_SIZE + pNewPath->u16Size);
+ rc = VbglR0SfHostReqRenameWithSrcContig(pSuperInfo->map.root, pReq, pOldPath, virt_to_phys(pOldPath), fRename);
+ VbglR0PhysHeapFree(pReq);
+ if (RT_SUCCESS(rc)) {
+ /*
+ * On success we replace the path in the inode and trigger
+ * restatting of both parent directories.
+ */
+ struct vbsf_inode_info *sf_old_parent_i = VBSF_GET_INODE_INFO(old_parent);
+ SFLOGFLOW(("vbsf_inode_rename: %s -> %s (%#x)\n", pOldPath->String.ach, pNewPath->String.ach, fRename));
+
+ sf_file_i->path = pNewPath;
+ kfree(pOldPath);
+ pNewPath = NULL;
+
+ sf_new_parent_i->force_restat = 1;
+ sf_old_parent_i->force_restat = 1;
+
+ vbsf_dentry_chain_increase_parent_ttl(old_dentry);
+ vbsf_dentry_chain_increase_parent_ttl(new_dentry);
+
+ rc = 0;
+ } else {
+ SFLOGFLOW(("vbsf_inode_rename: VbglR0SfHostReqRenameWithSrcContig(%s,%s,%#x) failed -> %d\n",
+ pOldPath->String.ach, pNewPath->String.ach, fRename, rc));
+ if (rc == VERR_IS_A_DIRECTORY || rc == VERR_IS_A_FILE)
+ vbsf_dentry_invalidate_ttl(old_dentry);
+ rc = -RTErrConvertToErrno(rc);
+ }
+ } else {
+ SFLOGFLOW(("vbsf_inode_rename: failed to allocate request (%#x bytes)\n",
+ RT_UOFFSETOF(VBOXSFRENAMEWITHSRCBUFREQ, StrDstPath.String) + pNewPath->u16Size));
+ rc = -ENOMEM;
+ }
+ if (pNewPath)
+ kfree(pNewPath);
+ } else
+ SFLOGFLOW(("vbsf_inode_rename: vbsf_path_from_dentry failed: %d\n", rc));
+ } else {
+ SFLOGFLOW(("vbsf_inode_rename: rename with different roots (%#x vs %#x)\n",
+ pSuperInfo->map.root, VBSF_GET_SUPER_INFO(new_parent->i_sb)->map.root));
+ rc = -EXDEV;
+ }
+#if RTLNX_VER_MIN(3,15,0)
+ } else {
+ SFLOGFLOW(("vbsf_inode_rename: Unsupported flags: %#x\n", flags));
+ rc = -EINVAL;
+ }
+#else
+ RT_NOREF(flags);
+#endif
+ return rc;
+}
+
+
+#if RTLNX_VER_MAX(4,9,0)
+/**
+ * The traditional rename interface without any flags.
+ */
+static int vbsf_inode_rename_no_flags(struct inode *old_parent, struct dentry *old_dentry,
+ struct inode *new_parent, struct dentry *new_dentry)
+{
+ return vbsf_inode_rename(old_parent, old_dentry, new_parent, new_dentry, 0);
+}
+#endif
+
+
+/**
+ * Create a symbolic link.
+ */
+#if RTLNX_VER_MIN(6,3,0)
+static int vbsf_inode_symlink(struct mnt_idmap *idmap, struct inode *parent, struct dentry *dentry, const char *target)
+#elif RTLNX_VER_MIN(5,12,0)
+static int vbsf_inode_symlink(struct user_namespace *ns, struct inode *parent, struct dentry *dentry, const char *target)
+#else
+static int vbsf_inode_symlink(struct inode *parent, struct dentry *dentry, const char *target)
+#endif
+{
+ /*
+ * Turn the target into a string (contiguous physcial memory).
+ */
+ /** @todo we can save a kmalloc here if we switch to embedding the target rather
+ * than the symlink path into the request. Will require more NLS helpers. */
+ struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(parent->i_sb);
+ PSHFLSTRING pTarget = NULL;
+ int rc = vbsf_nls_to_shflstring(pSuperInfo, target, &pTarget);
+ if (rc == 0) {
+ /*
+ * Create a full path for the symlink name.
+ */
+ struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(parent);
+ PSHFLSTRING pPath = NULL;
+ rc = vbsf_path_from_dentry(pSuperInfo, sf_i, dentry, &pPath, __func__);
+ if (rc == 0) {
+ /*
+ * Create the request and issue it.
+ */
+ uint32_t const cbReq = RT_UOFFSETOF(VBOXSFCREATESYMLINKREQ, StrSymlinkPath.String) + pPath->u16Size;
+ VBOXSFCREATESYMLINKREQ *pReq = (VBOXSFCREATESYMLINKREQ *)VbglR0PhysHeapAlloc(cbReq);
+ if (pReq) {
+ RT_ZERO(*pReq);
+ RT_BCOPY_UNFORTIFIED(&pReq->StrSymlinkPath, pPath, SHFLSTRING_HEADER_SIZE + pPath->u16Size);
+
+ rc = VbglR0SfHostReqCreateSymlinkContig(pSuperInfo->map.root, pTarget, virt_to_phys(pTarget), pReq);
+ if (RT_SUCCESS(rc)) {
+ sf_i->force_restat = 1;
+
+ /*
+ * Instantiate a new inode for the symlink.
+ */
+ rc = vbsf_inode_instantiate(parent, dentry, pPath, &pReq->ObjInfo, SHFL_HANDLE_NIL);
+ if (rc == 0) {
+ SFLOGFLOW(("vbsf_inode_symlink: Successfully created '%s' -> '%s'\n", pPath->String.ach, pTarget->String.ach));
+ pPath = NULL; /* consumed by inode */
+ vbsf_dentry_chain_increase_ttl(dentry);
+ } else {
+ SFLOGFLOW(("vbsf_inode_symlink: Failed to create inode for '%s': %d\n", pPath->String.ach, rc));
+ vbsf_dentry_chain_increase_parent_ttl(dentry);
+ vbsf_dentry_invalidate_ttl(dentry);
+ }
+ } else {
+ int const vrc = rc;
+ if (vrc == VERR_WRITE_PROTECT)
+ rc = -EPERM; /* EPERM: Symlink creation not supported according to the linux manpage as of 2017-09-15.
+ "VBoxInternal2/SharedFoldersEnableSymlinksCreate/<share>" is not 1. */
+ else
+ rc = -RTErrConvertToErrno(vrc);
+ SFLOGFLOW(("vbsf_inode_symlink: VbglR0SfHostReqCreateSymlinkContig failed for '%s' -> '%s': %Rrc (-> %d)\n",
+ pPath->String.ach, pTarget->String.ach, vrc, rc));
+ }
+ VbglR0PhysHeapFree(pReq);
+ } else {
+ SFLOGFLOW(("vbsf_inode_symlink: failed to allocate %u phys heap for the request!\n", cbReq));
+ rc = -ENOMEM;
+ }
+ if (pPath)
+ kfree(pPath);
+ }
+ kfree(pTarget);
+ }
+ return rc;
+}
+
+
+/**
+ * Directory inode operations.
+ */
+struct inode_operations vbsf_dir_iops = {
+ .lookup = vbsf_inode_lookup,
+#if RTLNX_VER_MIN(3,16,0)
+ .atomic_open = vbsf_inode_atomic_open,
+#endif
+ .create = vbsf_inode_create,
+ .symlink = vbsf_inode_symlink,
+ .mkdir = vbsf_inode_mkdir,
+ .rmdir = vbsf_inode_rmdir,
+ .unlink = vbsf_inode_unlink,
+#if RTLNX_VER_MIN(4,9,0)
+ .rename = vbsf_inode_rename,
+#else
+# if RTLNX_VER_MAX(3,17,0)
+ .rename = vbsf_inode_rename_no_flags,
+# endif
+# if RTLNX_VER_MIN(3,15,0)
+ .rename2 = vbsf_inode_rename,
+# endif
+#endif
+#if RTLNX_VER_MIN(2,5,18)
+ .getattr = vbsf_inode_getattr,
+#else
+ .revalidate = vbsf_inode_revalidate,
+#endif
+ .setattr = vbsf_inode_setattr,
+};
+