Index: trunk/src/VBox/HostServices/SharedFolders/mappings.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedFolders/mappings.cpp	(revision 75380)
+++ trunk/src/VBox/HostServices/SharedFolders/mappings.cpp	(revision 75407)
@@ -23,4 +23,5 @@
 #include <iprt/alloc.h>
 #include <iprt/assert.h>
+#include <iprt/list.h>
 #include <iprt/path.h>
 #include <iprt/string.h>
@@ -30,10 +31,45 @@
 #endif
 
-/* Shared folders order in the saved state and in the FolderMapping can differ.
+extern PVBOXHGCMSVCHELPERS g_pHelpers; /* service.cpp */
+
+
+/* Shared folders order in the saved state and in the g_FolderMapping can differ.
  * So a translation array of root handle is needed.
  */
 
-static MAPPING FolderMapping[SHFL_MAX_MAPPINGS];
-static SHFLROOT aIndexFromRoot[SHFL_MAX_MAPPINGS];
+static MAPPING g_FolderMapping[SHFL_MAX_MAPPINGS];
+static SHFLROOT g_aIndexFromRoot[SHFL_MAX_MAPPINGS];
+/**< Array running parallel to g_aIndexFromRoot and which entries are increased
+ * as an root handle is added or removed.
+ *
+ * This helps the guest figuring out that a mapping may have been reconfigured
+ * or that saved state has been restored.  Entry reuse is very likely given that
+ * vbsfRootHandleAdd() always starts searching at the start for an unused entry.
+ */
+static uint32_t g_auRootHandleVersions[SHFL_MAX_MAPPINGS];
+/** Version number that is increased for every change made.
+ * This is used by the automount guest service to wait for changes.
+ * @note This does not need saving, the guest should be woken up and refresh
+ *       its sate when restored. */
+static uint32_t volatile g_uFolderMappingsVersion = 0;
+
+
+/** For recording async vbsfMappingsWaitForChanges calls. */
+typedef struct SHFLMAPPINGSWAIT
+{
+    RTLISTNODE          ListEntry;  /**< List entry. */
+    PSHFLCLIENTDATA     pClient;    /**< The client that's waiting. */
+    VBOXHGCMCALLHANDLE  hCall;      /**< The call handle to signal completion with. */
+    PVBOXHGCMSVCPARM    pParm;      /**< The 32-bit unsigned parameter to stuff g_uFolderMappingsVersion into. */
+} SHFLMAPPINGSWAIT;
+/** Pointer to async mappings change wait. */
+typedef SHFLMAPPINGSWAIT *PSHFLMAPPINGSWAIT;
+/** List head for clients waiting on mapping changes (SHFLMAPPINGSWAIT). */
+static RTLISTANCHOR g_MappingsChangeWaiters;
+/** Number of clients waiting on mapping changes.
+ * We use this to limit the number of waiting calls the clients can make.  */
+static uint32_t     g_cMappingChangeWaiters = 0;
+static void vbsfMappingsWakeupAllWaiters(void);
+
 
 void vbsfMappingInit(void)
@@ -41,16 +77,18 @@
     unsigned root;
 
-    for (root = 0; root < RT_ELEMENTS(aIndexFromRoot); root++)
-    {
-        aIndexFromRoot[root] = SHFL_ROOT_NIL;
-    }
-}
-
-int vbsfMappingLoaded(const PMAPPING pLoadedMapping, SHFLROOT root)
+    for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+    {
+        g_aIndexFromRoot[root] = SHFL_ROOT_NIL;
+    }
+
+    RTListInit(&g_MappingsChangeWaiters);
+}
+
+int vbsfMappingLoaded(const MAPPING *pLoadedMapping, SHFLROOT root)
 {
     /* Mapping loaded from the saved state with the index. Which means
      * the guest uses the iMapping as root handle for this folder.
-     * Check whether there is the same mapping in FolderMapping and
-     * update the aIndexFromRoot.
+     * Check whether there is the same mapping in g_FolderMapping and
+     * update the g_aIndexFromRoot.
      *
      * Also update the mapping properties, which were lost: cMappings.
@@ -62,7 +100,7 @@
 
     SHFLROOT i;
-    for (i = 0; i < RT_ELEMENTS(FolderMapping); i++)
-    {
-        MAPPING *pMapping = &FolderMapping[i];
+    for (i = 0; i < RT_ELEMENTS(g_FolderMapping); i++)
+    {
+        MAPPING *pMapping = &g_FolderMapping[i];
 
         /* Equal? */
@@ -71,11 +109,20 @@
             && memcmp(pLoadedMapping->pMapName, pMapping->pMapName, ShflStringSizeOfBuffer(pMapping->pMapName)) == 0)
         {
-            /* Actual index is i. */
-            aIndexFromRoot[root] = i;
-
-            /* Update the mapping properties. */
-            pMapping->cMappings = pLoadedMapping->cMappings;
-
-            return VINF_SUCCESS;
+            if (!pMapping->fLoadedRootId)
+            {
+                pMapping->fLoadedRootId = true;
+                Log(("vbsfMappingLoaded: root=%u i=%u (was %u) (%ls)\n",
+                     root, i, g_aIndexFromRoot[root], pLoadedMapping->pMapName->String.utf16));
+
+                /* Actual index is i. */
+                /** @todo This will not work with global shared folders, as these can change
+                 *        while state is saved and these blind assignments may hid new ones.  */
+                g_aIndexFromRoot[root] = i;
+
+                /* Update the mapping properties. */
+                pMapping->cMappings = pLoadedMapping->cMappings;
+
+                return VINF_SUCCESS;
+            }
         }
     }
