summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/SharedFolders/vbsf.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsf.cpp2107
1 files changed, 2107 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedFolders/vbsf.cpp b/src/VBox/HostServices/SharedFolders/vbsf.cpp
new file mode 100644
index 00000000..e346b3f8
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsf.cpp
@@ -0,0 +1,2107 @@
+/* $Id: vbsf.cpp $ */
+/** @file
+ * Shared Folders - VBox Shared Folders.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#ifdef UNITTEST
+# include "testcase/tstSharedFolderService.h"
+#endif
+
+#include "vbsfpath.h"
+#include "mappings.h"
+#include "vbsf.h"
+#include "shflhandle.h"
+
+#include <VBox/AssertGuest.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#include <iprt/uni.h>
+#include <iprt/stream.h>
+#ifdef RT_OS_DARWIN
+# include <Carbon/Carbon.h>
+#endif
+
+#ifdef UNITTEST
+# include "teststubs.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
+
+
+/**
+ * @todo find a better solution for supporting the execute bit for non-windows
+ * guests on windows host. Search for "0111" to find all the relevant places.
+ */
+
+void vbsfStripLastComponent(char *pszFullPath, uint32_t cbFullPathRoot)
+{
+ RTUNICP cp;
+
+ /* Do not strip root. */
+ char *s = pszFullPath + cbFullPathRoot;
+ char *delimSecondLast = NULL;
+ char *delimLast = NULL;
+
+ LogFlowFunc(("%s -> %s\n", pszFullPath, s));
+
+ for (;;)
+ {
+ cp = RTStrGetCp(s);
+
+ if (cp == RTUNICP_INVALID || cp == 0)
+ {
+ break;
+ }
+
+ if (cp == RTPATH_DELIMITER)
+ {
+ if (delimLast != NULL)
+ {
+ delimSecondLast = delimLast;
+ }
+
+ delimLast = s;
+ }
+
+ s = RTStrNextCp(s);
+ }
+
+ if (cp == 0)
+ {
+ if (delimLast + 1 == s)
+ {
+ if (delimSecondLast)
+ {
+ *delimSecondLast = 0;
+ }
+ else if (delimLast)
+ {
+ *delimLast = 0;
+ }
+ }
+ else
+ {
+ if (delimLast)
+ {
+ *delimLast = 0;
+ }
+ }
+ }
+
+ LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));
+}
+
+static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING pPath,
+ uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot,
+ bool fWildCard = false, bool fPreserveLastComponent = false)
+{
+ char *pszHostPath = NULL;
+ uint32_t fu32PathFlags = 0;
+ uint32_t fu32Options = VBSF_O_PATH_CHECK_ROOT_ESCAPE
+ | (fWildCard? VBSF_O_PATH_WILDCARD: 0)
+ | (fPreserveLastComponent? VBSF_O_PATH_PRESERVE_LAST_COMPONENT: 0);
+
+ int rc = vbsfPathGuestToHost(pClient, root, pPath, cbPath,
+ &pszHostPath, pcbFullPathRoot, fu32Options, &fu32PathFlags);
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*s]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length, &pPath->String.utf8[0], pszHostPath, rc));
+ }
+ else
+ {
+ LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*ls]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length / 2, &pPath->String.ucs2[0], pszHostPath, rc));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (ppszFullPath)
+ *ppszFullPath = pszHostPath;
+ }
+ return rc;
+}
+
+static void vbsfFreeFullPath(char *pszFullPath)
+{
+ vbsfFreeHostPath(pszFullPath);
+}
+
+typedef enum VBSFCHECKACCESS
+{
+ VBSF_CHECK_ACCESS_READ = 0,
+ VBSF_CHECK_ACCESS_WRITE = 1
+} VBSFCHECKACCESS;
+
+/**
+ * Check if the handle data is valid and the operation is allowed on the shared folder.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pHandle Information about the file or directory object.
+ * @param enmCheckAccess Whether the operation needs read only or write access.
+ */
+static int vbsfCheckHandleAccess(SHFLCLIENTDATA *pClient, SHFLROOT root,
+ SHFLFILEHANDLE *pHandle, VBSFCHECKACCESS enmCheckAccess)
+{
+ /* Handle from the same 'root' index? */
+ if (RT_LIKELY(RT_VALID_PTR(pHandle) && root == pHandle->root))
+ { /* likely */ }
+ else
+ return VERR_INVALID_HANDLE;
+
+ /* Check if the guest is still allowed to access this share.
+ * vbsfMappingsQueryWritable returns error if the shared folder has been removed from the VM settings.
+ */
+ bool fWritable;
+ int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return VERR_ACCESS_DENIED;
+
+ if (enmCheckAccess == VBSF_CHECK_ACCESS_WRITE)
+ {
+ /* Operation requires write access. Check if the shared folder is writable too. */
+ if (RT_LIKELY(fWritable))
+ { /* likely */ }
+ else
+ return VERR_WRITE_PROTECT;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags.
+ *
+ * @returns iprt status code
+ * @param fWritable whether the shared folder is writable
+ * @param fShflFlags shared folder create flags
+ * @param fMode file attributes
+ * @param handleInitial initial handle
+ * @retval pfOpen iprt create flags
+ */
+static int vbsfConvertFileOpenFlags(bool fWritable, unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint32_t *pfOpen)
+{
+ uint32_t fOpen = 0;
+ int rc = VINF_SUCCESS;
+
+ if ( (fMode & RTFS_DOS_MASK) != 0
+ && (fMode & RTFS_UNIX_MASK) == 0)
+ {
+ /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
+ * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
+ * May be better to use RTFsModeNormalize here.
+ */
+ fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
+ /* x for directories. */
+ if (fMode & RTFS_DOS_DIRECTORY)
+ fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
+ /* writable? */
+ if (!(fMode & RTFS_DOS_READONLY))
+ fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
+
+ /* Set the requested mode using only allowed bits. */
+ fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
+ }
+ else
+ {
+ /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
+ * and it contained random bits from stack. Detect this using the handle field value
+ * passed from the guest: old additions set it (incorrectly) to 0, new additions
+ * set it to SHFL_HANDLE_NIL(~0).
+ */
+ if (handleInitial == 0)
+ {
+ /* Old additions. Do nothing, use default mode. */
+ }
+ else
+ {
+ /* New additions or Windows additions. Set the requested mode using only allowed bits.
+ * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
+ * will be set in fOpen.
+ */
+ fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
+ }
+ }
+
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW))
+ {
+ default:
+ case SHFL_CF_ACCESS_NONE:
+ {
+#ifdef RT_OS_WINDOWS
+ if (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR) != SHFL_CF_ACCESS_ATTR_NONE)
+ fOpen |= RTFILE_O_ATTR_ONLY;
+ else
+#endif
+ fOpen |= RTFILE_O_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_NONE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_READ:
+ {
+ fOpen |= RTFILE_O_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_READ\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_WRITE:
+ {
+ fOpen |= RTFILE_O_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_WRITE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_READWRITE:
+ {
+ fOpen |= RTFILE_O_READWRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n"));
+ break;
+ }
+ }
+
+ if (fShflFlags & SHFL_CF_ACCESS_APPEND)
+ {
+ fOpen |= RTFILE_O_APPEND;
+ }
+
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR))
+ {
+ default:
+ case SHFL_CF_ACCESS_ATTR_NONE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_READ:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_WRITE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_READWRITE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n"));
+ break;
+ }
+ }
+
+ /* Sharing mask */
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY))
+ {
+ default:
+ case SHFL_CF_ACCESS_DENYNONE:
+ fOpen |= RTFILE_O_DENY_NONE;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYREAD:
+ fOpen |= RTFILE_O_DENY_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYWRITE:
+ fOpen |= RTFILE_O_DENY_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYALL:
+ fOpen |= RTFILE_O_DENY_ALL;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n"));
+ break;
+ }
+
+ /* Open/Create action mask */
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ {
+ case SHFL_CF_ACT_OPEN_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN_CREATE;
+ Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN;
+ Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_FAIL_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE;
+ Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_REPLACE_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE_REPLACE;
+ Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
+ Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_OVERWRITE_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE_REPLACE;
+ Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
+ Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ default:
+ rc = VERR_INVALID_PARAMETER;
+ Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (!fWritable)
+ fOpen &= ~RTFILE_O_WRITE;
+
+ *pfOpen = fOpen;
+ }
+ return rc;
+}
+
+/**
+ * Open a file or create and open a new one.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pszPath Path to the file or folder on the host.
+ * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
+ * @param pParms @a Info When a new file is created this specifies the initial parameters.
+ * When a file is created or overwritten, it also specifies the
+ * initial size.
+ * @retval pParms @a Resulte Shared folder status code, see include/VBox/shflsvc.h
+ * @retval pParms @a Handle On success the (shared folder) handle of the file opened or
+ * created
+ * @retval pParms @a Info On success the parameters of the file opened or created
+ */
+static int vbsfOpenFile(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath, SHFLCREATEPARMS *pParms)
+{
+ LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
+ Log(("SHFL create flags %08x\n", pParms->CreateFlags));
+
+ SHFLHANDLE handle = SHFL_HANDLE_NIL;
+ SHFLFILEHANDLE *pHandle = 0;
+ /* Open or create a file. */
+ uint32_t fOpen = 0;
+ bool fNoError = false;
+ static int cErrors;
+
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc))
+ fWritable = false;
+
+ rc = vbsfConvertFileOpenFlags(fWritable, pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_NO_MEMORY; /* Default error. */
+ handle = vbsfAllocFileHandle(pClient);
+ if (handle != SHFL_HANDLE_NIL)
+ {
+ pHandle = vbsfQueryFileHandle(pClient, handle);
+ if (pHandle)
+ {
+ pHandle->root = root;
+ rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen);
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_FILE_NOT_FOUND:
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+
+ /* This actually isn't an error, so correct the rc before return later,
+ because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
+ fNoError = true;
+ break;
+ case VERR_PATH_NOT_FOUND:
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+
+ /* This actually isn't an error, so correct the rc before return later,
+ because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
+ fNoError = true;
+ break;
+ case VERR_ALREADY_EXISTS:
+ RTFSOBJINFO info;
+
+ /** @todo Possible race left here. */
+ if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient))))
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ pParms->Result = SHFL_FILE_EXISTS;
+
+ /* This actually isn't an error, so correct the rc before return later,
+ because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
+ fNoError = true;
+ break;
+ case VERR_TOO_MANY_OPEN_FILES:
+ if (cErrors < 32)
+ {
+ LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
+#if defined RT_OS_LINUX || defined(RT_OS_SOLARIS)
+ if (cErrors < 1)
+ LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n"));
+#endif
+ cErrors++;
+ }
+ pParms->Result = SHFL_NO_RESULT;
+ break;
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ else
+ {
+ /** @note The shared folder status code is very approximate, as the runtime
+ * does not really provide this information. */
+ pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was
+ created when we eliminated the race. */
+ if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
+ {
+ /* For now, we do not treat a failure here as fatal. */
+ /** @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if
+ SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
+ RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
+ pParms->Result = SHFL_FILE_REPLACED;
+ }
+ if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ || ( SHFL_CF_ACT_CREATE_IF_NEW
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
+ {
+ pParms->Result = SHFL_FILE_CREATED;
+ }
+#if 0
+ /** @todo */
+ /* Set new attributes. */
+ if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ || ( SHFL_CF_ACT_CREATE_IF_NEW
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
+ {
+ RTFileSetTimes(pHandle->file.Handle,
+ &pParms->Info.AccessTime,
+ &pParms->Info.ModificationTime,
+ &pParms->Info.ChangeTime,
+ &pParms->Info.BirthTime
+ );
+
+ RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
+ }
+#endif
+ RTFSOBJINFO info;
+
+ /* Get file information */
+ rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ }
+ /* Free resources if any part of the function has failed. */
+ if (RT_FAILURE(rc))
+ {
+ if ( (0 != pHandle)
+ && (NIL_RTFILE != pHandle->file.Handle)
+ && (0 != pHandle->file.Handle))
+ {
+ RTFileClose(pHandle->file.Handle);
+ pHandle->file.Handle = NIL_RTFILE;
+ }
+ if (SHFL_HANDLE_NIL != handle)
+ {
+ vbsfFreeFileHandle(pClient, handle);
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ else
+ {
+ pParms->Handle = handle;
+ }
+
+ /* Report the driver that all is okay, we're done here */
+ if (fNoError)
+ rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Open a folder or create and open a new one.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pszPath Path to the file or folder on the host.
+ * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
+ * @retval pParms @a Result Shared folder status code, see include/VBox/shflsvc.h
+ * @retval pParms @a Handle On success the (shared folder) handle of the folder opened or
+ * created
+ * @retval pParms @a Info On success the parameters of the folder opened or created
+ *
+ * @note folders are created with fMode = 0777
+ */
+static int vbsfOpenDir(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath,
+ SHFLCREATEPARMS *pParms)
+{
+ LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
+ Log(("SHFL create flags %08x\n", pParms->CreateFlags));
+
+ int rc = VERR_NO_MEMORY;
+ SHFLHANDLE handle = vbsfAllocDirHandle(pClient);
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle);
+ if (0 != pHandle)
+ {
+ pHandle->root = root;
+ rc = VINF_SUCCESS;
+ pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
+ /** @todo Can anyone think of a sensible, race-less way to do this? Although
+ I suspect that the race is inherent, due to the API available... */
+ /* Try to create the folder first if "create if new" is specified. If this
+ fails, and "open if exists" is specified, then we ignore the failure and try
+ to open the folder anyway. */
+ if ( SHFL_CF_ACT_CREATE_IF_NEW
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ /** @todo render supplied attributes.
+ * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
+ RTFMODE fMode = 0777;
+
+ pParms->Result = SHFL_FILE_CREATED;
+ rc = RTDirCreate(pszPath, fMode, 0);
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_ALREADY_EXISTS:
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ case VERR_PATH_NOT_FOUND:
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ break;
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ }
+ if ( RT_SUCCESS(rc)
+ || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
+ {
+ /* Open the directory now */
+ rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO info;
+
+ rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ }
+ else
+ {
+ switch (rc)
+ {
+ case VERR_FILE_NOT_FOUND: /* Does this make sense? */
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+ break;
+ case VERR_PATH_NOT_FOUND:
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ break;
+ case VERR_ACCESS_DENIED:
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ {
+ if ( (0 != pHandle)
+ && (0 != pHandle->dir.Handle))
+ {
+ RTDirClose(pHandle->dir.Handle);
+ pHandle->dir.Handle = 0;
+ }
+ if (SHFL_HANDLE_NIL != handle)
+ {
+ vbsfFreeFileHandle(pClient, handle);
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ else
+ {
+ pParms->Handle = handle;
+ }
+ LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
+ return rc;
+}
+
+static int vbsfCloseDir(SHFLFILEHANDLE *pHandle)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
+ pHandle->dir.Handle, pHandle->dir.SearchHandle));
+
+ RTDirClose(pHandle->dir.Handle);
+
+ if (pHandle->dir.SearchHandle)
+ RTDirClose(pHandle->dir.SearchHandle);
+
+ if (pHandle->dir.pLastValidEntry)
+ {
+ RTMemFree(pHandle->dir.pLastValidEntry);
+ pHandle->dir.pLastValidEntry = NULL;
+ }
+
+ LogFlow(("vbsfCloseDir: rc = %d\n", rc));
+
+ return rc;
+}
+
+
+static int vbsfCloseFile(SHFLFILEHANDLE *pHandle)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCloseFile: Handle = %08X\n",
+ pHandle->file.Handle));
+
+ rc = RTFileClose(pHandle->file.Handle);
+
+ LogFlow(("vbsfCloseFile: rc = %d\n", rc));
+
+ return rc;
+}
+
+/**
+ * Look up file or folder information by host path.
+ *
+ * @returns iprt status code (currently VINF_SUCCESS)
+ * @param pClient client data
+ * @param pszPath The path of the file to be looked up
+ * @retval pParms->Result Status of the operation (success or error)
+ * @retval pParms->Info On success, information returned about the file
+ */
+static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
+{
+ RTFSOBJINFO info;
+ int rc;
+
+ rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ LogFlow(("SHFL_CF_LOOKUP\n"));
+ /* Client just wants to know if the object exists. */
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ }
+
+ case VERR_FILE_NOT_FOUND:
+ {
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case VERR_PATH_NOT_FOUND:
+ {
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_CREATE API. Located here as a form of API
+ * documentation. */
+void testCreate(RTTEST hTest)
+{
+ /* Simple opening of an existing file. */
+ testCreateFileSimple(hTest);
+ testCreateFileSimpleCaseInsensitive(hTest);
+ /* Simple opening of an existing directory. */
+ /** @todo How do wildcards in the path name work? */
+ testCreateDirSimple(hTest);
+ /* If the number or types of parameters are wrong the API should fail. */
+ testCreateBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+/**
+ * Create or open a file or folder. Perform character set and case
+ * conversion on the file name if necessary.
+ *
+ * @returns IPRT status code, but see note below
+ * @param pClient Data structure describing the client accessing the shared
+ * folder
+ * @param root The index of the shared folder in the table of mappings.
+ * The host path of the shared folder is found using this.
+ * @param pPath The path of the file or folder relative to the host path
+ * indexed by root.
+ * @param cbPath Presumably the length of the path in pPath. Actually
+ * ignored, as pPath contains a length parameter.
+ * @param pParms @a Info If a new file is created or an old one overwritten, set
+ * these attributes
+ * @retval pParms @a Result Shared folder result code, see include/VBox/shflsvc.h
+ * @retval pParms @a Handle Shared folder handle to the newly opened file
+ * @retval pParms @a Info Attributes of the file or folder opened
+ *
+ * @note This function returns success if a "non-exceptional" error occurred,
+ * such as "no such file". In this case, the caller should check the
+ * pParms->Result return value and whether pParms->Handle is valid.
+ */
+int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
+ pClient, pPath, cbPath, pParms, pParms->CreateFlags));
+
+ /* Check the client access rights to the root. */
+ /** @todo */
+
+ /* Build a host full path for the given path, handle file name case issues (if the guest
+ * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
+ * necessary.
+ */
+ char *pszFullPath = NULL;
+ uint32_t cbFullPathRoot = 0;
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
+ if (RT_SUCCESS(rc))
+ {
+ /* Reset return value in case client forgot to do so.
+ * pParms->Handle must not be reset here, as it is used
+ * in vbsfOpenFile to detect old additions.
+ */
+ pParms->Result = SHFL_NO_RESULT;
+
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
+ {
+ rc = vbsfLookupFile(pClient, pszFullPath, pParms);
+ }
+ else
+ {
+ /* Query path information. */
+ RTFSOBJINFO info;
+
+ rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Mark it as a directory in case the caller didn't. */
+ /**
+ * @todo I left this in in order not to change the behaviour of the
+ * function too much. Is it really needed, and should it really be
+ * here?
+ */
+ if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
+ {
+ pParms->CreateFlags |= SHFL_CF_DIRECTORY;
+ }
+
+ /**
+ * @todo This should be in the Windows Guest Additions, as no-one else
+ * needs it.
+ */
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
+ {
+ vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
+ pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
+ pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
+ pParms->CreateFlags |= SHFL_CF_DIRECTORY;
+ pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
+ }
+ }
+
+ rc = VINF_SUCCESS;
+
+ /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
+ * will cause changes.
+ *
+ * Actual operations (write, set attr, etc), which can write to a shared folder, have
+ * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
+ */
+ if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
+ || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
+ || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
+ )
+ {
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc) || !fWritable)
+ rc = VERR_WRITE_PROTECT;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
+ {
+ rc = vbsfOpenDir(pClient, root, pszFullPath, pParms);
+ }
+ else
+ {
+ rc = vbsfOpenFile(pClient, root, pszFullPath, pParms);
+ }
+ }
+ else
+ {
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+
+ Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
+
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
+ * documentation. */
+void testClose(RTTEST hTest)
+{
+ /* If the API parameters are invalid the API should fail. */
+ testCloseBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
+{
+ LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n",
+ pClient, root, Handle));
+
+ int rc = VERR_INVALID_HANDLE;
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
+ switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
+ {
+ case SHFL_HF_TYPE_DIR:
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ if (RT_LIKELY(pHandle && root == pHandle->root))
+ {
+ rc = vbsfCloseDir(pHandle);
+ vbsfFreeFileHandle(pClient, Handle);
+ }
+ break;
+ }
+ case SHFL_HF_TYPE_FILE:
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ if (RT_LIKELY(pHandle && root == pHandle->root))
+ {
+ rc = vbsfCloseFile(pHandle);
+ vbsfFreeFileHandle(pClient, Handle);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ LogFunc(("rc = %Rrc\n", rc));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_READ API. Located here as a form of API
+ * documentation. */
+void testRead(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testReadBadParameters(hTest);
+ /* Basic reading from a file. */
+ testReadFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
+ pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if (RT_LIKELY(*pcbBuffer != 0))
+ {
+ rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ size_t count = 0;
+ rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
+ *pcbBuffer = (uint32_t)count;
+ }
+ else
+ AssertRC(rc);
+ }
+ else
+ {
+ /* Reading zero bytes always succeeds. */
+ rc = VINF_SUCCESS;
+ }
+
+ LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
+ * documentation. */
+void testWrite(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testWriteBadParameters(hTest);
+ /* Simple test of writing to a file. */
+ testWriteFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
+ pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if (RT_LIKELY(*pcbBuffer != 0))
+ {
+ rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ size_t count = 0;
+ rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
+ *pcbBuffer = (uint32_t)count;
+ }
+ else
+ AssertRC(rc);
+ }
+ else
+ {
+ /** @todo What writing zero bytes should do? */
+ rc = VINF_SUCCESS;
+ }
+
+ LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer));
+ return rc;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
+ * documentation. */
+void testFlush(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testFlushBadParameters(hTest);
+ /* Simple opening and flushing of a file. */
+ testFlushFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
+{
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
+ pClient, root, Handle));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ rc = RTFileFlush(pHandle->file.Handle);
+
+ LogFunc(("%Rrc\n", rc));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_LIST API. Located here as a form of API
+ * documentation. */
+void testDirList(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testDirListBadParameters(hTest);
+ /* Test listing an empty directory (simple edge case). */
+ testDirListEmpty(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
+{
+ PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
+ uint32_t cbDirEntry, cbBufferOrg;
+ PSHFLDIRINFO pSFDEntry;
+ PRTUTF16 pwszString;
+ RTDIR hDir;
+ const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ Assert(*pIndex == 0);
+
+ cbDirEntry = 4096;
+ pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
+ if (pDirEntry == 0)
+ {
+ AssertFailed();
+ return VERR_NO_MEMORY;
+ }
+
+ cbBufferOrg = *pcbBuffer;
+ *pcbBuffer = 0;
+ pSFDEntry = (PSHFLDIRINFO)pBuffer;
+
+ *pIndex = 1; /* not yet complete */
+ *pcFiles = 0;
+
+ if (!pPath)
+ hDir = pHandle->dir.Handle;
+ else
+ {
+ if (pHandle->dir.SearchHandle == 0)
+ {
+ /* Build a host full path for the given path
+ * and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+
+ Assert(pHandle->dir.pLastValidEntry == 0);
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+
+ if (RT_FAILURE(rc))
+ goto end;
+ }
+ else
+ goto end;
+ flags &= ~SHFL_LIST_RESTART;
+ }
+ Assert(pHandle->dir.SearchHandle);
+ hDir = pHandle->dir.SearchHandle;
+ }
+
+ if (flags & SHFL_LIST_RESTART)
+ {
+ rc = RTDirRewind(hDir);
+ if (RT_FAILURE(rc))
+ goto end;
+ }
+
+ while (cbBufferOrg)
+ {
+ size_t cbDirEntrySize = cbDirEntry;
+ uint32_t cbNeeded;
+
+ /* Do we still have a valid last entry for the active search? If so, then return it here */
+ if (pHandle->dir.pLastValidEntry)
+ {
+ pDirEntry = pHandle->dir.pLastValidEntry;
+ }
+ else
+ {
+ pDirEntry = pDirEntryOrg;
+
+ rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ if (rc == VERR_NO_MORE_FILES)
+ {
+ *pIndex = 0; /* listing completed */
+ break;
+ }
+
+ if ( rc != VINF_SUCCESS
+ && rc != VWRN_NO_DIRENT_INFO)
+ {
+ //AssertFailed();
+ if ( rc == VERR_NO_TRANSLATION
+ || rc == VERR_INVALID_UTF8_ENCODING)
+ continue;
+ break;
+ }
+ }
+
+ cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
+ if (fUtf8)
+ cbNeeded += pDirEntry->cbName + 1;
+ else
+ /* Overestimating, but that's ok */
+ cbNeeded += (pDirEntry->cbName + 1) * 2;
+
+ if (cbBufferOrg < cbNeeded)
+ {
+ /* No room, so save this directory entry, or else it's lost forever */
+ pHandle->dir.pLastValidEntry = pDirEntry;
+
+ if (*pcFiles == 0)
+ {
+ AssertFailed();
+ return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
+ }
+ return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
+ }
+
+#ifdef RT_OS_WINDOWS
+ pDirEntry->Info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
+ pSFDEntry->cucShortName = 0;
+
+ if (fUtf8)
+ {
+ void *src, *dst;
+
+ src = &pDirEntry->szName[0];
+ dst = &pSFDEntry->name.String.utf8[0];
+
+ memcpy(dst, src, pDirEntry->cbName + 1);
+
+ pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
+ pSFDEntry->name.u16Length = pDirEntry->cbName;
+ }
+ else
+ {
+ pSFDEntry->name.String.ucs2[0] = 0;
+ pwszString = pSFDEntry->name.String.ucs2;
+ int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
+ AssertRC(rc2);
+
+#ifdef RT_OS_DARWIN
+/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
+ * The question is simply whether the NFD normalization is actually applied on a (virtual) file
+ * system level in darwin, or just by the user mode application libs. */
+ {
+ // Convert to
+ // Normalization Form C (composed Unicode). We need this because
+ // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
+ // while most other OS', server-side programs usually expect NFC.
+ uint16_t ucs2Length;
+ CFRange rangeCharacters;
+ CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
+
+ ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
+ ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
+ ucs2Length = ::CFStringGetLength(inStr);
+
+ rangeCharacters.location = 0;
+ rangeCharacters.length = ucs2Length;
+ ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
+ pwszString[ucs2Length] = 0x0000; // NULL terminated
+
+ CFRelease(inStr);
+ }
+#endif
+ pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
+ pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
+
+ Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
+ Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
+
+ // adjust cbNeeded (it was overestimated before)
+ cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
+ }
+
+ pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
+ *pcbBuffer += cbNeeded;
+ cbBufferOrg-= cbNeeded;
+
+ *pcFiles += 1;
+
+ /* Free the saved last entry, that we've just returned */
+ if (pHandle->dir.pLastValidEntry)
+ {
+ RTMemFree(pHandle->dir.pLastValidEntry);
+ pHandle->dir.pLastValidEntry = NULL;
+
+ /* And use the newly allocated buffer from now. */
+ pDirEntry = pDirEntryOrg;
+ }
+
+ if (flags & SHFL_LIST_RETURN_ONE)
+ break; /* we're done */
+ }
+ Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
+
+end:
+ if (pDirEntry)
+ RTMemFree(pDirEntry);
+
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
+ * documentation. */
+void testReadLink(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testReadLinkBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pPath == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Build a host full path for the given path, handle file name case issues
+ * (if the guest expects case-insensitive paths but the host is
+ * case-sensitive) and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+ uint32_t cbFullPathRoot = 0;
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /* Convert the slashes in the link target to the guest path separator characters. */
+ char *psz = (char *)pBuffer;
+ while (*psz != '\0')
+ {
+ if (*psz == RTPATH_DELIMITER)
+ *psz = pClient->PathDelimiter;
+ psz++;
+ }
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+
+ return rc;
+}
+
+int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ int rc = VINF_SUCCESS;
+ SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
+ RTFSOBJINFO fileinfo;
+
+
+ AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
+ AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
+
+ /** @todo other options */
+ Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
+
+ *pcbBuffer = 0;
+
+ if (type == SHFL_HF_TYPE_DIR)
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+ }
+ else
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+#ifdef RT_OS_WINDOWS
+ if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
+ pObjInfo->Attr.fMode |= 0111;
+#endif
+ }
+ if (rc == VINF_SUCCESS)
+ {
+ vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ }
+ else
+ AssertFailed();
+
+ return rc;
+}
+
+static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ int rc = VINF_SUCCESS;
+ SHFLFSOBJINFO *pSFDEntry;
+
+ if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
+ || pcbBuffer == 0
+ || pBuffer == 0
+ || *pcbBuffer < sizeof(SHFLFSOBJINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ *pcbBuffer = 0;
+ pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
+
+ Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
+
+ /* Change only the time values that are not zero */
+ if (type == SHFL_HF_TYPE_DIR)
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ rc = RTDirSetTimes(pHandle->dir.Handle,
+ (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
+ );
+ }
+ else
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ rc = RTFileSetTimes(pHandle->file.Handle,
+ (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
+ (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
+ );
+ }
+ if (rc != VINF_SUCCESS)
+ {
+ Log(("RTFileSetTimes failed with %Rrc\n", rc));
+ Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
+ Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
+ Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
+ Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
+ /* temporary hack */
+ rc = VINF_SUCCESS;
+ }
+
+ if (type == SHFL_HF_TYPE_FILE)
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ /* Change file attributes if necessary */
+ if (pSFDEntry->Attr.fMode)
+ {
+ RTFMODE fMode = pSFDEntry->Attr.fMode;
+
+#ifndef RT_OS_WINDOWS
+ /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
+ * able to access this file anymore. Only for guests, which set the UNIX mode.
+ * Also, clear bits which we don't pass through for security reasons. */
+ if (fMode & RTFS_UNIX_MASK)
+ {
+ fMode |= RTFS_UNIX_IRUSR;
+ fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
+ }
+#endif
+
+ rc = RTFileSetMode(pHandle->file.Handle, fMode);
+ if (rc != VINF_SUCCESS)
+ {
+ Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
+ /* silent failure, because this tends to fail with e.g. windows guest & linux host */
+ rc = VINF_SUCCESS;
+ }
+ }
+ }
+ }
+ /** @todo mode for directories */
+
+ if (rc == VINF_SUCCESS)
+ {
+ uint32_t bufsize = sizeof(*pSFDEntry);
+
+ rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
+ if (rc == VINF_SUCCESS)
+ {
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ }
+ else
+ AssertFailed();
+ }
+
+ return rc;
+}
+
+
+/**
+ * Handles SHFL_FN_SET_FILE_SIZE.
+ */
+int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
+{
+ /*
+ * Resolve handle and validate write access.
+ */
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
+ ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
+
+ int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Execute the request.
+ */
+ rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
+ }
+ return rc;
+}
+
+
+static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ SHFLFSOBJINFO *pSFDEntry;
+
+ if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ *pcbBuffer = 0;
+ pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
+
+ if (flags & SHFL_INFO_SIZE)
+ {
+ rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
+ if (rc != VINF_SUCCESS)
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+
+ if (rc == VINF_SUCCESS)
+ {
+ RTFSOBJINFO fileinfo;
+
+ /* Query the new object info and return it */
+ rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+ if (rc == VINF_SUCCESS)
+ {
+#ifdef RT_OS_WINDOWS
+ fileinfo.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ }
+ else
+ AssertFailed();
+ }
+
+ return rc;
+}
+
+int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF2(root, flags);
+ int rc = VINF_SUCCESS;
+ SHFLVOLINFO *pSFDEntry;
+ char *pszFullPath = NULL;
+ union
+ {
+ SHFLSTRING Dummy;
+ uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
+ } Buf;
+
+ if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /** @todo other options */
+ Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
+
+ *pcbBuffer = 0;
+ pSFDEntry = (PSHFLVOLINFO)pBuffer;
+
+ ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
+ Buf.Dummy.String.ucs2[0] = '\0';
+ rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+
+ rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+
+ RTFSPROPERTIES FsProperties;
+ rc = RTFsQueryProperties(pszFullPath, &FsProperties);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+ vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
+
+ *pcbBuffer = sizeof(SHFLVOLINFO);
+ }
+ else AssertFailed();
+
+exit:
+ AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ return rc;
+}
+
+int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ if (pcbBuffer == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (flags & SHFL_INFO_FILE)
+ return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+ if (flags & SHFL_INFO_VOLUME)
+ return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
+
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
+ * documentation. */
+void testFSInfo(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testFSInfoBadParameters(hTest);
+ /* Basic get and set file size test. */
+ testFSInfoQuerySetFMode(hTest);
+ /* Basic get and set dir atime test. */
+ testFSInfoQuerySetDirATime(hTest);
+ /* Basic get and set file atime test. */
+ testFSInfoQuerySetFileATime(hTest);
+ /* Basic set end of file. */
+ testFSInfoQuerySetEndOfFile(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ uint32_t type = vbsfQueryHandleType(pClient, Handle)
+ & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
+
+ if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (flags & SHFL_INFO_FILE)
+ return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+ if (flags & SHFL_INFO_SIZE)
+ return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+// if (flags & SHFL_INFO_VOLUME)
+// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
+ * documentation. */
+void testLock(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testLockBadParameters(hTest);
+ /* Simple file locking and unlocking test. */
+ testLockFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
+{
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ uint32_t fRTLock = 0;
+
+ Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
+ || (flags & SHFL_LOCK_ENTIRE)
+ )
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Lock type */
+ switch(flags & SHFL_LOCK_MODE_MASK)
+ {
+ case SHFL_LOCK_SHARED:
+ fRTLock = RTFILE_LOCK_READ;
+ break;
+
+ case SHFL_LOCK_EXCLUSIVE:
+ fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
+ break;
+
+ default:
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Lock wait type */
+ if (flags & SHFL_LOCK_WAIT)
+ fRTLock |= RTFILE_LOCK_WAIT;
+ else
+ fRTLock |= RTFILE_LOCK_IMMEDIATELY;
+
+#ifdef RT_OS_WINDOWS
+ rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
+ if (rc != VINF_SUCCESS)
+ Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
+#else
+ Log(("vbsfLock: Pretend success handle=%x\n", Handle));
+ rc = VINF_SUCCESS;
+ RT_NOREF2(offset, length);
+#endif
+ return rc;
+}
+
+int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
+{
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+
+ Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
+ || (flags & SHFL_LOCK_ENTIRE)
+ )
+ {
+ return VERR_INVALID_PARAMETER;
+ }
+
+#ifdef RT_OS_WINDOWS
+ rc = RTFileUnlock(pHandle->file.Handle, offset, length);
+ if (rc != VINF_SUCCESS)
+ Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
+#else
+ Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
+ rc = VINF_SUCCESS;
+ RT_NOREF2(offset, length);
+#endif
+
+ return rc;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
+ * documentation. */
+void testRemove(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testRemoveBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Validate input */
+ if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
+ || cbPath == 0
+ || pPath == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Build a host full path for the given path
+ * and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc) || !fWritable)
+ rc = VERR_WRITE_PROTECT;
+
+ if (RT_SUCCESS(rc))
+ {
+ if (flags & SHFL_REMOVE_SYMLINK)
+ rc = RTSymlinkDelete(pszFullPath, 0);
+ else if (flags & SHFL_REMOVE_FILE)
+ rc = RTFileDelete(pszFullPath);
+ else
+ rc = RTDirRemove(pszFullPath);
+ }
+
+#ifndef DEBUG_dmik
+ // VERR_ACCESS_DENIED for example?
+ // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
+#endif
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+ return rc;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
+ * documentation. */
+void testRename(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testRenameBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Validate input */
+ if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
+ || pSrc == 0
+ || pDest == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Build a host full path for the given path
+ * and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPathSrc = NULL;
+ char *pszFullPathDest = NULL;
+
+ rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
+ if (rc != VINF_SUCCESS)
+ return rc;
+
+ rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
+ if (RT_SUCCESS (rc))
+ {
+ Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
+
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc) || !fWritable)
+ rc = VERR_WRITE_PROTECT;
+
+ if (RT_SUCCESS(rc))
+ {
+ if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
+ {
+ rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
+ flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
+ }
+ else if (flags & SHFL_RENAME_FILE)
+ {
+ rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
+ ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
+ }
+ else
+ {
+ /* NT ignores the REPLACE flag and simply return and already exists error. */
+ rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
+ ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
+ }
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPathDest);
+ }
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPathSrc);
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
+ * documentation. */
+void testSymlink(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testSymlinkBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
+{
+ int rc = VINF_SUCCESS;
+
+ char *pszFullNewPath = NULL;
+ char *pszFullOldPath = NULL;
+
+ /* XXX: no support for UCS2 at the moment. */
+ if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ return VERR_NOT_IMPLEMENTED;
+
+ bool fSymlinksCreate;
+ rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
+ AssertRCReturn(rc, rc);
+ if (!fSymlinksCreate)
+ return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
+
+ rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
+ AssertRCReturn(rc, rc);
+
+ /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
+ uint32_t fu32PathFlags = 0;
+ uint32_t fu32Options = 0;
+ rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
+ &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
+ if (RT_FAILURE(rc))
+ {
+ vbsfFreeFullPath(pszFullNewPath);
+ return rc;
+ }
+
+ rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
+ RTSYMLINKTYPE_UNKNOWN, 0);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO info;
+ rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ if (RT_SUCCESS(rc))
+ vbfsCopyFsObjInfoFromIprt(pInfo, &info);
+ }
+
+ vbsfFreeFullPath(pszFullOldPath);
+ vbsfFreeFullPath(pszFullNewPath);
+
+ return rc;
+}
+
+/*
+ * Clean up our mess by freeing all handles that are still valid.
+ *
+ */
+int vbsfDisconnect(SHFLCLIENTDATA *pClient)
+{
+ for (int i = 0; i < SHFLHANDLE_MAX; ++i)
+ {
+ SHFLFILEHANDLE *pHandle = NULL;
+ SHFLHANDLE Handle = (SHFLHANDLE)i;
+
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
+ {
+ case SHFL_HF_TYPE_DIR:
+ {
+ pHandle = vbsfQueryDirHandle(pClient, Handle);
+ break;
+ }
+ case SHFL_HF_TYPE_FILE:
+ {
+ pHandle = vbsfQueryFileHandle(pClient, Handle);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (pHandle)
+ {
+ LogFunc(("Opened handle 0x%08x\n", i));
+ vbsfClose(pClient, pHandle->root, Handle);
+ }
+ }
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
+ if (pClient->acMappings[i])
+ {
+ uint16_t cMappings = pClient->acMappings[i];
+ while (cMappings-- > 0)
+ vbsfUnmapFolder(pClient, i);
+ }
+
+ return VINF_SUCCESS;
+}