@@ -93,12 +140,12 @@
 MAPPING *vbsfMappingGetByRoot(SHFLROOT root)
 {
-    if (root < RT_ELEMENTS(aIndexFromRoot))
-    {
-        SHFLROOT iMapping = aIndexFromRoot[root];
+    if (root < RT_ELEMENTS(g_aIndexFromRoot))
+    {
+        SHFLROOT iMapping = g_aIndexFromRoot[root];
 
         if (   iMapping != SHFL_ROOT_NIL
-            && iMapping < RT_ELEMENTS(FolderMapping))
-        {
-            return &FolderMapping[iMapping];
+            && iMapping < RT_ELEMENTS(g_FolderMapping))
+        {
+            return &g_FolderMapping[iMapping];
         }
     }
@@ -111,7 +158,7 @@
     unsigned root;
 
-    for (root = 0; root < RT_ELEMENTS(aIndexFromRoot); root++)
-    {
-        if (iMapping == aIndexFromRoot[root])
+    for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+    {
+        if (iMapping == g_aIndexFromRoot[root])
         {
             return root;
@@ -122,13 +169,12 @@
 }
 
-static MAPPING *vbsfMappingGetByName (PRTUTF16 pwszName, SHFLROOT *pRoot)
-{
-    unsigned i;
-
-    for (i=0; i<SHFL_MAX_MAPPINGS; i++)
-    {
-        if (FolderMapping[i].fValid == true)
-        {
-            if (!RTUtf16LocaleICmp(FolderMapping[i].pMapName->String.ucs2, pwszName))
+static MAPPING *vbsfMappingGetByName(PRTUTF16 pwszName, SHFLROOT *pRoot)
+{
+    for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++)
+    {
+        if (   g_FolderMapping[i].fValid
+            && !g_FolderMapping[i].fPlaceholder) /* Don't allow mapping placeholders. */
+        {
+            if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pwszName))
             {
                 SHFLROOT root = vbsfMappingGetRootFromIndex(i);
@@ -140,14 +186,10 @@
                         *pRoot = root;
                     }
-                    return &FolderMapping[i];
+                    return &g_FolderMapping[i];
                 }
-                else
-                {
-                    AssertFailed();
-                }
+                AssertFailed();
             }
         }
     }
-
     return NULL;
 }
@@ -157,9 +199,10 @@
     unsigned root;
 
-    for (root = 0; root < RT_ELEMENTS(aIndexFromRoot); root++)
-    {
-        if (aIndexFromRoot[root] == SHFL_ROOT_NIL)
-        {
-            aIndexFromRoot[root] = iMapping;
+    for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+    {
+        if (g_aIndexFromRoot[root] == SHFL_ROOT_NIL)
+        {
+            g_aIndexFromRoot[root] = iMapping;
+            g_auRootHandleVersions[root] += 1;
             return;
         }
@@ -173,10 +216,15 @@
     unsigned root;
 
-    for (root = 0; root < RT_ELEMENTS(aIndexFromRoot); root++)
-    {
-        if (aIndexFromRoot[root] == iMapping)
-        {
-            aIndexFromRoot[root] = SHFL_ROOT_NIL;
-            return;
+    for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+    {
+        if (g_aIndexFromRoot[root] == iMapping)
+        {
+            g_aIndexFromRoot[root] = SHFL_ROOT_NIL;
+            g_auRootHandleVersions[root] += 1;
+            Log(("vbsfRootHandleRemove: Removed root=%u (iMapping=%u)\n", root, iMapping));
+
+            /* Note! Do not stop here as g_aIndexFromRoot may (at least it could
+                     prior to the introduction of fLoadedRootId) contain
+                     duplicates after restoring save state. */
         }
     }
@@ -209,20 +257,26 @@
     Log(("vbsfMappingsAdd %ls\n", pMapName->String.ucs2));
 
-    /* check for duplicates */
-    for (i=0; i<SHFL_MAX_MAPPINGS; i++)
-    {
-        if (FolderMapping[i].fValid == true)
-        {
-            if (!RTUtf16LocaleICmp(FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
+    /* Check for duplicates, ignoring placeholders to give the GUI to change stuff at runtime. */
+    /** @todo bird: Not entirely sure about ignoring placeholders, but you cannot
+     *        trigger auto-umounting without ignoring them. */
+    if (!fPlaceholder)
+    {
+        for (i = 0; i < SHFL_MAX_MAPPINGS; i++)
+        {
+            if (   g_FolderMapping[i].fValid
+                && !g_FolderMapping[i].fPlaceholder)
             {
-                AssertMsgFailed(("vbsfMappingsAdd: %ls mapping already exists!!\n", pMapName->String.ucs2));
-                return VERR_ALREADY_EXISTS;
+                if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
+                {
+                    AssertMsgFailed(("vbsfMappingsAdd: %ls mapping already exists!!\n", pMapName->String.ucs2));
+                    return VERR_ALREADY_EXISTS;
+                }
             }
         }
     }
 
-    for (i=0; i<SHFL_MAX_MAPPINGS; i++)
-    {
-        if (FolderMapping[i].fValid == false)
+    for (i = 0; i < SHFL_MAX_MAPPINGS; i++)
+    {
+        if (g_FolderMapping[i].fValid == false)
         {
             /* Make sure the folder name is an absolute path, otherwise we're
@@ -232,32 +286,34 @@
             AssertRCReturn(rc, rc);
 
-            FolderMapping[i].pszFolderName   = RTStrDup(szAbsFolderName);
-            FolderMapping[i].pMapName        = ShflStringDup(pMapName);
-            FolderMapping[i].pAutoMountPoint = ShflStringDup(pAutoMountPoint);
-            if (   !FolderMapping[i].pszFolderName
-                || !FolderMapping[i].pMapName
-                || !FolderMapping[i].pAutoMountPoint)
+            g_FolderMapping[i].pszFolderName   = RTStrDup(szAbsFolderName);
+            g_FolderMapping[i].pMapName        = ShflStringDup(pMapName);
+            g_FolderMapping[i].pAutoMountPoint = ShflStringDup(pAutoMountPoint);
+            if (   !g_FolderMapping[i].pszFolderName
+                || !g_FolderMapping[i].pMapName
+                || !g_FolderMapping[i].pAutoMountPoint)
             {
-                RTStrFree(FolderMapping[i].pszFolderName);
-                RTMemFree(FolderMapping[i].pMapName);
-                RTMemFree(FolderMapping[i].pAutoMountPoint);
+                RTStrFree(g_FolderMapping[i].pszFolderName);
+                RTMemFree(g_FolderMapping[i].pMapName);
+                RTMemFree(g_FolderMapping[i].pAutoMountPoint);
                 return VERR_NO_MEMORY;
             }
 
-            FolderMapping[i].fValid          = true;
-            FolderMapping[i].cMappings       = 0;
-            FolderMapping[i].fWritable       = fWritable;
-            FolderMapping[i].fAutoMount      = fAutoMount;
-            FolderMapping[i].fSymlinksCreate = fSymlinksCreate;
-            FolderMapping[i].fMissing        = fMissing;
-            FolderMapping[i].fPlaceholder    = fPlaceholder;
+            g_FolderMapping[i].fValid          = true;
+            g_FolderMapping[i].cMappings       = 0;
+            g_FolderMapping[i].fWritable       = fWritable;
+            g_FolderMapping[i].fAutoMount      = fAutoMount;
+            g_FolderMapping[i].fSymlinksCreate = fSymlinksCreate;
+            g_FolderMapping[i].fMissing        = fMissing;
+            g_FolderMapping[i].fPlaceholder    = fPlaceholder;
+            g_FolderMapping[i].fLoadedRootId   = false;
 
             /* Check if the host file system is case sensitive */
             RTFSPROPERTIES prop;
             prop.fCaseSensitive = false; /* Shut up MSC. */
-            rc = RTFsQueryProperties(FolderMapping[i].pszFolderName, &prop);
+            rc = RTFsQueryProperties(g_FolderMapping[i].pszFolderName, &prop);
             AssertRC(rc);
-            FolderMapping[i].fHostCaseSensitive = RT_SUCCESS(rc) ? prop.fCaseSensitive : false;
+            g_FolderMapping[i].fHostCaseSensitive = RT_SUCCESS(rc) ? prop.fCaseSensitive : false;
             vbsfRootHandleAdd(i);
+            vbsfMappingsWakeupAllWaiters();
             break;
         }
@@ -269,5 +325,6 @@
     }
 
-    Log(("vbsfMappingsAdd: added mapping %s to %ls\n", pszFolderName, pMapName->String.ucs2));
+    Log(("vbsfMappingsAdd: added mapping %s to %ls (slot %u, root %u)\n",
+         pszFolderName, pMapName->String.ucs2, i, vbsfMappingGetRootFromIndex(i)));
     return VINF_SUCCESS;
 }
@@ -285,42 +342,52 @@
 int vbsfMappingsRemove(PSHFLSTRING pMapName)
 {
-    unsigned i;
-
     Assert(pMapName);
-
     Log(("vbsfMappingsRemove %ls\n", pMapName->String.ucs2));
-    for (i=0; i<SHFL_MAX_MAPPINGS; i++)
-    {
-        if (FolderMapping[i].fValid == true)
-        {
-            if (!RTUtf16LocaleICmp(FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
+
+    /*
+     * We must iterate thru the whole table as may have 0+ placeholder entries
+     * and 0-1 regular entries with the same name.  Also, it is good to kick
+     * the guest automounter into action wrt to evicting placeholders.
+     */
+    int rc = VERR_FILE_NOT_FOUND;
+    for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++)
+    {
+        if (g_FolderMapping[i].fValid == true)
+        {
+            if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
             {
-                if (FolderMapping[i].cMappings != 0)
+                if (g_FolderMapping[i].cMappings != 0)
                 {
-                    LogRel2(("SharedFolders: removing '%ls' -> '%s', which is still used by the guest\n",
-                             pMapName->String.ucs2, FolderMapping[i].pszFolderName));
-                    FolderMapping[i].fMissing = true;
-                    FolderMapping[i].fPlaceholder = true;
-                    return VINF_PERMISSION_DENIED;
+                    LogRel2(("SharedFolders: removing '%ls' -> '%s'%s, which is still used by the guest\n", pMapName->String.ucs2,
+                             g_FolderMapping[i].pszFolderName, g_FolderMapping[i].fPlaceholder ? " (again)" : ""));
+                    g_FolderMapping[i].fMissing = true;
+                    g_FolderMapping[i].fPlaceholder = true;
+                    vbsfMappingsWakeupAllWaiters();
+                    rc = VINF_PERMISSION_DENIED;
                 }
-
-                /* pMapName can be the same as FolderMapping[i].pMapName,
-                 * log it before deallocating the memory.
-                 */
-                Log(("vbsfMappingsRemove: mapping %ls removed\n", pMapName->String.ucs2));
-
-                RTStrFree(FolderMapping[i].pszFolderName);
-                RTMemFree(FolderMapping[i].pMapName);
-                FolderMapping[i].pszFolderName = NULL;
-                FolderMapping[i].pMapName      = NULL;
-                FolderMapping[i].fValid        = false;
-                vbsfRootHandleRemove(i);
-                return VINF_SUCCESS;
+                else
+                {
+                    /* pMapName can be the same as g_FolderMapping[i].pMapName when
+                     * called from vbsfUnmapFolder, log it before deallocating the memory. */
+                    Log(("vbsfMappingsRemove: mapping %ls removed\n", pMapName->String.ucs2));
+                    bool fSame = g_FolderMapping[i].pMapName == pMapName;
+
+                    RTStrFree(g_FolderMapping[i].pszFolderName);
+                    RTMemFree(g_FolderMapping[i].pMapName);
+                    g_FolderMapping[i].pszFolderName = NULL;
+                    g_FolderMapping[i].pMapName      = NULL;
+                    g_FolderMapping[i].fValid        = false;
+                    vbsfRootHandleRemove(i);
+                    vbsfMappingsWakeupAllWaiters();
+                    if (rc == VERR_FILE_NOT_FOUND)
+                        rc = VINF_SUCCESS;
+                    if (fSame)
+                        break;
+                }
             }
         }
     }
 
-    AssertMsgFailed(("vbsfMappingsRemove: mapping %ls not found!!!!\n", pMapName->String.ucs2));
-    return VERR_FILE_NOT_FOUND;
+    return rc;
 }
 
@@ -378,37 +445,30 @@
 #endif
 /**
- * Note: If pMappings / *pcMappings is smaller than the actual amount of mappings
- *       that *could* have been returned *pcMappings contains the required buffer size
- *       so that the caller can retry the operation if wanted.
- */
-int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, PSHFLMAPPING pMappings, uint32_t *pcMappings)
-{
-    int rc = VINF_SUCCESS;
-
-    uint32_t cMappings = 0; /* Will contain actual valid mappings. */
-    uint32_t idx = 0;       /* Current index in mappings buffer. */
-
+ * @note If pMappings / *pcMappings is smaller than the actual amount of
+ *       mappings that *could* have been returned *pcMappings contains the
+ *       required buffer size so that the caller can retry the operation if
+ *       wanted.
+ */
+int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings)
+{
     LogFlow(("vbsfMappingsQuery: pClient = %p, pMappings = %p, pcMappings = %p, *pcMappings = %d\n",
              pClient, pMappings, pcMappings, *pcMappings));
 
+    uint32_t const cMaxMappings = *pcMappings;
+    uint32_t       idx          = 0;
     for (uint32_t i = 0; i < SHFL_MAX_MAPPINGS; i++)
     {
         MAPPING *pFolderMapping = vbsfMappingGetByRoot(i);
         if (   pFolderMapping != NULL
-            && pFolderMapping->fValid == true)
-        {
-            if (idx < *pcMappings)
+            && pFolderMapping->fValid
+            && (   !fOnlyAutoMounts
+                || (pFolderMapping->fAutoMount && !pFolderMapping->fPlaceholder)) )
+        {
+            if (idx < cMaxMappings)
             {
-                /* Skip mappings which are not marked for auto-mounting if
-                 * the SHFL_MF_AUTOMOUNT flag ist set. */
-                if (   (pClient->fu32Flags & SHFL_MF_AUTOMOUNT)
-                    && !pFolderMapping->fAutoMount)
-                    continue;
-
                 pMappings[idx].u32Status = SHFL_MS_NEW;
-                pMappings[idx].root = i;
-                idx++;
+                pMappings[idx].root      = i;
             }
-            cMappings++;
+            idx++;
         }
     }
@@ -416,8 +476,14 @@
     /* Return actual number of mappings, regardless whether the handed in
      * mapping buffer was big enough. */
-    *pcMappings = cMappings;
-
-    LogFlow(("vbsfMappingsQuery: return rc = %Rrc\n", rc));
-    return rc;
+    /** @todo r=bird: This is non-standard interface behaviour.  We return
+     *        VERR_BUFFER_OVERFLOW or at least a VINF_BUFFER_OVERFLOW here.
+     *
+     *        Guess this goes well along with ORing SHFL_MF_AUTOMOUNT into
+     *        pClient->fu32Flags rather than passing it as fOnlyAutoMounts...
+     *        Not amused by this. */
+    *pcMappings = idx;
+
+    LogFlow(("vbsfMappingsQuery: returns VINF_SUCCESS (idx=%u, cMaxMappings=%u)\n", idx, cMaxMappings));
+    return VINF_SUCCESS;
 }
 
@@ -437,42 +503,39 @@
 int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString)
 {
-    int rc = VINF_SUCCESS;
-
-    LogFlow(("vbsfMappingsQuery: pClient = %p, root = %d, *pString = %p\n",
-             pClient, root, pString));
-
+    LogFlow(("vbsfMappingsQuery: pClient = %p, root = %d, *pString = %p\n", pClient, root, pString));
+
+    int rc;
     MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
-    if (pFolderMapping == NULL)
-    {
-        return VERR_INVALID_PARAMETER;
-    }
-
-    if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
-    {
-        /* Not implemented. */
-        AssertFailed();
-        return VERR_INVALID_PARAMETER;
-    }
-
-    if (pFolderMapping->fValid == true)
-    {
-        if (pString->u16Size < pFolderMapping->pMapName->u16Size)
-        {
-            Log(("vbsfMappingsQuery: passed string too short (%d < %d bytes)!\n",
-                pString->u16Size,  pFolderMapping->pMapName->u16Size));
-            rc = VERR_INVALID_PARAMETER;
+    if (pFolderMapping)
+    {
+        if (pFolderMapping->fValid)
+        {
+            if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+                rc = ShflStringCopyUtf16BufAsUtf8(pString, pFolderMapping->pMapName);
+            else
+            {
+                /* Not using ShlfStringCopy here as behaviour shouldn't change... */
+                if (pString->u16Size < pFolderMapping->pMapName->u16Size)
+                {
+                    Log(("vbsfMappingsQuery: passed string too short (%d < %d bytes)!\n",
+                        pString->u16Size,  pFolderMapping->pMapName->u16Size));
+                    rc = VERR_INVALID_PARAMETER;
+                }
+                else
+                {
+                    pString->u16Length = pFolderMapping->pMapName->u16Length;
+                    memcpy(pString->String.ucs2, pFolderMapping->pMapName->String.ucs2,
+                           pFolderMapping->pMapName->u16Size);
+                    rc = VINF_SUCCESS;
+                }
+            }
         }
         else
-        {
-            pString->u16Length = pFolderMapping->pMapName->u16Length;
-            memcpy(pString->String.ucs2, pFolderMapping->pMapName->String.ucs2,
-                   pFolderMapping->pMapName->u16Size);
-        }
+            rc = VERR_FILE_NOT_FOUND;
     }
     else
-        rc = VERR_FILE_NOT_FOUND;
+        rc = VERR_INVALID_PARAMETER;
 
     LogFlow(("vbsfMappingsQuery:Name return rc = %Rrc\n", rc));
-
     return rc;
 }
@@ -540,4 +603,62 @@
     return rc;
 }
+
+/**
+ * Implements SHFL_FN_QUERY_MAP_INFO.
+ * @since VBox 6.0
+ */
+int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf,
+                          uint64_t *pfFlags, uint32_t *puVersion)
+{
+    LogFlow(("vbsfMappingsQueryInfo: pClient=%p root=%d\n", pClient, root));
+
+    /* Resolve the root handle. */
+    int rc;
+    PMAPPING pFolderMapping = vbsfMappingGetByRoot(root);
+    if (pFolderMapping)
+    {
+        if (pFolderMapping->fValid)
+        {
+            /*
+             * Produce the output.
+             */
+            *puVersion = g_auRootHandleVersions[root];
+
+            *pfFlags = 0;
+            if (pFolderMapping->fWritable)
+                *pfFlags |= SHFL_MIF_WRITABLE;
+            if (pFolderMapping->fAutoMount)
+                *pfFlags |= SHFL_MIF_AUTO_MOUNT;
+            if (pFolderMapping->fHostCaseSensitive)
+                *pfFlags |= SHFL_MIF_HOST_ICASE;
+            if (pFolderMapping->fGuestCaseSensitive)
+                *pfFlags |= SHFL_MIF_GUEST_ICASE;
+            if (pFolderMapping->fSymlinksCreate)
+                *pfFlags |= SHFL_MIF_SYMLINK_CREATION;
+
+            int rc2;
+            if (pClient->fu32Flags & SHFL_CF_UTF8)
+            {
+                rc = ShflStringCopyUtf16BufAsUtf8(pNameBuf, pFolderMapping->pMapName);
+                rc2 = ShflStringCopyUtf16BufAsUtf8(pMntPtBuf, pFolderMapping->pAutoMountPoint);
+            }
+            else
+            {
+                rc = ShflStringCopy(pNameBuf, pFolderMapping->pMapName, sizeof(RTUTF16));
+                rc2 = ShflStringCopy(pMntPtBuf, pFolderMapping->pAutoMountPoint, sizeof(RTUTF16));
+            }
+            if (RT_SUCCESS(rc))
+                rc = rc2;
+        }
+        else
+            rc = VERR_FILE_NOT_FOUND;
+    }
+    else
+        rc = VERR_INVALID_PARAMETER;
+    LogFlow(("vbsfMappingsQueryInfo: returns %Rrc\n", rc));
+    return rc;
+}
+
+
 
 #ifdef UNITTEST
@@ -662,2 +783,117 @@
     return rc;
 }
+
+/**
+ * SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES implementation.
+ *
+ * @returns VBox status code.
+ * @retval  VINF_SUCCESS on change.
+ * @retval  VINF_TRY_AGAIN on resume.
+ * @retval  VINF_HGCM_ASYNC_EXECUTE if waiting.
+ * @retval  VERR_CANCELLED if cancelled.
+ * @retval  VERR_OUT_OF_RESOURCES if there are too many pending waits.
+ *
+ * @param   pClient     The calling client.
+ * @param   hCall       The call handle.
+ * @param   pParm       The parameter (32-bit).
+ * @param   fRestored   Set if this is a call restored & resubmitted from saved
+ *                      state.
+ * @since   VBox 6.0
+ */
+int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored)
+{
+    /*
+     * Return immediately if the fodler mappings have changed since last call
+     * or if we got restored from saved state (adding of global folders, etc).
+     */
+    uint32_t uCurVersion = g_uFolderMappingsVersion;
+    if (   pParm->u.uint32 != uCurVersion
+        || fRestored
+        || (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT) )
+    {
+        int rc = VINF_SUCCESS;
+        if (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT)
+        {
+            pClient->fu32Flags &= ~SHFL_CF_CANCEL_NEXT_WAIT;
+            rc = VERR_CANCELLED;
+        }
+        else if (fRestored)
+        {
+            rc = VINF_TRY_AGAIN;
+            if (pParm->u.uint32 == uCurVersion)
+                uCurVersion = uCurVersion != UINT32_C(0x55555555) ? UINT32_C(0x55555555) : UINT32_C(0x99999999);
+        }
+        Log(("vbsfMappingsWaitForChanges: Version %#x -> %#x, returning %Rrc immediately.\n", pParm->u.uint32, uCurVersion, rc));
+        pParm->u.uint32 = uCurVersion;
+        return rc;
+    }
+
+    /*
+     * Setup a wait if we can.
+     */
+    if (g_cMappingChangeWaiters < 64)
+    {
+        PSHFLMAPPINGSWAIT pWait = (PSHFLMAPPINGSWAIT)RTMemAlloc(sizeof(*pWait));
+        if (pWait)
+        {
+            pWait->pClient = pClient;
+            pWait->hCall   = hCall;
+            pWait->pParm   = pParm;
+
+            RTListAppend(&g_MappingsChangeWaiters, &pWait->ListEntry);
+            g_cMappingChangeWaiters += 1;
+            return VINF_HGCM_ASYNC_EXECUTE;
+        }
+        return VERR_NO_MEMORY;
+    }
+    LogRelMax(32, ("vbsfMappingsWaitForChanges: Too many threads waiting for changes!\n"));
+    return VERR_OUT_OF_RESOURCES;
+}
+
+/**
+ * SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS implementation.
+ *
+ * @returns VINF_SUCCESS
+ * @param   pClient     The calling client to cancel all waits for.
+ * @since   VBox 6.0
+ */
+int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient)
+{
+    uint32_t const uCurVersion = g_uFolderMappingsVersion;
+
+    PSHFLMAPPINGSWAIT pCur, pNext;
+    RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry)
+    {
+        if (pCur->pClient == pClient)
+        {
+            RTListNodeRemove(&pCur->ListEntry);
+            pCur->pParm->u.uint32 = uCurVersion;
+            g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED);
+            RTMemFree(pCur);
+        }
+    }
+
+    /* Set a flag to make sure the next SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES doesn't block.
+       This should help deal with races between this call and a thread about to do a wait. */
+    pClient->fu32Flags |= SHFL_CF_CANCEL_NEXT_WAIT;
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Wakes up all clients waiting on
+ */
+static void vbsfMappingsWakeupAllWaiters(void)
+{
+    uint32_t const uCurVersion = ++g_uFolderMappingsVersion;
+
+    PSHFLMAPPINGSWAIT pCur, pNext;
+    RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry)
+    {
+        RTListNodeRemove(&pCur->ListEntry);
+        pCur->pParm->u.uint32 = uCurVersion;
+        g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED);
+        RTMemFree(pCur);
+    }
+}
+
Index: trunk/src/VBox/HostServices/SharedFolders/mappings.h
===================================================================
--- trunk/src/VBox/HostServices/SharedFolders/mappings.h	(revision 75380)
+++ trunk/src/VBox/HostServices/SharedFolders/mappings.h	(revision 75407)
@@ -38,4 +38,5 @@
     bool        fPlaceholder;           /**< Mapping does not exist in the VM settings but the guest
                                              still has. fMissing is always true for this mapping. */
+    bool        fLoadedRootId;          /**< Set if vbsfMappingLoaded has found this mapping already. */
 } MAPPING;
 /** Pointer to a MAPPING structure. */
@@ -50,13 +51,18 @@
 int vbsfMappingsRemove(PSHFLSTRING pMapName);
 
-int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, PSHFLMAPPING pMappings, uint32_t *pcMappings);
+int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings);
 int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString);
 int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable);
 int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount);
 int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate);
+int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf,
+                          uint64_t *pfFlags, uint32_t *puVersion);
 
 int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName, RTUTF16 delimiter,
                   bool fCaseSensitive, SHFLROOT *pRoot);
 int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root);
+
+int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored);
+int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient);
 
 const char* vbsfMappingsQueryHostRoot(SHFLROOT root);
@@ -65,5 +71,5 @@
 bool vbsfIsHostMappingCaseSensitive(SHFLROOT root);
 
-int vbsfMappingLoaded(const PMAPPING pLoadedMapping, SHFLROOT root);
+int vbsfMappingLoaded(MAPPING const *pLoadedMapping, SHFLROOT root);
 PMAPPING vbsfMappingGetByRoot(SHFLROOT root);
 
Index: trunk/src/VBox/HostServices/SharedFolders/service.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedFolders/service.cpp	(revision 75380)
+++ trunk/src/VBox/HostServices/SharedFolders/service.cpp	(revision 75407)
@@ -26,4 +26,5 @@
 #include <iprt/string.h>
 #include <iprt/assert.h>
+#include <VBox/AssertGuest.h>
 #include <VBox/vmm/ssm.h>
 #include <VBox/vmm/pdmifs.h>
@@ -237,5 +238,5 @@
                 rc = SSMR3GetMem(pSSM, pFolderName, cb);
                 AssertRCReturn(rc, rc);
-                AssertReturn(pFolderName->u16Length < cb && pFolderName->u16Size < pFolderName->u16Length,
+                AssertReturn(pFolderName->u16Size < cb && pFolderName->u16Length < pFolderName->u16Size,
                              SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
                                                "Bad folder name string: %#x/%#x cb=%#x\n",
@@ -267,5 +268,5 @@
             rc = SSMR3GetMem(pSSM, pMapName, cb);
             AssertRCReturn(rc, rc);
-            AssertReturn(pMapName->u16Length < cb && pMapName->u16Size < pMapName->u16Length,
+            AssertReturn(pMapName->u16Size < cb && pMapName->u16Length < pMapName->u16Size,
                          SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
                                            "Bad map name string: %#x/%#x cb=%#x\n",
@@ -293,5 +294,5 @@
                 rc = SSMR3GetMem(pSSM, pAutoMountPoint, cb);
                 AssertRCReturn(rc, rc);
-                AssertReturn(pAutoMountPoint->u16Length < cb && pAutoMountPoint->u16Size < pAutoMountPoint->u16Length,
+                AssertReturn(pAutoMountPoint->u16Size < cb && pAutoMountPoint->u16Length < pAutoMountPoint->u16Size,
                              SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
                                                "Bad auto mount point string: %#x/%#x cb=%#x\n",
@@ -331,5 +332,6 @@
 }
 
-static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
+                                   void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
 {
     RT_NOREF1(u32ClientID);
@@ -388,8 +390,11 @@
                     if (fu32Flags & SHFL_MF_UTF8)
                         pClient->fu32Flags |= SHFL_CF_UTF8;
-                    if (fu32Flags & SHFL_MF_AUTOMOUNT)
-                        pClient->fu32Flags |= SHFL_MF_AUTOMOUNT;
-
-                    rc = vbsfMappingsQuery(pClient, pMappings, &cMappings);
+                    /// @todo r=bird: Someone please explain this amusing code (r63916):
+                    //if (fu32Flags & SHFL_MF_AUTOMOUNT)
+                    //    pClient->fu32Flags |= SHFL_MF_AUTOMOUNT;
+                    //
+                    //rc = vbsfMappingsQuery(pClient, pMappings, &cMappings);
+
+                    rc = vbsfMappingsQuery(pClient, RT_BOOL(fu32Flags & SHFL_MF_AUTOMOUNT), pMappings, &cMappings);
                     if (RT_SUCCESS(rc))
                     {
@@ -1295,4 +1300,56 @@
         }
 
+        case SHFL_FN_QUERY_MAP_INFO:
+        {
+            Log(("SharedFolders host service: svnCall: SHFL_FN_QUERY_MAP_INFO\n"));
+
+            /* Validate input: */
+            rc = VERR_INVALID_PARAMETER;
+            ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_QUERY_MAP_INFO);
+            ASSERT_GUEST_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT); /* root */
+            ASSERT_GUEST_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR);   /* name */
+            PSHFLSTRING  pNameBuf  = (PSHFLSTRING)paParms[1].u.pointer.addr;
+            ASSERT_GUEST_BREAK(ShflStringIsValidOut(pNameBuf, paParms[1].u.pointer.size));
+            ASSERT_GUEST_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_PTR);   /* mountPoint */
+            PSHFLSTRING  pMntPtBuf = (PSHFLSTRING)paParms[2].u.pointer.addr;
+            ASSERT_GUEST_BREAK(ShflStringIsValidOut(pMntPtBuf, paParms[2].u.pointer.size));
+            ASSERT_GUEST_BREAK(paParms[3].type == VBOX_HGCM_SVC_PARM_64BIT); /* flags */
+            ASSERT_GUEST_BREAK(!(paParms[3].u.uint64 & ~(SHFL_MIQF_DRIVE_LETTER | SHFL_MIQF_PATH))); /* flags */
+            ASSERT_GUEST_BREAK(paParms[4].type == VBOX_HGCM_SVC_PARM_32BIT); /* version */
+
+            /* Execute the function: */
+            rc = vbsfMappingsQueryInfo(pClient, paParms[0].u.uint32, pNameBuf, pMntPtBuf,
+                                       &paParms[3].u.uint64, &paParms[4].u.uint32);
+            break;
+        }
+
+        case SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES:
+        {
+            Log(("SharedFolders host service: svnCall: SHFL_FN_WAIT_FOR_CHANGES\n"));
+
+            /* Validate input: */
+            rc = VERR_INVALID_PARAMETER;
+            ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_WAIT_FOR_MAPPINGS_CHANGES);
+            ASSERT_GUEST_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT); /* uFolderMappingsVersion */
+
+            /* Execute the function: */
+            rc = vbsfMappingsWaitForChanges(pClient, callHandle, paParms, g_pHelpers->pfnIsCallRestored(callHandle));
+            fAsynchronousProcessing = rc == VINF_HGCM_ASYNC_EXECUTE;
+            break;
+        }
+
+        case SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS:
+        {
+            Log(("SharedFolders host service: svnCall: SHFL_FN_CANCEL_WAIT_FOR_CHANGES\n"));
+
+            /* Validate input: */
+            rc = VERR_INVALID_PARAMETER;
+            ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_CANCEL_MAPPINGS_CHANGES_WAITS);
+
+            /* Execute the function: */
+            rc = vbsfMappingsCancelChangesWaits(pClient);
+            break;
+        }
+
         default:
         {
Index: trunk/src/VBox/HostServices/SharedFolders/shfl.h
===================================================================
--- trunk/src/VBox/HostServices/SharedFolders/shfl.h	(revision 75380)
+++ trunk/src/VBox/HostServices/SharedFolders/shfl.h	(revision 75407)
@@ -43,4 +43,8 @@
 #define SHFL_CF_SYMLINKS         (0x00000008)
 
+/** The call to SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES will return immediately
+ *  because of a SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS call. */
+#define SHFL_CF_CANCEL_NEXT_WAIT (0x00000010)
+
 /** @} */
 
Index: trunk/src/VBox/HostServices/SharedFolders/vbsf.h
===================================================================
--- trunk/src/VBox/HostServices/SharedFolders/vbsf.h	(revision 75380)
+++ trunk/src/VBox/HostServices/SharedFolders/vbsf.h	(revision 75407)
@@ -1,2 +1,3 @@
+/* $Id$ */
 /** @file
  * VBox Shared Folders header.
@@ -15,6 +16,6 @@
  */
 
-#ifndef __VBSF__H
-#define __VBSF__H
+#ifndef ___vbsf_h___
+#define ___vbsf_h___
 
 #include "shfl.h"
@@ -41,3 +42,4 @@
 int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo);
 
-#endif /* __VBSF__H */
+#endif /* !___vbsf_h___ */
+
