Index: /trunk/src/VBox/Additions/os2/VBoxSF/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/Makefile.kmk	(revision 75336)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/Makefile.kmk	(revision 75337)
@@ -29,45 +29,89 @@
 #
 
+ifneq ($(KBUILD_HOST),os2)
+ VBOX_USE_WATCOM_FOR_OS2  = 1
+ KBUILD_TARGET      = os2
+  BUILD_TARGET      = os2
+ KBUILD_TARGET_ARCH = x86
+  BUILD_TARGET_ARCH = x86
+endif
+
 SUB_DEPTH = ../../../../..
 include $(KBUILD_PATH)/subheader.kmk
 
-VBOXSF_USE_WATCOM=1
-TEMPLATE_VBoxGuestR0Watcom = xxx
-TEMPLATE_VBoxGuestR0Watcom_EXTENDS = VBOXGUESTR0
-TEMPLATE_VBoxGuestR0Watcom_TOOL    = OPENWATCOM
-TEMPLATE_VBoxGuestR0Watcom_CTOOL   = OPENWATCOM
-TEMPLATE_VBoxGuestR0Watcom_CXXTOOL = OPENWATCOM
-TEMPLATE_VBoxGuestR0Watcom_LDTOOL  = OPENWATCOM-WL
-TEMPLATE_VBoxGuestR0Watcom_CINCS   = \
-	$(PATH_TOOL_OPENWATCOM)/h \
-	$(PATH_TOOL_OPENWATCOM)/h/os2 \
-       $(PATH_ROOT)/include \
-	/libc/svn/trunk/libc/include # for os2ddk
-TEMPLATE_VBoxGuestR0Watcom_CXXINCS = $(TEMPLATE_VBoxGuestR0Watcom_CINCS)
-TEMPLATE_VBoxGuestR0Watcom_CFLAGS  = -s  -ze -w4 -d2 -hc -nt=TEXT32 -nc=CODE -4
-TEMPLATE_VBoxGuestR0Watcom_CXXFLAGS = $(TEMPLATE_VBoxGuestR0Watcom_CFLAGS)
-
-TEMPLATE_VBoxGuestR0Watcom_LDFLAGS = \
-	form os2 lx physdevice \
-	option NODefaultlibs \
-	debug codeview all \
-	option symfile
-TEMPLATE_VBoxGuestR0Watcom_LIBS = $(NO_SUCH_VARIABLE)
-
-
-#?DLLS += VBoxSF
+#
+# Debug segment name hack for NASM and dependency workaround (see valkit/bootsector).
+#
+include $(KBUILD_PATH)/tools/NASM.kmk
+TOOL_VBoxNasmDot = Our version of the NASM tool
+ifndef TOOL_VBoxNasmDot_PATH
+ ifdef TOOL_VBoxNasm_PATH
+  TOOL_VBoxNasmDot_PATH = $(TOOL_VBoxNasm_PATH)
+ else
+  TOOL_VBoxNasmDot_PATH   := $(firstword $(rsort $(wildcard $(KBUILD_DEVTOOLS_HST)/nasm/v*.*)))
+  if "$(TOOL_VBoxNasmDot_PATH)" == "" && "$(KBUILD_DEVTOOLS_HST_ALT)" != ""
+   TOOL_VBoxNasmDot_PATH  := $(firstword $(rsort $(wildcard $(KBUILD_DEVTOOLS_HST_ALT)/nasm/v*.*)))
+  endif
+ endif
+endif
+ifneq ($(TOOL_VBoxNasmDot_PATH),)
+ TOOL_VBoxNasmDot_AS     ?= $(TOOL_VBoxNasmDot_PATH)/nasm$(HOSTSUFF_EXE)
+else
+ TOOL_VBoxNasmDot_AS     ?= nasm$(HOSTSUFF_EXE)
+endif
+TOOL_VBoxNasmDot_ASFLAGS ?= $(TOOL_NASM_ASFLAGS)
+TOOL_VBoxNasmDot_COMPILE_AS_OUTPUT = $(outbase).lst
+TOOL_VBoxNasmDot_COMPILE_AS_DEPEND = $(dotseg_1_TARGET)
+TOOL_VBoxNasmDot_COMPILE_AS_DEPORD =
+define TOOL_VBoxNasmDot_COMPILE_AS_CMDS
+ifdef TOOL_VBoxNasmDot_USE_KSUBMIT
+	$(QUIET)kmk_builtin_kSubmit -C $(PATH_OUT_BASE) -- $(TOOL_VBoxNasmDot_AS)\
+		$(flags) $(addsuffix /,$(addprefix -i, $(incs))) $(addprefix -D, $(defs))\
+		-l $(outbase).lst\
+		-o $(obj)\
+               -MD "$(dep)" -MP\
+		$(abspath $(source))
+else
+	$(QUIET)$(REDIRECT) -C $(PATH_OUT_BASE) -- $(TOOL_VBoxNasmDot_AS)\
+		$(flags) $(addsuffix /,$(addprefix -i, $(incs))) $(addprefix -D, $(defs))\
+		-l $(outbase).lst\
+		-o $(obj)\
+               -MD "$(dep)" -MP\
+		$(abspath $(source))
+endif
+	$(QUIET)"$(dotseg_1_TARGET)" "$(obj)"
+endef
+
+#
+# Tool for renaming segments and groups from '$debug_*' to '.debug_*'.
+#
+BLDPROGS += dotseg
+dotseg_TEMPLATE = VBoxBldProg
+dotseg_SOURCES = dotseg.cpp
+
+
+#
+# IFS kBuild template tweak.
+#
+TEMPLATE_VBoxR0DrvIFS = Hack to make sure we link a library. IFS must not be physical devices, but DLLs!
+TEMPLATE_VBoxR0DrvIFS_EXTENDS = VBOXGUESTR0
+ifdef VBOX_USE_WATCOM_FOR_OS2
+TEMPLATE_VBoxR0DrvIFS_LDFLAGS = $(subst physdevice,dll, $(TEMPLATE_VBOXGUESTR0_LDFLAGS))
+endif
+TEMPLATE_VBoxR0DrvIFS_SYSSUFF = .ifs
+TEMPLATE_VBoxR0DrvIFS_DLLSUFF = .ifs
+TEMPLATE_VBoxR0DrvIFS_ASTOOL = VBoxNasmDot
+
+
+#
+# The OS/2 shared folder installable file system driver (IFS).
+#
 SYSMODS += VBoxSF
-ifdef VBOXSF_USE_WATCOM
-VBoxSF_TEMPLATE      = VBoxGuestR0Watcom
-else
-VBoxSF_TEMPLATE      = VBOXGUESTR0
-endif
-VBoxSF_SYSSUFF       = .ifs
-VBoxSF_DLLSUFF       = .ifs
+VBoxSF_TEMPLATE      = VBoxR0DrvIFS
 VBoxSF_DEFS          = VBOX_WITH_HGCM
 VBoxSF_INCS         := \
 	. \
 	$(PATH_ROOT)/src/VBox/Runtime/include
-ifdef VBOXSF_USE_WATCOM
+ifdef VBOX_USE_WATCOM_FOR_OS2
 VBoxSF_LDFLAGS      := \
 	OPTION MODNAME='VBoxSF' \
@@ -126,8 +170,8 @@
 	EXPORT 'FS_SETSWAP' \
 	EXPORT 'FS_SHUTDOWN' \
+	EXPORT 'FS_VERIFYUNCNAME' \
 	EXPORT 'FS_WRITE'
 # 32-bit entry points.
 VBoxSF_LDFLAGS += \
-	EXPORT 'FS32_CHGFILEPTR' \
 	EXPORT 'FS32_CHGFILEPTRL' \
 	EXPORT 'FS32_READ' \
@@ -148,5 +192,5 @@
 	VBoxSFFind.cpp \
 	VBoxSFInit.cpp
-ifndef VBOXSF_USE_WATCOM
+ifndef VBOX_USE_WATCOM_FOR_OS2
  VBoxSF_SOURCES     += \
 	VBoxSF.def
@@ -155,5 +199,5 @@
 	$(VBOX_LIB_VBGL_R0) \
 	$(VBOX_LIB_IPRT_GUEST_R0)
-ifdef VBOXSF_USE_WATCOM
+ifdef VBOX_USE_WATCOM_FOR_OS2
  VBoxSF_LIBS        += $(PATH_IGCC)/lib/libend.lib
 else
@@ -161,4 +205,28 @@
 endif
 
+
+#
+# Simple utility for attaching a shared folder to a drive letter.
+#
+PROGRAMS += VBoxSFUtil
+VBoxSFUtil_TEMPLATE = DUMMY
+VBoxSFUtil_TOOL     = OPENWATCOM
+VBoxSFUtil_ASTOOL   = NASM
+VBoxSFUtil_ASFLAGS  = -f obj -DASM_FORMAT_OMF
+VBoxSFUtil_INCS     = $(PATH_TOOL_OPENWATCOM)/h/os2
+VBoxSFUtil_LDFLAGS  = -bcl=os2v2
+VBoxSFUtil_SOURCES  = \
+	VBoxSFUtil.cpp \
+	VBoxSFUtilA.asm
+
+#PROGRAMS += VBoxSFUtil16
+#VBoxSFUtil16_TEMPLATE = DUMMY
+#VBoxSFUtil16_TOOL     = OPENWATCOM-16
+#VBoxSFUtil16_ASTOOL   = NASM
+#VBoxSFUtil16_INCS     = $(PATH_TOOL_OPENWATCOM)/h/os2
+#VBoxSFUtil16_LDFLAGS  = -bcl=os2
+#VBoxSFUtil16_SOURCES  = VBoxSFUtil.cpp
+
+
 include $(FILE_KBUILD_SUB_FOOTER)
 
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp	(revision 75336)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp	(revision 75337)
@@ -38,5 +38,669 @@
 #include <VBox/log.h>
 #include <iprt/assert.h>
-
+#include <iprt/ctype.h>
+#include <iprt/mem.h>
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+/** Max folder name length, including terminator.
+ * Easier to deal with stack buffers if we put a reasonable limit on the. */
+#define VBOXSFOS2_MAX_FOLDER_NAME   64
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+/** The shared mutex protecting folders list, drives and the connection. */
+MutexLock_t         g_MtxFolders;
+/** The shared folder service client structure. */
+VBGLSFCLIENT        g_SfClient;
+/** Set if g_SfClient is valid, clear if not. */
+bool                g_fIsConnectedToService = false;
+/** List of active folder (PVBOXSFFOLDER). */
+RTLISTANCHOR        g_FolderHead;
+/** This is incremented everytime g_FolderHead is modified. */
+uint32_t volatile   g_uFolderRevision;
+/** Folders mapped on drive letters.  Pointers include a reference. */
+PVBOXSFFOLDER       g_apDriveFolders[26];
+
+
+
+/**
+ * Generic IPRT -> OS/2 status code converter.
+ *
+ * @returns OS/2 status code.
+ * @param   vrc             IPRT/VBox status code.
+ * @param   rcDefault       The OS/2 status code to return when there
+ *                          is no translation.
+ */
+APIRET vboxSfOs2ConvertStatusToOs2(int vrc, APIRET rcDefault)
+{
+    switch (vrc)
+    {
+        default:                        return rcDefault;
+
+        case VERR_FILE_NOT_FOUND:       return ERROR_FILE_NOT_FOUND;
+        case VERR_PATH_NOT_FOUND:       return ERROR_PATH_NOT_FOUND;
+        case VERR_SHARING_VIOLATION:    return ERROR_SHARING_VIOLATION;
+        case VERR_ACCESS_DENIED:        return ERROR_ACCESS_DENIED;
+        case VERR_ALREADY_EXISTS:       return ERROR_ACCESS_DENIED;
+        case VERR_WRITE_PROTECT:        return ERROR_WRITE_PROTECT;
+        case VERR_IS_A_DIRECTORY:       return ERROR_DIRECTORY;
+        case VINF_SUCCESS:              return NO_ERROR;
+    }
+}
+
+
+/**
+ * Gets the delta for the local timezone, in minutes.
+ *
+ * We need to do this once for each API call rather than over and over again for
+ * each date/time conversion, so as not to create an update race.
+ *
+ * @returns Delta in minutes.  Current thinking is that positive means timezone
+ *          is west of UTC, while negative is east of it.
+ */
+int16_t vboxSfOs2GetLocalTimeDelta(void)
+{
+    GINFOSEG volatile *pGis = (GINFOSEG volatile *)&KernSISData;
+    if (pGis)
+    {
+        uint16_t cDelta = pGis->timezone;
+        if (cDelta != 0 && cDelta != 0xffff)
+            return (int16_t)cDelta;
+    }
+    return 0;
+}
+
+
+/**
+ * Helper for converting from IPRT timespec format to OS/2 DATE/TIME.
+ *
+ * @param   pDosDate            The output DOS date.
+ * @param   pDosTime            The output DOS time.
+ * @param   SrcTimeSpec         The IPRT input timestamp.
+ * @param   cMinLocalTimeDelta  The timezone delta in minutes.
+ */
+void vboxSfOs2DateTimeFromTimeSpec(FDATE *pDosDate, FTIME *pDosTime, RTTIMESPEC SrcTimeSpec, int16_t cMinLocalTimeDelta)
+{
+    if (cMinLocalTimeDelta != 0)
+        RTTimeSpecAddSeconds(&SrcTimeSpec, cMinLocalTimeDelta * 60);
+
+    RTTIME Time;
+    if (   RTTimeSpecGetNano(&SrcTimeSpec) >= RTTIME_OFFSET_DOS_TIME
+        && RTTimeExplode(&Time, &SrcTimeSpec))
+    {
+        pDosDate->year    = Time.i32Year - 1980;
+        pDosDate->month   = Time.u8Month;
+        pDosDate->day     = Time.u8MonthDay;
+        pDosTime->hours   = Time.u8Hour;
+        pDosTime->minutes = Time.u8Minute;
+        pDosTime->twosecs = Time.u8Second / 2;
+    }
+    else
+    {
+        pDosDate->year    = 0;
+        pDosDate->month   = 1;
+        pDosDate->day     = 1;
+        pDosTime->hours   = 0;
+        pDosTime->minutes = 0;
+        pDosTime->twosecs = 0;
+    }
+}
+
+
+/**
+ * Helper for converting from OS/2 DATE/TIME to IPRT timespec format.
+ *
+ * @returns pDstTimeSpec on success, NULL if invalid input.
+ * @param   DosDate             The input DOS date.
+ * @param   DosTime             The input DOS time.
+ * @param   cMinLocalTimeDelta  The timezone delta in minutes.
+ * @param   pDstTimeSpec        The IPRT output timestamp.
+ */
+PRTTIMESPEC vboxSfOs2DateTimeToTimeSpec(FDATE DosDate, FTIME DosTime, int16_t cMinLocalTimeDelta, PRTTIMESPEC pDstTimeSpec)
+{
+    RTTIME Time;
+    Time.i32Year        = DosDate.year + 1980;
+    Time.u8Month        = DosDate.month;
+    Time.u8WeekDay      = UINT8_MAX;
+    Time.u16YearDay     = 0;
+    Time.u8MonthDay     = DosDate.day;
+    Time.u8Hour         = DosTime.hours;
+    Time.u8Minute       = DosTime.minutes;
+    Time.u8Second       = DosTime.twosecs * 2;
+    Time.u32Nanosecond  = 0;
+    Time.fFlags         = RTTIME_FLAGS_TYPE_LOCAL;
+    Time.offUTC         = cMinLocalTimeDelta;
+    if (RTTimeLocalNormalize(&Time))
+        return RTTimeImplode(pDstTimeSpec, &Time);
+    return NULL;
+}
+
+
+/*********************************************************************************************************************************
+*   Shared Folder String Buffer Management                                                                                       *
+*********************************************************************************************************************************/
+
+/**
+ * Allocates a SHFLSTRING buffer.
+ *
+ * @returns Pointer to a SHFLSTRING buffer, NULL if out of memory.
+ * @param   cchLength   The desired string buffer length (excluding terminator).
+ */
+PSHFLSTRING vboxSfOs2StrAlloc(size_t cchLength)
+{
+    AssertReturn(cchLength <= 0x1000, NULL);
+
+    PSHFLSTRING pStr = (PSHFLSTRING)VbglR0PhysHeapAlloc(SHFLSTRING_HEADER_SIZE + cchLength + 1);
+    if (pStr)
+    {
+        pStr->u16Size       = (uint16_t)(cchLength + 1);
+        pStr->u16Length     = 0;
+        pStr->String.ach[0] = '\0';
+        return pStr;
+    }
+    return NULL;
+}
+
+
+/**
+ * Duplicates a UTF-8 string into a SHFLSTRING buffer.
+ *
+ * @returns Pointer to a SHFLSTRING buffer containing the copy.
+ *          NULL if out of memory or the string is too long.
+ * @param   pachSrc     The string to clone.
+ * @param   cchSrc      The length of the substring, RTMAX_STR for the whole.
+ */
+PSHFLSTRING vboxSfOs2StrDup(const char *pachSrc, size_t cchSrc)
+{
+    if (cchSrc == RTSTR_MAX)
+        cchSrc = strlen(pachSrc);
+    if (cchSrc < 0x1000)
+    {
+        PSHFLSTRING pStr = (PSHFLSTRING)VbglR0PhysHeapAlloc(SHFLSTRING_HEADER_SIZE + cchSrc + 1);
+        if (pStr)
+        {
+            pStr->u16Size  = (uint16_t)(cchSrc + 1);
+            pStr->u16Length = (uint16_t)cchSrc;
+            memcpy(pStr->String.ach, pachSrc, cchSrc);
+            pStr->String.ach[cchSrc] = '\0';
+            return pStr;
+        }
+    }
+    return NULL;
+}
+
+
+/**
+ * Frees a SHLFSTRING buffer.
+ *
+ * @param   pStr        The buffer to free.
+ */
+void vboxSfOs2StrFree(PSHFLSTRING pStr)
+{
+    if (pStr)
+        VbglR0PhysHeapFree(pStr);
+}
+
+
+
+/*********************************************************************************************************************************
+*   Folders, Paths and Service Connection.                                                                                       *
+*********************************************************************************************************************************/
+
+/**
+ * Ensures that we're connected to the host service.
+ *
+ * @returns VBox status code.
+ * @remarks Caller owns g_MtxFolder exclusively!
+ */
+static int vboxSfOs2EnsureConnected(void)
+{
+    if (g_fIsConnectedToService)
+        return VINF_SUCCESS;
+
+    int rc = VbglR0SfConnect(&g_SfClient);
+    if (RT_SUCCESS(rc))
+    {
+        rc = VbglR0SfSetUtf8(&g_SfClient);
+        if (RT_SUCCESS(rc))
+            g_fIsConnectedToService = true;
+        else
+        {
+            LogRel(("VbglR0SfSetUtf8 failed: %Rrc\n", rc));
+            VbglR0SfDisconnect(&g_SfClient);
+        }
+    }
+    else
+        LogRel(("VbglR0SfConnect failed: %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * Destroys a folder when the reference count has reached zero.
+ *
+ * @param   pFolder         The folder to destroy.
+ */
+static void vboxSfOs2DestroyFolder(PVBOXSFFOLDER pFolder)
+{
+    /* Note! We won't get there while the folder is on the list. */
+    LogRel(("vboxSfOs2ReleaseFolder: Destroying %p [%s]\n", pFolder, pFolder->szName));
+    VbglR0SfUnmapFolder(&g_SfClient, &pFolder->hHostFolder);
+    RT_ZERO(pFolder);
+    RTMemFree(pFolder);
+}
+
+
+/**
+ * Releases a reference to a folder.
+ *
+ * @param   pFolder         The folder to release.
+ */
+void vboxSfOs2ReleaseFolder(PVBOXSFFOLDER pFolder)
+{
+    if (pFolder)
+    {
+        uint32_t cRefs = ASMAtomicDecU32(&pFolder->cRefs);
+        AssertMsg(cRefs < _64K, ("%#x\n", cRefs));
+        if (!cRefs)
+            vboxSfOs2DestroyFolder(pFolder);
+    }
+}
+
+
+/**
+ * Retain a reference to a folder.
+ *
+ * @param   pFolder         The folder to release.
+ */
+void vboxSfOs2RetainFolder(PVBOXSFFOLDER pFolder)
+{
+    uint32_t cRefs = ASMAtomicIncU32(&pFolder->cRefs);
+    AssertMsg(cRefs < _64K, ("%#x\n", cRefs));
+}
+
+
+/**
+ * Locates and retains a folder structure.
+ *
+ * @returns Folder matching the name, NULL of not found.
+ * @remarks Caller owns g_MtxFolder.
+ */
+static PVBOXSFFOLDER vboxSfOs2FindAndRetainFolder(const char *pachName, size_t cchName)
+{
+    PVBOXSFFOLDER pCur;
+    RTListForEach(&g_FolderHead, pCur, VBOXSFFOLDER, ListEntry)
+    {
+        if (   pCur->cchName == cchName
+            && RTStrNICmpAscii(pCur->szName, pachName, cchName) == 0)
+        {
+            uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
+            AssertMsg(cRefs < _64K, ("%#x\n", cRefs));
+            return pCur;
+        }
+    }
+    return NULL;
+}
+
+
+/**
+ * Maps a folder, linking it into the list of folders.
+ *
+ * One reference is retained for the caller, which must pass it on or release
+ * it.  The list also have a reference to it.
+ *
+ * @returns VBox status code.
+ * @param   pName       The name of the folder to map.
+ * @param   ppFolder    Where to return the folder structure on success.
+ *
+ * @remarks Caller owns g_MtxFolder exclusively!
+ */
+static int vboxSfOs2MapFolder(PSHFLSTRING pName, PVBOXSFFOLDER *ppFolder)
+{
+    int rc;
+    PVBOXSFFOLDER pNew = (PVBOXSFFOLDER)RTMemAlloc(RT_UOFFSETOF_DYN(VBOXSFFOLDER, szName[pName->u16Length + 1]));
+    if (pNew != NULL)
+    {
+        pNew->u32Magic      = VBOXSFFOLDER_MAGIC;
+        pNew->cRefs         = 2; /* (List reference + the returned reference.) */
+        pNew->cOpenFiles    = 0;
+        pNew->cDrives       = 0;
+        RT_ZERO(pNew->hHostFolder);
+        pNew->hVpb          = 0;
+        pNew->cchName       = (uint8_t)pName->u16Length;
+        memcpy(pNew->szName, pName->String.utf8, pName->u16Length);
+        pNew->szName[pName->u16Length] = '\0';
+
+        rc = VbglR0SfMapFolder(&g_SfClient, pName, &pNew->hHostFolder);
+        if (RT_SUCCESS(rc))
+        {
+            RTListAppend(&g_FolderHead, &pNew->ListEntry);
+            ASMAtomicIncU32(&g_uFolderRevision);
+            LogRel(("vboxSfOs2MapFolder: %p - %s\n", pNew, pNew->szName));
+
+            *ppFolder = pNew;
+        }
+        else
+        {
+            LogRel(("vboxSfOs2MapFolder: VbglR0SfMapFolder(,%.*s,) -> %Rrc\n", pName->u16Length, pName->String.utf8, rc));
+            RTMemFree(pNew);
+        }
+    }
+    else
+    {
+        LogRel(("vboxSfOs2MapFolder: Out of memory :-(\n"));
+        rc = VERR_NO_MEMORY;
+    }
+    return rc;
+}
+
+
+/**
+ * Worker for vboxSfOs2UncPrefixLength.
+ */
+DECLINLINE(size_t) vboxSfOs2CountLeadingSlashes(const char *pszPath)
+{
+    size_t cchSlashes = 0;
+    char ch;
+    while ((ch = *pszPath) == '\\' || ch == '/')
+        cchSlashes++, pszPath++;
+    return cchSlashes;
+}
+
+
+/**
+ * Checks for a VBox UNC prefix (server + slashes) and determins its length when
+ * found.
+ *
+ * @returns Length of VBoxSF UNC prefix, 0 if not VBoxSF UNC prefix.
+ * @param   pszPath             The possible UNC path.
+ */
+DECLINLINE(size_t) vboxSfOs2UncPrefixLength(const char *pszPath)
+{
+    char ch;
+    if (   ((ch = pszPath[0]) == '\\' || ch == '/')
+        && ((ch = pszPath[1]) == '\\' || ch == '/')
+        && ((ch = pszPath[2]) == 'V'  || ch == 'v')
+        && ((ch = pszPath[3]) == 'B'  || ch == 'b')
+        && ((ch = pszPath[4]) == 'O'  || ch == 'o')
+        && ((ch = pszPath[5]) == 'X'  || ch == 'x')
+        && ((ch = pszPath[6]) == 'S'  || ch == 's')
+       )
+    {
+        /* \\VBoxSf\ */
+        if (   ((ch = pszPath[7]) == 'F'  || ch == 'f')
+            && ((ch = pszPath[8]) == '\\' || ch == '/') )
+            return vboxSfOs2CountLeadingSlashes(&pszPath[9]) + 9;
+
+        /* \\VBoxSvr\ */
+        if (   ((ch = pszPath[7]) == 'V'  || ch == 'v')
+            && ((ch = pszPath[8]) == 'R'  || ch == 'r')
+            && ((ch = pszPath[9]) == '\\' || ch == '/') )
+            return vboxSfOs2CountLeadingSlashes(&pszPath[10]) + 10;
+
+        /* \\VBoxSrv\ */
+        if (   ((ch = pszPath[7]) == 'R'  || ch == 'r')
+            && ((ch = pszPath[8]) == 'V'  || ch == 'v')
+            && ((ch = pszPath[9]) == '\\' || ch == '/') )
+            return vboxSfOs2CountLeadingSlashes(&pszPath[10]) + 10;
+    }
+
+    return 0;
+}
+
+
+/**
+ * Converts a path to UTF-8 and puts it in a VBGL friendly buffer.
+ *
+ * @returns OS/2 status code
+ * @param   pszFolderPath   The path to convert.
+ * @param   ppStr           Where to return the pointer to the buffer.  Free
+ *                          using vboxSfOs2FreePath.
+ */
+APIRET vboxSfOs2ConvertPath(const char *pszFolderPath, PSHFLSTRING *ppStr)
+{
+    /* Skip unnecessary leading slashes. */
+    char ch = *pszFolderPath;
+    if (ch == '\\' || ch == '/')
+        while ((ch = pszFolderPath[1]) == '\\' || ch == '/')
+            pszFolderPath++;
+
+    /** @todo do proper codepage -> utf8 conversion and straighten out
+     *        everything... */
+    size_t cchSrc = strlen(pszFolderPath);
+    PSHFLSTRING pDst = vboxSfOs2StrAlloc(cchSrc);
+    if (pDst)
+    {
+        pDst->u16Length = (uint16_t)cchSrc;
+        memcpy(pDst->String.utf8, pszFolderPath, cchSrc);
+        pDst->String.utf8[cchSrc] = '\0';
+        *ppStr = pDst;
+        return NO_ERROR;
+    }
+    *ppStr = NULL;
+    return ERROR_NOT_ENOUGH_MEMORY;
+}
+
+
+/**
+ * Counterpart to vboxSfOs2ResolvePath.
+ *
+ * @param   pStrPath        The path to free.
+ * @param   pFolder         The folder to release.
+ */
+void vboxSfOs2ReleasePathAndFolder(PSHFLSTRING pStrPath, PVBOXSFFOLDER pFolder)
+{
+    if (pStrPath)
+        VbglR0PhysHeapFree(pStrPath);
+    if (pFolder)
+    {
+        uint32_t cRefs = ASMAtomicDecU32(&pFolder->cRefs);
+        Assert(cRefs < _64K);
+        if (!cRefs)
+            vboxSfOs2DestroyFolder(pFolder);
+    }
+}
+
+
+/**
+ * Worker for vboxSfOs2ResolvePath() for dynamically mapping folders for UNC
+ * paths.
+ *
+ * @returns OS/2 status code.
+ * @param   pachFolderName  The folder to map.  Not necessarily zero terminated
+ *                          at the end of the folder name!
+ * @param   cchFolderName   The length of the folder name.
+ * @param   uRevBefore      The previous folder list revision.
+ * @param   ppFolder        Where to return the pointer to the retained folder.
+ */
+DECL_NO_INLINE(static, int) vboxSfOs2AttachUncAndRetain(const char *pachFolderName, size_t cchFolderName,
+                                                        uint32_t uRevBefore, PVBOXSFFOLDER *ppFolder)
+{
+    KernRequestExclusiveMutex(&g_MtxFolders);
+
+    /*
+     * Check if someone raced us to it.
+     */
+    if (uRevBefore != g_uFolderRevision)
+    {
+        PVBOXSFFOLDER pFolder = vboxSfOs2FindAndRetainFolder(pachFolderName, cchFolderName);
+        if (pFolder)
+        {
+            KernReleaseExclusiveMutex(&g_MtxFolders);
+            *ppFolder = pFolder;
+            return NO_ERROR;
+        }
+    }
+
+    int rc = vboxSfOs2EnsureConnected();
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Copy the name into the buffer format that Vbgl desires.
+         */
+        PSHFLSTRING pStrName = vboxSfOs2StrDup(pachFolderName, cchFolderName);
+        if (pStrName)
+        {
+            /*
+             * Do the attaching.
+             */
+            rc = vboxSfOs2MapFolder(pStrName, ppFolder);
+            vboxSfOs2StrFree(pStrName);
+            if (RT_SUCCESS(rc))
+            {
+                KernReleaseExclusiveMutex(&g_MtxFolders);
+                LogRel(("vboxSfOs2AttachUncAndRetain: Successfully attached '%s' (as UNC).\n", (*ppFolder)->szName));
+                return NO_ERROR;
+            }
+
+            if (rc == VERR_NO_MEMORY)
+                rc = ERROR_NOT_ENOUGH_MEMORY;
+            else
+                rc = ERROR_PATH_NOT_FOUND;
+        }
+        else
+            rc = ERROR_NOT_ENOUGH_MEMORY;
+    }
+    else
+        rc = ERROR_PATH_NOT_FOUND;
+
+    KernReleaseExclusiveMutex(&g_MtxFolders);
+    return rc;
+}
+
+
+/**
+ * Resolves the given path to a folder structure and folder relative string.
+ *
+ * @returns OS/2 status code.
+ * @param   pszPath         The path to resolve.
+ * @param   pCdFsd          The IFS dependent CWD structure if present.
+ * @param   offCurDirEnd    The offset into @a pszPath of the CWD.  -1 if not
+ *                          CWD relative path.
+ * @param   ppFolder        Where to return the referenced pointer to the folder
+ *                          structure.  Call vboxSfOs2ReleaseFolder() when done.
+ * @param   ppStrFolderPath Where to return a buffer holding the folder relative
+ *                          path component.  Free using vboxSfOs2FreePath().
+ */
+APIRET vboxSfOs2ResolvePath(const char *pszPath, PVBOXSFCD pCdFsd, LONG offCurDirEnd,
+                            PVBOXSFFOLDER *ppFolder, PSHFLSTRING *ppStrFolderPath)
+{
+    APIRET rc;
+
+    /*
+     * UNC path?  Reject the prefix to be on the safe side.
+     */
+    char ch = pszPath[0];
+    if (ch == '\\' || ch == '/')
+    {
+        size_t cchPrefix = vboxSfOs2UncPrefixLength(pszPath);
+        if (cchPrefix > 0)
+        {
+            /* Find the length of the folder name (share). */
+            const char *pszFolderName = &pszPath[cchPrefix];
+            size_t      cchFolderName = 0;
+            while ((ch = pszFolderName[cchFolderName]) != '\0' && ch != '\\' && ch != '/')
+            {
+                if ((uint8_t)ch >= 0x20 && (uint8_t)ch <= 0x7f && ch != ':')
+                    cchFolderName++;
+                else
+                {
+                    LogRel(("vboxSfOs2ResolvePath: Invalid share name (@%u): %.*Rhxs\n",
+                            cchPrefix + cchFolderName, strlen(pszPath), pszPath));
+                    return ERROR_INVALID_NAME;
+                }
+            }
+            if (cchFolderName >= VBOXSFOS2_MAX_FOLDER_NAME)
+            {
+                LogRel(("vboxSfOs2ResolvePath: Folder name is too long: %u, max %u (%s)\n",
+                        cchFolderName, VBOXSFOS2_MAX_FOLDER_NAME, pszPath));
+                return ERROR_FILENAME_EXCED_RANGE;
+            }
+
+            /*
+             * Look for the share.
+             */
+            KernRequestSharedMutex(&g_MtxFolders);
+            PVBOXSFFOLDER pFolder = *ppFolder = vboxSfOs2FindAndRetainFolder(pszFolderName, cchFolderName);
+            if (pFolder)
+            {
+                vboxSfOs2RetainFolder(pFolder);
+                KernReleaseSharedMutex(&g_MtxFolders);
+            }
+            else
+            {
+                uint32_t const uRevBefore = g_uFolderRevision;
+                KernReleaseSharedMutex(&g_MtxFolders);
+                rc = vboxSfOs2AttachUncAndRetain(pszFolderName, cchFolderName, uRevBefore, ppFolder);
+                if (rc == NO_ERROR)
+                    pFolder = *ppFolder;
+                else
+                    return rc;
+            }
+
+            /*
+             * Convert the path and put it in a Vbgl compatible buffer..
+             */
+            rc = vboxSfOs2ConvertPath(&pszFolderName[cchFolderName], ppStrFolderPath);
+            if (rc == NO_ERROR)
+                return rc;
+
+            vboxSfOs2ReleaseFolder(pFolder);
+            *ppFolder = NULL;
+            return rc;
+        }
+
+        LogRel(("vboxSfOs2ResolvePath: Unexpected path: %s\n", pszPath));
+        return ERROR_PATH_NOT_FOUND;
+    }
+
+    /*
+     * Drive letter?
+     */
+    ch &= ~0x20; /* upper case */
+    if (   ch >= 'A'
+        && ch <= 'Z'
+        && pszPath[1] == ':')
+    {
+        unsigned iDrive = ch - 'A';
+        ch  = pszPath[2];
+        if (ch == '\\' || ch == '/')
+        {
+            KernRequestSharedMutex(&g_MtxFolders);
+            PVBOXSFFOLDER pFolder = *ppFolder = g_apDriveFolders[iDrive];
+            if (pFolder)
+            {
+                vboxSfOs2RetainFolder(pFolder);
+                KernReleaseSharedMutex(&g_MtxFolders);
+
+                /*
+                 * Convert the path and put it in a Vbgl compatible buffer..
+                 */
+                rc = vboxSfOs2ConvertPath(&pszPath[3], ppStrFolderPath);
+                if (rc == NO_ERROR)
+                    return rc;
+
+                vboxSfOs2ReleaseFolder(pFolder);
+                *ppFolder = NULL;
+                return rc;
+            }
+            KernReleaseSharedMutex(&g_MtxFolders);
+            LogRel(("vboxSfOs2ResolvePath: No folder mapped on '%s'. Detach race?\n", pszPath));
+            return ERROR_PATH_NOT_FOUND;
+        }
+        LogRel(("vboxSfOs2ResolvePath: No root slash: '%s'\n", pszPath));
+        return ERROR_PATH_NOT_FOUND;
+    }
+    LogRel(("vboxSfOs2ResolvePath: Unexpected path: %s\n", pszPath));
+    RT_NOREF_PV(pCdFsd); RT_NOREF_PV(offCurDirEnd);
+    return ERROR_PATH_NOT_FOUND;
+}
 
 
@@ -44,25 +708,289 @@
 FS32_EXIT(ULONG uid, ULONG pid, ULONG pdb)
 {
+    LogFlow(("FS32_EXIT: uid=%u pid=%u pdb=%#x\n", uid, pid, pdb));
     NOREF(uid); NOREF(pid); NOREF(pdb);
 }
 
 
-DECLASM(int)
-FS32_SHUTDOWN(ULONG type, ULONG reserved)
-{
-    NOREF(type); NOREF(reserved);
+DECLASM(APIRET)
+FS32_SHUTDOWN(ULONG uType, ULONG uReserved)
+{
+    LogFlow(("FS32_SHUTDOWN: type=%u uReserved=%u\n", uType, uReserved));
+    NOREF(uType); NOREF(uReserved);
     return NO_ERROR;
 }
 
 
-DECLASM(int)
-FS32_ATTACH(ULONG fFlags, PCSZ pszDev, PVBOXSFVP pvpfsd, PVBOXSFCD pcdfsd, PBYTE pszParm, PUSHORT pcbParm)
-{
-    NOREF(fFlags); NOREF(pszDev); NOREF(pvpfsd); NOREF(pcdfsd); NOREF(pszParm); NOREF(pcbParm);
+/**
+ * FS32_ATTACH worker: FS_ATTACH
+ */
+static APIRET vboxSfOs2Attach(PCSZ pszDev, PVBOXSFVP pVpFsd, PVBOXSFCD pCdFsd, PBYTE pszParam, PUSHORT pcbParam,
+                              PSHFLSTRING *ppCleanup)
+{
+    /*
+     * Check out the parameters, copying the pszParam into a suitable string buffer.
+     */
+    if (pszDev == NULL || !*pszDev || !RT_C_IS_ALPHA(pszDev[0]) || pszDev[1] != ':' || pszDev[2] != '\0')
+    {
+        LogRel(("vboxSfOs2Attach: Invalid pszDev value:%p:{%s}\n", pszDev, pszDev));
+        return ERROR_INVALID_PARAMETER;
+    }
+    unsigned const iDrive = (pszDev[0] & ~0x20) - 'A';
+
+    if (pszParam == NULL || pcbParam == NULL)
+    {
+        LogRel(("vboxSfOs2Attach: NULL parameter buffer or buffer length\n"));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    PSHFLSTRING pStrName = *ppCleanup = vboxSfOs2StrAlloc(VBOXSFOS2_MAX_FOLDER_NAME - 1);
+    pStrName->u16Length = *pcbParam;
+    if (pStrName->u16Length < 1 || pStrName->u16Length > VBOXSFOS2_MAX_FOLDER_NAME)
+    {
+        LogRel(("vboxSfOs2Attach: Parameter buffer length is out of bounds: %u (min: 1, max " RT_XSTR(VBOXSFOS2_MAX_FOLDER_NAME) ")\n",
+                pStrName->u16Length));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    int rc = KernCopyIn(pStrName->String.utf8, pszParam, pStrName->u16Length);
+    if (rc != NO_ERROR)
+        return rc;
+
+    pStrName->u16Length -= 1;
+    if (pStrName->String.utf8[pStrName->u16Length] != '\0')
+    {
+        LogRel(("vboxSfOs2Attach: Parameter not null terminated\n"));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /* Make sure it's only ascii and contains not weird stuff. */
+    unsigned off = pStrName->u16Length;
+    while (off-- > 0)
+    {
+        char const ch = pStrName->String.utf8[off];
+        if (ch < 0x20 || ch >= 0x7f || ch == ':' || ch == '\\' || ch == '/')
+        {
+            LogRel(("vboxSfOs2Attach: Malformed folder name: %.*Rhxs (off %#x)\n", pStrName->u16Length, pStrName->String.utf8, off));
+            return ERROR_INVALID_PARAMETER;
+        }
+    }
+
+    if (!pVpFsd)
+    {
+        LogRel(("vboxSfOs2Attach: pVpFsd is NULL\n"));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /*
+     * Look for the folder to see if we're already using it.  Map it if needed.
+     */
+    KernRequestExclusiveMutex(&g_MtxFolders);
+    if (g_apDriveFolders[iDrive] == NULL)
+    {
+
+        PVBOXSFFOLDER pFolder = vboxSfOs2FindAndRetainFolder(pStrName->String.ach, pStrName->u16Length);
+        if (!pFolder)
+        {
+            rc = vboxSfOs2EnsureConnected();
+            if (RT_SUCCESS(rc))
+                rc = vboxSfOs2MapFolder(pStrName, &pFolder);
+        }
+        if (pFolder && RT_SUCCESS(rc))
+        {
+            pFolder->cDrives += 1;
+            g_apDriveFolders[iDrive] = pFolder;
+
+            pVpFsd->u32Magic = VBOXSFVP_MAGIC;
+            pVpFsd->pFolder  = pFolder;
+
+            KernReleaseExclusiveMutex(&g_MtxFolders);
+
+            LogRel(("vboxSfOs2Attach: Successfully attached '%s' to '%s'.\n", pFolder->szName, pszDev));
+            return NO_ERROR;
+        }
+
+        KernReleaseExclusiveMutex(&g_MtxFolders);
+        return ERROR_FILE_NOT_FOUND;
+    }
+    KernReleaseExclusiveMutex(&g_MtxFolders);
+
+    LogRel(("vboxSfOs2Attach: Already got a folder on '%s'!\n", pszDev));
+    RT_NOREF(pCdFsd);
+    return ERROR_BUSY_DRIVE;
+}
+
+
+/**
+ * FS32_ATTACH worker: FS_DETACH
+ */
+static APIRET vboxSfOs2Detach(PCSZ pszDev, PVBOXSFVP pVpFsd, PVBOXSFCD pCdFsd, PBYTE pszParam, PUSHORT pcbParam)
+{
+    /*
+     * Validate the volume data and assocated folder.
+     */
+    AssertPtrReturn(pVpFsd, ERROR_SYS_INTERNAL);
+    AssertReturn(pVpFsd->u32Magic == VBOXSFVP_MAGIC, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pVpFsd->pFolder;
+    AssertPtrReturn(pFolder, ERROR_SYS_INTERNAL);
+    AssertReturn(pFolder->u32Magic == VBOXSFFOLDER_MAGIC, ERROR_SYS_INTERNAL);
+
+    uint8_t idxDrive = UINT8_MAX;
+    if (   pszDev
+        && RT_C_IS_ALPHA(*pszDev))
+        idxDrive = (*pszDev & ~0x20) - 'A';
+
+    /*
+     * Can we detach it?
+     */
+    APIRET rc;
+    KernRequestExclusiveMutex(&g_MtxFolders);
+    if (   pFolder->cOpenFiles == 0
+        && pFolder->cOpenSearches == 0)
+    {
+        /*
+         * Check that we've got the right folder/drive combo.
+         */
+        if (   idxDrive < RT_ELEMENTS(g_apDriveFolders)
+            && g_apDriveFolders[idxDrive] == pFolder)
+        {
+            g_apDriveFolders[idxDrive] = NULL;
+            uint8_t cDrives = --pFolder->cDrives;
+            AssertMsg(cDrives < 30, ("%#x\n", cDrives));
+
+            uint32_t cRefs = ASMAtomicDecU32(&pFolder->cRefs);
+            AssertMsg(cRefs < _32K, ("%#x\n", cRefs));
+            if (cRefs)
+            {
+                /* If there are no zero drives, unlink it from the list and release
+                   the list reference.  This should almost always drop end up with us
+                   destroying the folder.*/
+                if (cDrives == 0)
+                {
+                    RTListNodeRemove(&pFolder->ListEntry);
+                    cRefs = ASMAtomicDecU32(&pFolder->cRefs);
+                    AssertMsg(cRefs < _32K, ("%#x\n", cRefs));
+                    if (!cRefs)
+                        vboxSfOs2DestroyFolder(pFolder);
+                }
+            }
+            else
+            {
+                LogRel(("vboxSfOs2Detach: cRefs=0?!?\n"));
+                vboxSfOs2DestroyFolder(pFolder);
+            }
+        }
+        else
+        {
+            LogRel(("vboxSfOs2Detach: g_apDriveFolders[%#x]=%p pFolder=%p\n",
+                    idxDrive, idxDrive < RT_ELEMENTS(g_apDriveFolders) ? g_apDriveFolders[idxDrive] : NULL, pFolder));
+            rc = ERROR_NOT_SUPPORTED;
+        }
+    }
+    else
+        rc = ERROR_BUSY_DRIVE;
+    KernReleaseExclusiveMutex(&g_MtxFolders);
+
+    RT_NOREF(pszDev, pVpFsd, pCdFsd, pszParam, pcbParam);
+    return rc;
+}
+
+
+/**
+ * FS32_ATTACH worker: FSA_ATTACH_INFO
+ */
+static APIRET vboxSfOs2QueryAttachInfo(PCSZ pszDev, PVBOXSFVP pVpFsd, PVBOXSFCD pCdFsd, PBYTE pbData, PUSHORT pcbParam)
+{
+    /*
+     * Userland calls the kernel with a FSQBUFFER buffer, the kernel
+     * fills in the first part of us and hands us &FSQBUFFER::cbFSAData
+     * to do the rest.  We could return the share name here, for instance.
+     */
+    APIRET rc;
+    USHORT cbParam = *pcbParam;
+    if (   pszDev == NULL
+        || (pszDev[0] != '\\' && pszDev[0] != '/'))
+    {
+        /* Validate the volume data and assocated folder. */
+        AssertPtrReturn(pVpFsd, ERROR_SYS_INTERNAL);
+        AssertReturn(pVpFsd->u32Magic == VBOXSFVP_MAGIC, ERROR_SYS_INTERNAL);
+        PVBOXSFFOLDER pFolder = pVpFsd->pFolder;
+        AssertPtrReturn(pFolder, ERROR_SYS_INTERNAL);
+        AssertReturn(pFolder->u32Magic == VBOXSFFOLDER_MAGIC, ERROR_SYS_INTERNAL);
+
+        /* Try copy out the data. */
+        if (cbParam >= sizeof(USHORT) + pFolder->cchName + 1)
+        {
+            *pcbParam = (uint16_t)sizeof(USHORT) + pFolder->cchName + 1;
+            cbParam = pFolder->cchName + 1;
+            rc = KernCopyOut(pbData, &cbParam, sizeof(cbParam));
+            if (rc != NO_ERROR)
+                rc = KernCopyOut(pbData + sizeof(USHORT), pFolder->szName, pFolder->cchName + 1);
+        }
+        else
+            rc = ERROR_BUFFER_OVERFLOW;
+    }
+    else
+    {
+        /* Looks like a device query, so return zero bytes. */
+        if (cbParam >= sizeof(USHORT))
+        {
+            *pcbParam = sizeof(USHORT);
+            cbParam   = 0;
+            rc = KernCopyOut(pbData, &cbParam, sizeof(cbParam));
+        }
+        else
+            rc = ERROR_BUFFER_OVERFLOW;
+    }
+
+    RT_NOREF(pCdFsd);
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_ATTACH(ULONG fFlags, PCSZ pszDev, PVBOXSFVP pVpFsd, PVBOXSFCD pCdFsd, PBYTE pszParam, PUSHORT pcbParam)
+{
+    LogFlow(("FS32_ATTACH: fFlags=%#x  pszDev=%p:{%s} pVpFsd=%p pCdFsd=%p pszParam=%p pcbParam=%p\n",
+             fFlags, pszDev, pszDev, pVpFsd, pCdFsd, pszParam, pcbParam));
+    APIRET rc;
+    if (pVpFsd)
+    {
+        PSHFLSTRING pCleanup = NULL;
+
+        if (fFlags == FS_ATTACH)
+            rc = vboxSfOs2Attach(pszDev, pVpFsd, pCdFsd, pszParam, pcbParam, &pCleanup);
+        else if (fFlags == FSA_DETACH)
+            rc = vboxSfOs2Detach(pszDev, pVpFsd, pCdFsd, pszParam, pcbParam);
+        else if (fFlags == FSA_ATTACH_INFO)
+            rc = vboxSfOs2QueryAttachInfo(pszDev, pVpFsd, pCdFsd, pszParam, pcbParam);
+        else
+        {
+            LogRel(("FS32_ATTACH: Unsupported fFlags value: %#x\n", fFlags));
+            rc = ERROR_NOT_SUPPORTED;
+        }
+
+        if (pCleanup)
+            vboxSfOs2StrFree(pCleanup);
+    }
+    else
+        rc = ERROR_NOT_SUPPORTED; /* We don't support device attaching. */
+    LogFlow(("FS32_ATTACH: returns %u\n", rc));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_VERIFYUNCNAME(ULONG uType, PCSZ pszName)
+{
+    LogFlow(("FS32_VERIFYUNCNAME: uType=%#x pszName=%p:{%s}\n", uType, pszName, pszName));
+    RT_NOREF(uType); /* pass 1 or pass 2 doesn't matter to us, we've only got one 'server'. */
+
+    if (vboxSfOs2UncPrefixLength(pszName) > 0 )
+        return NO_ERROR;
     return ERROR_NOT_SUPPORTED;
 }
 
 
-DECLASM(int)
+DECLASM(APIRET)
 FS32_FLUSHBUF(USHORT hVPB, ULONG fFlags)
 {
@@ -72,18 +1000,161 @@
 
 
-DECLASM(int)
-FS32_FSINFO(ULONG fFlags, USHORT hVPB, PBYTE pbData, USHORT cbData, ULONG uLevel)
-{
-    NOREF(fFlags); NOREF(hVPB); NOREF(pbData); NOREF(cbData); NOREF(uLevel);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_FSCTL(union argdat *pArgdat, ULONG iArgType, ULONG uFunction,
+DECLASM(APIRET)
+FS32_FSINFO(ULONG fFlags, USHORT hVpb, PBYTE pbData, ULONG cbData, ULONG uLevel)
+{
+    LogFlow(("FS32_FSINFO: fFlags=%#x hVpb=%#x pbData=%p cbData=%#x uLevel=%p\n", fFlags, hVpb, pbData, cbData, uLevel));
+
+    /*
+     * Resolve hVpb and do parameter validation.
+     */
+    PVPFSI pVpFsi = NULL;
+    PVBOXSFVP pVpFsd = Fsh32GetVolParams(hVpb, &pVpFsi);
+    Log(("FS32_FSINFO: hVpb=%#x -> pVpFsd=%p pVpFsi=%p\n", hVpb, pVpFsd, pVpFsi));
+
+    AssertPtrReturn(pVpFsd, ERROR_SYS_INTERNAL);
+    AssertReturn(pVpFsd->u32Magic == VBOXSFVP_MAGIC, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pVpFsd->pFolder;      /** @todo need to retain it behind locks. */
+    AssertPtrReturn(pFolder, ERROR_SYS_INTERNAL);
+    AssertReturn(pFolder->u32Magic == VBOXSFFOLDER_MAGIC, ERROR_SYS_INTERNAL);
+
+    APIRET rc;
+
+    /*
+     * Queries.
+     */
+    if (fFlags == INFO_RETREIVE)
+    {
+        /* Check that buffer/level matches up. */
+        switch (uLevel)
+        {
+            case FSIL_ALLOC:
+                if (cbData >= sizeof(FSALLOCATE))
+                    break;
+                LogFlow(("FS32_FSINOF: cbData=%u < sizeof(FSALLOCATE) -> ERROR_BUFFER_OVERFLOW\n", cbData));
+                return ERROR_BUFFER_OVERFLOW;
+
+            case FSIL_VOLSER:
+                if (cbData >= sizeof(FSINFO))
+                    break;
+                LogFlow(("FS32_FSINOF: cbData=%u < sizeof(FSINFO) -> ERROR_BUFFER_OVERFLOW\n", cbData));
+                return ERROR_BUFFER_OVERFLOW;
+
+            default:
+                LogRel(("FS32_FSINFO: Unsupported info level %u!\n", uLevel));
+                return ERROR_INVALID_LEVEL;
+        }
+
+        /* Work buffer union to keep it to a single allocation and no stack. */
+        union FsInfoBufs
+        {
+            struct
+            {
+                SHFLCREATEPARMS Params;
+                union
+                {
+                    SHFLSTRING Path;
+                    uint8_t    abPadding[SHFLSTRING_HEADER_SIZE + 4];
+                };
+            } Open;
+            struct
+            {
+                SHFLVOLINFO VolInfo;
+                union
+                {
+                    FSALLOCATE  Alloc;
+                    FSINFO      FsInfo;
+                };
+            } Info;
+        } *pu = (union FsInfoBufs *)VbglR0PhysHeapAlloc(sizeof(*pu));
+        if (!pu)
+            return ERROR_NOT_ENOUGH_MEMORY;
+
+        /*
+         * To get the info we need to open the root of the folder.
+         */
+        RT_ZERO(pu->Open.Params);
+        pu->Open.Params.CreateFlags = SHFL_CF_DIRECTORY   | SHFL_CF_ACT_FAIL_IF_NEW  | SHFL_CF_ACT_OPEN_IF_EXISTS
+                                    | SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_ATTR_READ | SHFL_CF_ACCESS_DENYNONE;
+        pu->Open.Path.u16Size   = 3;
+        pu->Open.Path.u16Length = 2;
+        pu->Open.Path.String.utf8[0] = '\\';
+        pu->Open.Path.String.utf8[1] = '.';
+        pu->Open.Path.String.utf8[2] = '\0';
+
+        int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, &pu->Open.Path, &pu->Open.Params);
+        LogFlow(("FS32_FSINFO: VbglR0SfCreate -> %Rrc Result=%d Handle=%#RX64\n", vrc, pu->Open.Params.Result, pu->Open.Params.Handle));
+        if (   RT_SUCCESS(vrc)
+            && pu->Open.Params.Handle != SHFL_HANDLE_NIL)
+        {
+            SHFLHANDLE hHandle = pu->Open.Params.Handle;
+
+            RT_ZERO(pu->Info.VolInfo);
+            uint32_t cbBuf = sizeof(pu->Info.VolInfo);
+            vrc = VbglR0SfFsInfo(&g_SfClient, &pFolder->hHostFolder, hHandle, SHFL_INFO_VOLUME | SHFL_INFO_GET,
+                                 &cbBuf, (PSHFLDIRINFO)&pu->Info.VolInfo);
+            if (RT_SUCCESS(vrc))
+            {
+                /*
+                 * Construct and copy out the requested info.
+                 */
+                if (uLevel == FSIL_ALLOC)
+                {
+                    pu->Info.Alloc.idFileSystem = 0; /* unknown */
+                    pu->Info.Alloc.cSectorUnit  = pu->Info.VolInfo.ulBytesPerAllocationUnit / RT_MAX(pu->Info.VolInfo.ulBytesPerSector, 1);
+                    pu->Info.Alloc.cUnit        = (uint32_t)(pu->Info.VolInfo.ullTotalAllocationBytes  / RT_MAX(pu->Info.VolInfo.ulBytesPerAllocationUnit, 1));
+                    pu->Info.Alloc.cUnitAvail   = (uint32_t)(pu->Info.VolInfo.ullAvailableAllocationBytes  / RT_MAX(pu->Info.VolInfo.ulBytesPerAllocationUnit, 1));
+                    pu->Info.Alloc.cbSector     = (uint16_t)(pu->Info.VolInfo.ulBytesPerSector);
+                    rc = KernCopyOut(pbData, &pu->Info.Alloc, sizeof(pu->Info.Alloc));
+                }
+                else
+                {
+                    RT_ZERO(pu->Info.FsInfo);
+                    pu->Info.FsInfo.vol.cch = (uint8_t)RT_MIN(pFolder->cchName, sizeof(pu->Info.FsInfo.vol.szVolLabel) - 1);
+                    memcpy(pu->Info.FsInfo.vol.szVolLabel, pFolder->szName, pu->Info.FsInfo.vol.cch);
+                    *(uint32_t *)&pu->Info.FsInfo.fdateCreation = pu->Info.VolInfo.ulSerial;
+                    rc = KernCopyOut(pbData, &pu->Info.FsInfo, sizeof(pu->Info.FsInfo));
+                }
+            }
+            else
+            {
+                LogRel(("FS32_FSINFO: VbglR0SfFsInfo/SHFL_INFO_VOLUME failed: %Rrc\n", rc));
+                rc = ERROR_GEN_FAILURE;
+            }
+
+            vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, hHandle);
+            AssertRC(vrc);
+        }
+        else
+            rc = ERROR_GEN_FAILURE;
+
+        VbglR0PhysHeapFree(pu);
+    }
+    /*
+     * We don't allow setting anything.
+     */
+    else if (fFlags == INFO_SET)
+    {
+        LogRel(("FS32_FSINFO: Attempting to set volume info (uLevel=%u, cbData=%#x) -> ERROR_ACCESS_DENIED\n", uLevel, cbData));
+        rc = ERROR_ACCESS_DENIED;
+    }
+    else
+    {
+        LogRel(("FS32_FSINFO: Unknown flags: %#x\n", fFlags));
+        rc = ERROR_SYS_INTERNAL;
+    }
+
+    LogFlow(("FS32_FSINFO: returns %#x\n", rc));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_FSCTL(union argdat *pArgData, ULONG iArgType, ULONG uFunction,
            PVOID pvParm, USHORT cbParm, PUSHORT pcbParmIO,
            PVOID pvData, USHORT cbData, PUSHORT pcbDataIO)
 {
-    NOREF(pArgdat); NOREF(iArgType); NOREF(uFunction); NOREF(pvParm); NOREF(cbParm); NOREF(pcbParmIO);
+    LogFlow(("FS32_FSCTL: pArgData=%p iArgType=%#x uFunction=%#x pvParam=%p cbParam=%#x pcbParmIO=%p pvData=%p cbData=%#x pcbDataIO=%p\n",
+             pArgData, iArgType, uFunction, pvParm, cbParm, pcbParmIO, pvData, cbData, pcbDataIO));
+    NOREF(pArgData); NOREF(iArgType); NOREF(uFunction); NOREF(pvParm); NOREF(cbParm); NOREF(pcbParmIO);
     NOREF(pvData); NOREF(cbData); NOREF(pcbDataIO);
     return ERROR_NOT_SUPPORTED;
@@ -91,7 +1162,8 @@
 
 
-DECLASM(int)
+DECLASM(APIRET)
 FS32_PROCESSNAME(PSZ pszName)
 {
+    LogFlow(("FS32_PROCESSNAME: '%s'\n", pszName));
     NOREF(pszName);
     return NO_ERROR;
@@ -99,78 +1171,959 @@
 
 
-DECLASM(int)
-FS32_CHDIR(ULONG fFlags, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszDir, USHORT iCurDirEnd)
-{
-    NOREF(fFlags); NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszDir); NOREF(iCurDirEnd);
+DECLASM(APIRET)
+FS32_CHDIR(ULONG fFlags, PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszDir, LONG offCurDirEnd)
+{
+    LogFlow(("FS32_CHDIR: fFlags=%#x pCdFsi=%p:{%#x,%s} pCdFsd=%p pszDir=%p:{%s} offCurDirEnd=%d\n",
+             fFlags, pCdFsi, pCdFsi ? pCdFsi->cdi_hVPB : 0xffff, pCdFsi ? pCdFsi->cdi_curdir : "", pCdFsd, pszDir, pszDir, offCurDirEnd));
+
+    /*
+     * We do not keep any information about open directory, just verify
+     * them before they are CD'ed into and when asked to revalidate them.
+     * If there were any path walking benefits, we could consider opening the
+     * directory and keeping it open, but there isn't, so we don't do that.
+     */
+    APIRET rc = NO_ERROR;
+    if (   fFlags == CD_EXPLICIT
+        || fFlags == CD_VERIFY)
+    {
+        if (fFlags == CD_VERIFY)
+            pszDir = pCdFsi->cdi_curdir;
+
+        PVBOXSFFOLDER pFolder;
+        PSHFLSTRING   pStrFolderPath;
+        rc = vboxSfOs2ResolvePath(pszDir, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
+        if (rc == NO_ERROR)
+        {
+            SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
+            if (pParams)
+            {
+                RT_ZERO(*pParams);
+                pParams->CreateFlags = SHFL_CF_LOOKUP;
+
+                int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
+                LogFlow(("FS32_CHDIR: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
+                if (RT_SUCCESS(vrc))
+                {
+                    switch (pParams->Result)
+                    {
+                        case SHFL_FILE_EXISTS:
+                            if (RTFS_IS_DIRECTORY(pParams->Info.Attr.fMode))
+                                rc = NO_ERROR;
+                            else
+                                rc = ERROR_ACCESS_DENIED;
+                            break;
+
+                        case SHFL_PATH_NOT_FOUND:
+                            rc = ERROR_PATH_NOT_FOUND;
+                            break;
+
+                        default:
+                        case SHFL_FILE_NOT_FOUND:
+                            rc = ERROR_FILE_NOT_FOUND;
+                            break;
+                    }
+                }
+                else
+                    rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_PATH_NOT_FOUND);
+                VbglR0PhysHeapFree(pParams);
+            }
+            else
+                rc = ERROR_NOT_ENOUGH_MEMORY;
+            vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
+        }
+    }
+    else if (fFlags == CD_FREE)
+    {
+        /* nothing to do here. */
+    }
+    else
+    {
+        LogRel(("FS32_CHDIR: Unexpected fFlags value: %#x\n", fFlags));
+        rc = ERROR_NOT_SUPPORTED;
+    }
+
+    LogFlow(("FS32_CHDIR: returns %u\n", rc));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_MKDIR(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszDir, LONG offCurDirEnd, PEAOP pEaOp, ULONG fFlags)
+{
+    LogFlow(("FS32_MKDIR: pCdFsi=%p pCdFsd=%p pszDir=%p:{%s} pEAOp=%p fFlags=%#x\n", pCdFsi, pCdFsd, pszDir, pszDir, offCurDirEnd, pEaOp, fFlags));
+
+    /*
+     * We don't do EAs.
+     */
+    APIRET rc;
+    if (pEaOp == NULL)
+    {
+        /*
+         * Resolve the path.
+         */
+        PVBOXSFFOLDER pFolder;
+        PSHFLSTRING   pStrFolderPath;
+        rc = vboxSfOs2ResolvePath(pszDir, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
+        if (rc == NO_ERROR)
+        {
+            /*
+             * The silly interface for creating directories amounts an open call that
+             * fails if it exists and we get a file handle back that needs closing.  Sigh.
+             */
+            SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
+            if (pParams != NULL)
+            {
+                RT_ZERO(*pParams);
+                pParams->CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_CREATE_IF_NEW | SHFL_CF_ACT_FAIL_IF_EXISTS
+                                     | SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_DENYNONE;
+
+                int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
+                LogFlow(("FS32_MKDIR: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
+                if (RT_SUCCESS(vrc))
+                {
+                    switch (pParams->Result)
+                    {
+                        case SHFL_FILE_CREATED:
+                            if (pParams->Handle != SHFL_HANDLE_NIL)
+                            {
+                                vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, pParams->Handle);
+                                AssertRC(vrc);
+                            }
+                            rc = NO_ERROR;
+                            break;
+
+                        case SHFL_FILE_EXISTS:
+                            rc = ERROR_ACCESS_DENIED;
+                            break;
+
+                        case SHFL_PATH_NOT_FOUND:
+                            rc = ERROR_PATH_NOT_FOUND;
+                            break;
+
+                        default:
+                        case SHFL_FILE_NOT_FOUND:
+                            rc = ERROR_FILE_NOT_FOUND;
+                            break;
+                    }
+                }
+                else if (vrc == VERR_ALREADY_EXISTS)
+                    rc = ERROR_ACCESS_DENIED;
+                else
+                    rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_FILE_NOT_FOUND);
+                VbglR0PhysHeapFree(pParams);
+            }
+            else
+                rc = ERROR_NOT_ENOUGH_MEMORY;
+            vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
+        }
+    }
+    else
+    {
+        Log(("FS32_MKDIR: EAs not supported\n"));
+        rc = ERROR_EAS_NOT_SUPPORTED;
+    }
+
+    RT_NOREF_PV(pCdFsi);
+    LogFlow(("FS32_RMDIR: returns %u\n", rc));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_RMDIR(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszDir, LONG offCurDirEnd)
+{
+    LogFlow(("FS32_RMDIR: pCdFsi=%p pCdFsd=%p pszDir=%p:{%s} offCurDirEnd=%d\n", pCdFsi, pCdFsd, pszDir, pszDir, offCurDirEnd));
+
+    /*
+     * Resolve the path.
+     */
+    PVBOXSFFOLDER pFolder;
+    PSHFLSTRING   pStrFolderPath;
+    APIRET rc = vboxSfOs2ResolvePath(pszDir, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
+    if (rc == NO_ERROR)
+    {
+        int vrc = VbglR0SfRemove(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, SHFL_REMOVE_DIR);
+        LogFlow(("FS32_RMDIR: VbglR0SfRemove -> %Rrc\n", rc));
+        if (RT_SUCCESS(vrc))
+            rc = NO_ERROR;
+        else
+            rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_ACCESS_DENIED);
+
+        vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
+    }
+
+    RT_NOREF_PV(pCdFsi);
+    LogFlow(("FS32_RMDIR: returns %u\n", rc));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_COPY(ULONG fFlags, PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszSrc, LONG offSrcCurDirEnd,
+          PCSZ pszDst, LONG offDstCurDirEnd, ULONG uNameType)
+{
+    LogFlow(("FS32_COPY: fFlags=%#x pCdFsi=%p pCdFsd=%p pszSrc=%p:{%s} offSrcCurDirEnd=%d pszDst=%p:{%s} offDstCurDirEnd=%d uNameType=%#x\n",
+             fFlags, pCdFsi, pCdFsd, pszSrc, pszSrc, offSrcCurDirEnd, pszDst, pszDst, offDstCurDirEnd, uNameType));
+    NOREF(fFlags); NOREF(pCdFsi); NOREF(pCdFsd); NOREF(pszSrc); NOREF(offSrcCurDirEnd);
+    NOREF(pszDst); NOREF(offDstCurDirEnd); NOREF(uNameType);
+
+    /* Let DOSCALL1.DLL do the work for us till we get a host side function for doing this. */
+    return ERROR_CANNOT_COPY;
+}
+
+
+DECLASM(APIRET)
+FS32_MOVE(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszSrc, LONG offSrcCurDirEnd, PCSZ pszDst, LONG offDstCurDirEnd, ULONG uNameType)
+{
+    LogFlow(("FS32_MOVE: pCdFsi=%p pCdFsd=%p pszSrc=%p:{%s} offSrcCurDirEnd=%d pszDst=%p:{%s} offDstcurDirEnd=%d uNameType=%#x\n",
+             pCdFsi, pCdFsd, pszSrc, pszSrc, offSrcCurDirEnd, pszDst, pszDst, offDstCurDirEnd, uNameType));
+
+    /*
+     * Resolve the source and destination paths and check that they
+     * refer to the same folder.
+     */
+    PVBOXSFFOLDER pSrcFolder;
+    PSHFLSTRING   pSrcFolderPath;
+    APIRET rc = vboxSfOs2ResolvePath(pszSrc, pCdFsd, offSrcCurDirEnd, &pSrcFolder, &pSrcFolderPath);
+    if (rc == NO_ERROR)
+    {
+        PVBOXSFFOLDER pDstFolder;
+        PSHFLSTRING   pDstFolderPath;
+        rc = vboxSfOs2ResolvePath(pszDst, pCdFsd, offDstCurDirEnd, &pDstFolder, &pDstFolderPath);
+        if (rc == NO_ERROR)
+        {
+            if (pSrcFolder == pDstFolder)
+            {
+                /*
+                 * Do the renaming.
+                 * Note! Requires 6.0.0beta2+ or 5.2.24+ host for renaming files.
+                 */
+                int vrc = VbglR0SfRename(&g_SfClient, &pSrcFolder->hHostFolder, pSrcFolderPath, pDstFolderPath,
+                                         SHFL_RENAME_FILE | SHFL_RENAME_DIR);
+                if (RT_SUCCESS(vrc))
+                    rc = NO_ERROR;
+                else
+                {
+                    Log(("FS32_MOVE: VbglR0SfRename failed: %Rrc\n", rc));
+                    rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_ACCESS_DENIED);
+                }
+            }
+            else
+            {
+                Log(("FS32_MOVE: source folder '%s' != destiation folder '%s'\n", pSrcFolder->szName, pDstFolder->szName));
+                rc = ERROR_NOT_SAME_DEVICE;
+            }
+            vboxSfOs2ReleasePathAndFolder(pDstFolderPath, pDstFolder);
+        }
+        vboxSfOs2ReleasePathAndFolder(pSrcFolderPath, pSrcFolder);
+    }
+
+    RT_NOREF_PV(pCdFsi); RT_NOREF_PV(uNameType);
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_DELETE(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszFile, LONG offCurDirEnd)
+{
+    LogFlow(("FS32_DELETE: pCdFsi=%p pCdFsd=%p pszFile=%p:{%s} offCurDirEnd=%d\n", pCdFsi, pCdFsd, pszFile, pszFile, offCurDirEnd));
+
+    /*
+     * Resolve the path.
+     */
+    PVBOXSFFOLDER pFolder;
+    PSHFLSTRING   pStrFolderPath;
+    APIRET rc = vboxSfOs2ResolvePath(pszFile, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
+    if (rc == NO_ERROR)
+    {
+        int vrc = VbglR0SfRemove(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, SHFL_REMOVE_FILE);
+        LogFlow(("FS32_DELETE: VbglR0SfRemove -> %Rrc\n", rc));
+        if (RT_SUCCESS(vrc))
+            rc = NO_ERROR;
+        else if (rc == VERR_FILE_NOT_FOUND)
+            rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_ACCESS_DENIED);
+
+        vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
+    }
+
+    RT_NOREF_PV(pCdFsi);
+    LogFlow(("FS32_DELETE: returns %u\n", rc));
+    return rc;
+}
+
+
+
+/**
+ * Worker for FS32_PATHINFO that handles file stat setting.
+ *
+ * @returns OS/2 status code
+ * @param   pFolder         The folder.
+ * @param   hHostFile       The host file handle.
+ * @param   fAttribs        The attributes to set.
+ * @param   pTimestamps     Pointer to the timestamps.  NULL if none should be
+ *                          modified.
+ * @param   pObjInfoBuf     Buffer to use when setting the attributes (host will
+ *                          return current info upon successful return).
+ */
+APIRET vboxSfOs2SetInfoCommonWorker(PVBOXSFFOLDER pFolder, SHFLHANDLE hHostFile, ULONG fAttribs,
+                                    PFILESTATUS pTimestamps, PSHFLFSOBJINFO pObjInfoBuf)
+{
+    /*
+     * Validate the data a little and convert it to host speak.
+     * When the date part is zero, the timestamp should not be updated.
+     */
+    RT_ZERO(*pObjInfoBuf);
+    uint16_t cDelta = vboxSfOs2GetLocalTimeDelta();
+
+    /** @todo should we validate attributes?   */
+    pObjInfoBuf->Attr.fMode = (fAttribs << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_OS2;
+
+    if (pTimestamps)
+    {
+        if (   *(uint16_t *)&pTimestamps->fdateCreation   != 0
+            && !vboxSfOs2DateTimeToTimeSpec(pTimestamps->fdateCreation,   pTimestamps->ftimeCreation,   cDelta, &pObjInfoBuf->BirthTime))
+        {
+            LogRel(("vboxSfOs2SetInfoCommonWorker: Bad creation timestamp: %u-%u-%u %u:%u:%u\n",
+                    pTimestamps->fdateCreation.year + 1980, pTimestamps->fdateCreation.month, pTimestamps->fdateCreation.day,
+                    pTimestamps->ftimeCreation.hours, pTimestamps->ftimeCreation.minutes, pTimestamps->ftimeCreation.twosecs * 2));
+            return ERROR_INVALID_PARAMETER;
+        }
+        if (   *(uint16_t *)&pTimestamps->fdateLastAccess != 0
+            && !vboxSfOs2DateTimeToTimeSpec(pTimestamps->fdateLastAccess, pTimestamps->ftimeLastAccess, cDelta, &pObjInfoBuf->AccessTime))
+        {
+            LogRel(("vboxSfOs2SetInfoCommonWorker: Bad last access timestamp: %u-%u-%u %u:%u:%u\n",
+                    pTimestamps->fdateLastAccess.year + 1980, pTimestamps->fdateLastAccess.month, pTimestamps->fdateLastAccess.day,
+                    pTimestamps->ftimeLastAccess.hours, pTimestamps->ftimeLastAccess.minutes, pTimestamps->ftimeLastAccess.twosecs * 2));
+            return ERROR_INVALID_PARAMETER;
+        }
+        if (   *(uint16_t *)&pTimestamps->fdateLastWrite  != 0
+            && !vboxSfOs2DateTimeToTimeSpec(pTimestamps->fdateLastWrite,  pTimestamps->ftimeLastWrite,  cDelta, &pObjInfoBuf->ModificationTime))
+        {
+            LogRel(("vboxSfOs2SetInfoCommonWorker: Bad last access timestamp: %u-%u-%u %u:%u:%u\n",
+                    pTimestamps->fdateLastWrite.year + 1980, pTimestamps->fdateLastWrite.month, pTimestamps->fdateLastWrite.day,
+                    pTimestamps->ftimeLastWrite.hours, pTimestamps->ftimeLastWrite.minutes, pTimestamps->ftimeLastWrite.twosecs * 2));
+            return ERROR_INVALID_PARAMETER;
+        }
+    }
+
+    /*
+     * Call the host to do the updating.
+     */
+    uint32_t cbBuf = sizeof(*pObjInfoBuf);
+    int vrc = VbglR0SfFsInfo(&g_SfClient, &pFolder->hHostFolder, hHostFile, SHFL_INFO_SET | SHFL_INFO_FILE,
+                             &cbBuf, (SHFLDIRINFO *)pObjInfoBuf);
+    LogFlow(("vboxSfOs2SetFileInfo: VbglR0SfFsInfo -> %Rrc\n", vrc));
+
+    if (RT_SUCCESS(vrc))
+        return NO_ERROR;
+    return vboxSfOs2ConvertStatusToOs2(vrc, ERROR_ACCESS_DENIED);
+}
+
+
+/**
+ * Worker for FS32_FILEATTRIBUTE and FS32_PATHINFO that handles setting stuff.
+ *
+ * @returns OS/2 status code.
+ * @param   pFolder             The folder.
+ * @param   pFolderPath         The path within the folder.
+ * @param   fAttribs            New file attributes.
+ * @param   pTimestamps         New timestamps.  May be NULL.
+ */
+static APIRET vboxSfOs2SetPathInfoWorker(PVBOXSFFOLDER pFolder, PSHFLSTRING pFolderPath, ULONG fAttribs, PFILESTATUS pTimestamps)
+
+{
+    /*
+     * In order to do anything we need to open the object.
+     */
+    APIRET rc;
+    SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
+    if (pParams)
+    {
+        RT_ZERO(*pParams);
+        pParams->CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW
+                             | SHFL_CF_ACCESS_ATTR_READWRITE | SHFL_CF_ACCESS_DENYNONE | SHFL_CF_ACCESS_NONE;
+
+        int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pFolderPath, pParams);
+        LogFlow(("vboxSfOs2SetPathInfoWorker: VbglR0SfCreate -> %Rrc Result=%d Handle=%#RX64 fMode=%#x\n",
+                 vrc, pParams->Result, pParams->Handle, pParams->Info.Attr.fMode));
+        if (   vrc == VERR_IS_A_DIRECTORY
+            || (   RT_SUCCESS(vrc)
+                && pParams->Handle == SHFL_HANDLE_NIL
+                && RTFS_IS_DIRECTORY(pParams->Info.Attr.fMode)))
+        {
+            RT_ZERO(*pParams);
+            pParams->CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW
+                                 | SHFL_CF_ACCESS_ATTR_READWRITE | SHFL_CF_ACCESS_DENYNONE | SHFL_CF_ACCESS_NONE;
+            vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pFolderPath, pParams);
+            LogFlow(("vboxSfOs2SetPathInfoWorker: VbglR0SfCreate#2 -> %Rrc Result=%d Handle=%#RX64 fMode=%#x\n",
+                     vrc, pParams->Result, pParams->Handle, pParams->Info.Attr.fMode));
+        }
+        if (RT_SUCCESS(vrc))
+        {
+            switch (pParams->Result)
+            {
+                case SHFL_FILE_EXISTS:
+                    if (pParams->Handle != SHFL_HANDLE_NIL)
+                    {
+                        /*
+                         * Join up with FS32_FILEINFO to do the actual setting.
+                         */
+                        rc = vboxSfOs2SetInfoCommonWorker(pFolder, pParams->Handle, fAttribs, pTimestamps, &pParams->Info);
+
+                        vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, pParams->Handle);
+                        AssertRC(vrc);
+                    }
+                    else
+                    {
+                        LogRel(("vboxSfOs2SetPathInfoWorker: No handle! fMode=%#x\n", pParams->Info.Attr.fMode));
+                        rc = ERROR_SYS_INTERNAL;
+                    }
+                    break;
+
+                case SHFL_PATH_NOT_FOUND:
+                    rc = ERROR_PATH_NOT_FOUND;
+                    break;
+
+                default:
+                case SHFL_FILE_NOT_FOUND:
+                    rc = ERROR_FILE_NOT_FOUND;
+                    break;
+            }
+        }
+        else
+            rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_FILE_NOT_FOUND);
+        VbglR0PhysHeapFree(pParams);
+    }
+    else
+        rc = ERROR_NOT_ENOUGH_MEMORY;
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_FILEATTRIBUTE(ULONG fFlags, PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszName, LONG offCurDirEnd, PUSHORT pfAttr)
+{
+    LogFlow(("FS32_FILEATTRIBUTE: fFlags=%#x pCdFsi=%p:{%#x,%s} pCdFsd=%p pszName=%p:{%s} offCurDirEnd=%d pfAttr=%p\n",
+             fFlags, pCdFsi, pCdFsi->cdi_hVPB, pCdFsi->cdi_curdir, pCdFsd, pszName, pszName, offCurDirEnd, pfAttr));
+    RT_NOREF(offCurDirEnd);
+
+    APIRET rc;
+    if (   fFlags == FA_RETRIEVE
+        || fFlags == FA_SET)
+    {
+        PVBOXSFFOLDER pFolder;
+        PSHFLSTRING   pStrFolderPath;
+        rc = vboxSfOs2ResolvePath(pszName, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
+        LogRel(("FS32_FILEATTRIBUTE: vboxSfOs2ResolvePath: -> %u pFolder=%p\n", rc, pFolder));
+        if (rc == NO_ERROR)
+        {
+            if (fFlags == FA_RETRIEVE)
+            {
+                /*
+                 * Query it.
+                 */
+                SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
+                if (pParams)
+                {
+                    RT_ZERO(*pParams);
+                    pParams->CreateFlags = SHFL_CF_LOOKUP;
+
+                    int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
+                    LogFlow(("FS32_FILEATTRIBUTE: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
+                    if (RT_SUCCESS(vrc))
+                    {
+                        switch (pParams->Result)
+                        {
+                            case SHFL_FILE_EXISTS:
+                                *pfAttr = (uint16_t)((pParams->Info.Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT);
+                                rc = NO_ERROR;
+                                break;
+
+                            case SHFL_PATH_NOT_FOUND:
+                                rc = ERROR_PATH_NOT_FOUND;
+                                break;
+
+                            default:
+                            case SHFL_FILE_NOT_FOUND:
+                                rc = ERROR_FILE_NOT_FOUND;
+                                break;
+                        }
+                    }
+                    else
+                        rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_FILE_NOT_FOUND);
+                    VbglR0PhysHeapFree(pParams);
+                }
+                else
+                    rc = ERROR_NOT_ENOUGH_MEMORY;
+            }
+            else
+            {
+                /*
+                 * Set the info.  Join paths with FS32_PATHINFO.
+                 */
+                rc = vboxSfOs2SetPathInfoWorker(pFolder, pStrFolderPath, *pfAttr, NULL);
+            }
+            vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
+        }
+    }
+    else
+    {
+        LogRel(("FS32_FILEATTRIBUTE: Unknwon flag value: %#x\n", fFlags));
+        rc = ERROR_NOT_SUPPORTED;
+    }
+    LogFlow(("FS32_FILEATTRIBUTE: returns %u\n", rc));
+    return rc;
+}
+
+
+/**
+ * Creates an empty full EA list given a GEALIST and info level.
+ *
+ * @returns OS/2 status code.
+ * @param   pEaOp           Kernel copy of the EA request with flattened pointers.
+ * @param   uLevel          The info level being queried.
+ * @param   pcbWritten      Where to return the length of the resulting list.  Optional.
+ * @param   poffError       User buffer address of EAOP.oError for reporting GEALIST issues.
+ */
+APIRET vboxSfOs2MakeEmptyEaListEx(PEAOP pEaOp, ULONG uLevel, uint32_t *pcbWritten, ULONG *poffError)
+{
+    ULONG  cbDstList;
+    APIRET rc;
+
+    /*
+     * Levels 8 and 5 are simple.
+     */
+    if (   pEaOp->fpGEAList == NULL
+        || uLevel == FI_LVL_EAS_FULL_8
+        || uLevel == FI_LVL_EAS_FULL_5)
+    {
+        Log2(("vboxSfOs2MakeEmptyEaList: #1\n"));
+        cbDstList = RT_UOFFSET_AFTER(FEALIST, cbList);
+        rc = NO_ERROR;
+    }
+    /*
+     * For levels 3 and 4 we have to do work when a request list is present.
+     */
+    else
+    {
+        ULONG cbGetEasLeft = 0;
+        rc = KernCopyIn(&cbGetEasLeft, &pEaOp->fpGEAList->cbList, sizeof(pEaOp->fpGEAList->cbList));
+        ULONG cbFullEasLeft = 0;
+        if (rc == NO_ERROR)
+            rc = KernCopyIn(&cbFullEasLeft, &pEaOp->fpFEAList->cbList, sizeof(cbFullEasLeft));
+        if (   rc == NO_ERROR
+            && cbGetEasLeft  >= sizeof(pEaOp->fpGEAList->cbList)
+            && cbFullEasLeft >= sizeof(pEaOp->fpFEAList->cbList))
+        {
+            cbGetEasLeft  -= sizeof(pEaOp->fpGEAList->cbList);
+            cbFullEasLeft -= sizeof(pEaOp->fpFEAList->cbList);
+
+            char *pszNameBuf = (char *)RTMemAlloc(256 + 1);
+            if (!pszNameBuf)
+                return ERROR_NOT_ENOUGH_MEMORY;
+            /* Start of no-return zone. */
+
+            uint8_t const *pbSrc = (uint8_t const *)&pEaOp->fpGEAList->list[0]; /* user buffer! */
+            uint8_t       *pbDst = (uint8_t       *)&pEaOp->fpFEAList->list[0]; /* user buffer! */
+            Log2(("vboxSfOs2MakeEmptyEaList: %p LB %#x -> %p LB %#x...\n", pbSrc, cbGetEasLeft, pbDst, cbFullEasLeft));
+            while (cbGetEasLeft > 0)
+            {
+                /*
+                 * pbSrc: GEA: BYTE cbName; char szName[];
+                 */
+                /* Get name length. */
+                uint8_t cbName = 0;
+                rc = KernCopyIn(&cbName, pbSrc, sizeof(cbName));
+                Log3(("vboxSfOs2MakeEmptyEaList: cbName=%#x rc=%u\n", cbName, rc));
+                if (rc != NO_ERROR)
+                    break;
+                pbSrc++;
+                cbGetEasLeft--;
+                if (cbName + 1 > cbGetEasLeft)
+                {
+                    cbDstList = pbSrc - 1 - (uint8_t *)pEaOp->fpGEAList;
+                    rc = KernCopyOut(poffError, &cbDstList, sizeof(pEaOp->oError));
+                    if (rc == NO_ERROR)
+                        rc = ERROR_EA_LIST_INCONSISTENT;
+                    Log(("vboxSfOs2MakeEmptyEaList: ERROR_EA_LIST_INCONSISTENT\n"));
+                    break;
+                }
+
+                /* Copy in name. */
+                rc = KernCopyIn(pszNameBuf, pbSrc, cbName + 1);
+                if (rc != NO_ERROR)
+                    break;
+                Log3(("vboxSfOs2MakeEmptyEaList: szName: %.*Rhxs\n", cbName + 1, pszNameBuf));
+                if ((char *)memchr(pszNameBuf, '\0', cbName) != &pszNameBuf[cbName])
+                {
+                    cbDstList = pbSrc - 1 - (uint8_t *)pEaOp->fpGEAList;
+                    rc = KernCopyOut(poffError, &cbDstList, sizeof(pEaOp->oError));
+                    if (rc == NO_ERROR)
+                        rc = ERROR_INVALID_EA_NAME;
+                    Log(("vboxSfOs2MakeEmptyEaList: ERROR_INVALID_EA_NAME\n"));
+                    break;
+                }
+
+                /* Skip input. */
+                cbGetEasLeft -= cbName + 1;
+                pbSrc        += cbName + 1;
+
+                /*
+                 * Construct and emit output.
+                 * Note! We should technically skip duplicates here, but who cares...
+                 */
+                if (cbName > 0)
+                {
+                    FEA Result;
+                    if (sizeof(Result) + cbName + 1 > cbFullEasLeft)
+                    {
+                        Log(("vboxSfOs2MakeEmptyEaList: ERROR_BUFFER_OVERFLOW (%#x vs %#x)\n", sizeof(Result) + cbName + 1, cbFullEasLeft));
+                        rc = ERROR_BUFFER_OVERFLOW;
+                        break;
+                    }
+                    cbFullEasLeft -= sizeof(Result) + cbName + 1;
+
+                    Result.fEA     = 0;
+                    Result.cbName  = cbName;
+                    Result.cbValue = 0;
+                    rc = KernCopyOut(pbDst, &Result, sizeof(Result));
+                    if (rc != NO_ERROR)
+                        break;
+                    pbDst += sizeof(Result);
+
+                    rc = KernCopyOut(pbDst, pszNameBuf, cbName + 1);
+                    if (rc != NO_ERROR)
+                        break;
+                    pbDst += cbName + 1;
+                }
+            } /* (while more GEAs) */
+
+            /* End of no-return zone. */
+            RTMemFree(pszNameBuf);
+
+            cbDstList = (uintptr_t)pbDst - (uintptr_t)pEaOp->fpFEAList;
+        }
+        else
+        {
+            if (rc == NO_ERROR)
+                rc = ERROR_BUFFER_OVERFLOW;
+            cbDstList = 0; /* oh, shut up. */
+        }
+
+    }
+
+    /* Set the list length. */
+    if (rc == NO_ERROR)
+        rc = KernCopyOut(&pEaOp->fpFEAList->cbList, &cbDstList, sizeof(pEaOp->fpFEAList->cbList));
+
+    if (pcbWritten)
+        *pcbWritten = cbDstList;
+
+    Log(("vboxSfOs2MakeEmptyEaList: return %u (cbDstList=%#x)\n", rc, cbDstList));
+    return rc;
+}
+
+
+
+/**
+ * Creates an empty full EA list given a GEALIST and info level.
+ *
+ * @returns OS/2 status code.
+ * @param   pEaOp           The EA request.  User buffer.
+ * @param   uLevel          The info level being queried.
+ */
+DECL_NO_INLINE(RT_NOTHING, APIRET)
+vboxSfOs2MakeEmptyEaList(PEAOP pEaOp, ULONG uLevel)
+{
+    /*
+     * Copy the user request into memory, do pointer conversion, and
+     * join extended function version.
+     */
+    EAOP EaOp = { NULL, NULL, 0 };
+    APIRET rc = KernCopyIn(&EaOp, pEaOp, sizeof(EaOp));
+    if (rc == NO_ERROR)
+    {
+        Log2(("vboxSfOs2MakeEmptyEaList: #0: %p %p %#x\n", EaOp.fpGEAList, EaOp.fpFEAList, EaOp.oError));
+        EaOp.fpFEAList = (PFEALIST)KernSelToFlat((uintptr_t)EaOp.fpFEAList);
+        EaOp.fpGEAList = (PGEALIST)KernSelToFlat((uintptr_t)EaOp.fpGEAList);
+        Log2(("vboxSfOs2MakeEmptyEaList: #0b: %p %p\n", EaOp.fpGEAList, EaOp.fpFEAList));
+
+        rc = vboxSfOs2MakeEmptyEaListEx(&EaOp, uLevel, NULL, &pEaOp->oError);
+    }
+    return rc;
+}
+
+
+/**
+ * Corrects the case of the given path.
+ *
+ * @returns OS/2 status code
+ * @param   pFolder         The folder.
+ * @param   pStrFolderPath  The path within the folder.
+ * @param   pszPath         The original path for figuring the drive letter or
+ *                          UNC part of the path.
+ * @param   pbData          Where to return the data (user address).
+ * @param   cbData          The maximum amount of data we can return.
+ */
+static int vboxSfOs2QueryCorrectCase(PVBOXSFFOLDER pFolder, PSHFLSTRING pStrFolderPath, const char *pszPath,
+                                     PBYTE pbData, ULONG cbData)
+{
+/** @todo do case correction.  Can do step-by-step dir info... but slow */
+    RT_NOREF(pFolder, pStrFolderPath, pszPath, pbData, cbData);
     return ERROR_NOT_SUPPORTED;
 }
 
 
-DECLASM(int)
-FS32_MKDIR(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszDir, USHORT iCurDirEnd,
-           PBYTE pbEABuf, ULONG fFlags)
-{
-    NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszDir); NOREF(iCurDirEnd); NOREF(pbEABuf); NOREF(fFlags);
+/**
+ * Copy out file status info.
+ *
+ * @returns OS/2 status code.
+ * @param   pbDst           User address to put the status info at.
+ * @param   cbDst           The size of the structure to produce.
+ * @param   uLevel          The info level of the structure to produce.
+ * @param   pSrc            The shared folder FS object info source structure.
+ * @note    Careful with stack, thus no-inlining.
+ */
+DECL_NO_INLINE(RT_NOTHING, APIRET)
+vboxSfOs2FileStatusFromObjInfo(PBYTE pbDst, ULONG cbDst, ULONG uLevel, SHFLFSOBJINFO const *pSrc)
+{
+    union
+    {
+        FILESTATUS      Fst;
+        FILESTATUS2     Fst2;
+        FILESTATUS3L    Fst3L;
+        FILESTATUS4L    Fst4L;
+    } uTmp;
+
+    int16_t cMinLocalTimeDelta = vboxSfOs2GetLocalTimeDelta();
+    vboxSfOs2DateTimeFromTimeSpec(&uTmp.Fst.fdateCreation,   &uTmp.Fst.ftimeCreation,   pSrc->BirthTime, cMinLocalTimeDelta);
+    vboxSfOs2DateTimeFromTimeSpec(&uTmp.Fst.fdateLastAccess, &uTmp.Fst.ftimeLastAccess, pSrc->AccessTime, cMinLocalTimeDelta);
+    vboxSfOs2DateTimeFromTimeSpec(&uTmp.Fst.fdateLastWrite,  &uTmp.Fst.ftimeLastWrite,  pSrc->ModificationTime, cMinLocalTimeDelta);
+    if (uLevel < FI_LVL_STANDARD_64)
+    {
+        uTmp.Fst.cbFile       = (uint32_t)RT_MIN(pSrc->cbObject,    UINT32_MAX);
+        uTmp.Fst.cbFileAlloc  = (uint32_t)RT_MIN(pSrc->cbAllocated, UINT32_MAX);
+        uTmp.Fst.attrFile     = (uint16_t)((pSrc->Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT);
+        if (uLevel == FI_LVL_STANDARD_EASIZE)
+            uTmp.Fst2.cbList = 0;
+    }
+    else
+    {
+        uTmp.Fst3L.cbFile      = pSrc->cbObject;
+        uTmp.Fst3L.cbFileAlloc = pSrc->cbAllocated;
+        uTmp.Fst3L.attrFile    = (pSrc->Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT;
+        uTmp.Fst4L.cbList      = 0;
+    }
+
+    return KernCopyOut(pbDst, &uTmp, cbDst);
+}
+
+
+
+/**
+ * Worker for FS32_PATHINFO that handles file stat queries.
+ *
+ * @returns OS/2 status code
+ * @param   pFolder         The folder.
+ * @param   pStrFolderPath  The path within the folder.
+ * @param   uLevel          The information level.
+ * @param   pbData          Where to return the data (user address).
+ * @param   cbData          The amount of data to produce.
+ */
+static APIRET vboxSfOs2QueryPathInfo(PVBOXSFFOLDER pFolder, PSHFLSTRING pStrFolderPath, ULONG uLevel, PBYTE pbData, ULONG cbData)
+{
+    APIRET rc;
+    SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
+    if (pParams)
+    {
+        RT_ZERO(*pParams);
+        pParams->CreateFlags = SHFL_CF_LOOKUP;
+
+        int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
+        LogFlow(("FS32_PATHINFO: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
+        if (RT_SUCCESS(vrc))
+        {
+            switch (pParams->Result)
+            {
+                case SHFL_FILE_EXISTS:
+                    switch (uLevel)
+                    {
+                        /*
+                         * Produce the desired file stat data.
+                         */
+                        case FI_LVL_STANDARD:
+                        case FI_LVL_STANDARD_EASIZE:
+                        case FI_LVL_STANDARD_64:
+                        case FI_LVL_STANDARD_EASIZE_64:
+                            rc = vboxSfOs2FileStatusFromObjInfo(pbData, cbData, uLevel, &pParams->Info);
+                            break;
+
+                        /*
+                         * We don't do EAs and we "just" need to return no-EAs.
+                         * However, that's not as easy as you might think.
+                         */
+                        case FI_LVL_EAS_FROM_LIST:
+                        case FI_LVL_EAS_FULL:
+                        case FI_LVL_EAS_FULL_5:
+                        case FI_LVL_EAS_FULL_8:
+                            rc = vboxSfOs2MakeEmptyEaList((PEAOP)pbData, uLevel);
+                            break;
+
+                        default:
+                            AssertFailed();
+                            rc = ERROR_GEN_FAILURE;
+                            break;
+                    }
+                    break;
+
+                case SHFL_PATH_NOT_FOUND:
+                    rc = ERROR_PATH_NOT_FOUND;
+                    break;
+
+                default:
+                case SHFL_FILE_NOT_FOUND:
+                    rc = ERROR_FILE_NOT_FOUND;
+                    break;
+            }
+        }
+        else
+            rc = vboxSfOs2ConvertStatusToOs2(rc, ERROR_FILE_NOT_FOUND);
+        VbglR0PhysHeapFree(pParams);
+    }
+    else
+        rc = ERROR_NOT_ENOUGH_MEMORY;
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_PATHINFO(USHORT fFlags, PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszPath, LONG offCurDirEnd,
+              ULONG uLevel, PBYTE pbData, ULONG cbData)
+{
+    LogFlow(("FS32_PATHINFO: fFlags=%#x pCdFsi=%p:{%#x,%s} pCdFsd=%p pszPath=%p:{%s} offCurDirEnd=%d uLevel=%u pbData=%p cbData=%#x\n",
+             fFlags, pCdFsi, pCdFsi->cdi_hVPB, pCdFsi->cdi_curdir, pCdFsd, pszPath, pszPath, offCurDirEnd, uLevel, pbData, cbData));
+
+    /*
+     * Check the level.
+     *
+     * Note! You would think this is FIL_STANDARD, FIL_QUERYEASIZE,
+     *       FIL_QUERYEASFROMLISTL and such.  However, there are several levels
+     *       (4/14, 6/16, 7/17, 8/18) that are not defined in os2.h and then
+     *       there and FIL_QUERYFULLNAME that is used very between the kernel
+     *       and the FSD so the kernel can implement DosEnumAttributes.
+     *
+     * Note! DOSCALL1.DLL has code for converting FILESTATUS to FILESTATUS3
+     *       and FILESTATUS2 to FILESTATUS4 as needed.  We don't need to do this.
+     *       It also has weird code for doubling the FILESTATUS2.cbList value
+     *       for no apparent reason.
+     */
+    ULONG cbMinData;
+    switch (uLevel)
+    {
+        case FI_LVL_STANDARD:
+            cbMinData = sizeof(FILESTATUS);
+            AssertCompileSize(FILESTATUS,  0x16);
+            break;
+        case FI_LVL_STANDARD_64:
+            cbMinData = sizeof(FILESTATUS3L);
+            AssertCompileSize(FILESTATUS3L, 0x20); /* cbFile and cbFileAlloc are misaligned. */
+            break;
+        case FI_LVL_STANDARD_EASIZE:
+            cbMinData = sizeof(FILESTATUS2);
+            AssertCompileSize(FILESTATUS2, 0x1a);
+            break;
+        case FI_LVL_STANDARD_EASIZE_64:
+            cbMinData = sizeof(FILESTATUS4L);
+            AssertCompileSize(FILESTATUS4L, 0x24); /* cbFile and cbFileAlloc are misaligned. */
+            break;
+        case FI_LVL_EAS_FROM_LIST:
+        case FI_LVL_EAS_FULL:
+        case FI_LVL_EAS_FULL_5:
+        case FI_LVL_EAS_FULL_8:
+            cbMinData = sizeof(EAOP);
+            break;
+        case FI_LVL_VERIFY_PATH:
+        case FI_LVL_CASE_CORRECT_PATH:
+            cbMinData = 1;
+            break;
+        default:
+            LogRel(("FS32_PATHINFO: Unsupported info level %u!\n", uLevel));
+            return ERROR_INVALID_LEVEL;
+    }
+    if (cbData < cbMinData || pbData == NULL)
+    {
+        Log(("FS32_PATHINFO: ERROR_BUFFER_OVERFLOW (cbMinData=%#x, cbData=%#x, pszPath=%s)\n", cbMinData, cbData, pszPath));
+        return ERROR_BUFFER_OVERFLOW;
+    }
+
+    /*
+     * Resolve the path to a folder and folder path.
+     */
+    PVBOXSFFOLDER pFolder;
+    PSHFLSTRING   pStrFolderPath;
+    APIRET rc = vboxSfOs2ResolvePath(pszPath, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
+    LogFlow(("FS32_PATHINFO: vboxSfOs2ResolvePath: -> %u pFolder=%p\n", rc, pFolder));
+    if (rc == NO_ERROR)
+    {
+        /*
+         * Query information.
+         */
+        if (fFlags == PI_RETRIEVE)
+        {
+            if (   uLevel != FI_LVL_VERIFY_PATH
+                && uLevel != FI_LVL_CASE_CORRECT_PATH)
+                rc = vboxSfOs2QueryPathInfo(pFolder, pStrFolderPath, uLevel, pbData, cbMinData);
+            else if (uLevel == FI_LVL_VERIFY_PATH)
+                rc = NO_ERROR; /* vboxSfOs2ResolvePath should've taken care of this already */
+            else
+                rc = vboxSfOs2QueryCorrectCase(pFolder, pStrFolderPath, pszPath, pbData, cbData);
+        }
+        /*
+         * Update information.
+         */
+        else if (   fFlags == PI_SET
+                 || fFlags == (PI_SET | PI_WRITE_THRU))
+        {
+            if (   uLevel == FI_LVL_STANDARD
+                || uLevel == FI_LVL_STANDARD_64)
+            {
+                /* Read in the data and join paths with FS32_FILEATTRIBUTE: */
+                PFILESTATUS pDataCopy = (PFILESTATUS)VbglR0PhysHeapAlloc(cbMinData);
+                if (pDataCopy)
+                {
+                    rc = KernCopyIn(pDataCopy, pbData, cbMinData);
+                    if (rc == NO_ERROR)
+                        rc = vboxSfOs2SetPathInfoWorker(pFolder, pStrFolderPath,
+                                                        uLevel == FI_LVL_STANDARD
+                                                        ? (ULONG)pDataCopy->attrFile
+                                                        : ((PFILESTATUS3L)pDataCopy)->attrFile,
+                                                        (PFILESTATUS)pDataCopy);
+                    VbglR0PhysHeapFree(pDataCopy);
+                }
+                else
+                    rc = ERROR_NOT_ENOUGH_MEMORY;
+            }
+            else if (uLevel == FI_LVL_STANDARD_EASIZE)
+                rc = ERROR_EAS_NOT_SUPPORTED;
+            else
+                rc = ERROR_INVALID_LEVEL;
+        }
+        else
+        {
+            LogRel(("FS32_PATHINFO: Unknown flags value: %#x (path: %s)\n", fFlags, pszPath));
+            rc = ERROR_INVALID_PARAMETER;
+        }
+        vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
+    }
+    RT_NOREF_PV(pCdFsi);
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_MOUNT(USHORT fFlags, PVPFSI pvpfsi, PVBOXSFVP pVpFsd, USHORT hVPB, PCSZ pszBoot)
+{
+    NOREF(fFlags); NOREF(pvpfsi); NOREF(pVpFsd); NOREF(hVPB); NOREF(pszBoot);
     return ERROR_NOT_SUPPORTED;
 }
 
-
-DECLASM(int)
-FS32_RMDIR(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszDir, USHORT iCurDirEnd)
-{
-    NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszDir); NOREF(iCurDirEnd);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_COPY(USHORT fFlags, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszSrc, USHORT iSrcCurDirEnd,
-          PCSZ pszDst, USHORT iDstCurDirEnd, USHORT uNameType)
-{
-    NOREF(fFlags); NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszSrc); NOREF(iSrcCurDirEnd);
-    NOREF(pszDst); NOREF(iDstCurDirEnd); NOREF(uNameType);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_MOVE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszSrc, USHORT iSrcCurDirEnd,
-          PCSZ pszDst, USHORT iDstCurDirEnd, USHORT uNameType)
-{
-    NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszSrc); NOREF(iSrcCurDirEnd); NOREF(pszDst); NOREF(iDstCurDirEnd); NOREF(uNameType);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_DELETE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszFile, USHORT iCurDirEnd)
-{
-    NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszFile); NOREF(iCurDirEnd);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_FILEATTRIBUTE(ULONG fFlags, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, PUSHORT pfAttr)
-{
-    NOREF(fFlags); NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszName); NOREF(iCurDirEnd); NOREF(pfAttr);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_PATHINFO(USHORT fFlags, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd,
-              USHORT uLevel, PBYTE pbData, USHORT cbData)
-{
-    NOREF(fFlags); NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszName); NOREF(iCurDirEnd); NOREF(uLevel); NOREF(pbData); NOREF(cbData);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_MOUNT(USHORT fFlags, PVPFSI pvpfsi, PVBOXSFVP pvpfsd, USHORT hVPB, PCSZ pszBoot)
-{
-    NOREF(fFlags); NOREF(pvpfsi); NOREF(pvpfsd); NOREF(hVPB); NOREF(pszBoot);
-    return ERROR_NOT_SUPPORTED;
-}
-
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.def
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.def	(revision 75336)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.def	(revision 75337)
@@ -85,8 +85,8 @@
     FS_SETSWAP
     FS_SHUTDOWN
+    FS_VERIFYUNCNAME
     FS_WRITE
 
     ; 32-bit entry points.
-    FS32_CHGFILEPTR
     FS32_CHGFILEPTRL
     FS32_READ
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFA.asm
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFA.asm	(revision 75336)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFA.asm	(revision 75337)
@@ -30,19 +30,187 @@
 
 
-;*******************************************************************************
-;* Header Files                                                                *
-;*******************************************************************************
+;*********************************************************************************************************************************
+;*  Header Files                                                                                                                 *
+;*********************************************************************************************************************************
 %define RT_INCL_16BIT_SEGMENTS
 %include "iprt/asmdefs.mac"
 %include "iprt/err.mac"
+%include "iprt/x86.mac"
+%include "iprt/formats/dwarf.mac"
 %include "VBox/VBoxGuest.mac"
 
 
-;*******************************************************************************
-;*  Defined Constants And Macros                                               *
-;*******************************************************************************
+;*********************************************************************************************************************************
+;*  Dwarf constants and macros                                                                                                   *
+;*********************************************************************************************************************************
+;; enable dwarf debug info
+%define WITH_DWARF 1
+
+;; Emits a LEB128 (signed) constant (%1) - limited range.
+%macro DWARF_LEB128 1
+%if %1 >= 0
+ %if %1 < 64
+        db  %1
+ %else
+        db  (%1 & 0x7f) | 0x80
+        db  (%1 >> 7)   & 0x7f
+ %endif
+%else
+ %if %1 > -64
+        db  (%1 & 0x3f) | 0x40
+ %else
+        db  (%1 & 0x7f) | 0x80
+        db  ((%1 >> 7)  & 0x7f) | 0x40
+ %endif
+%endif
+%endmacro
+
+;; Emits a ULEB128 (unsigned) constant (%1) - limited range.
+%macro DWARF_ULEB128 1
+%if %1 < 0x80
+        db  %1
+%elif %1 < 0x4000
+        db  (%1 & 0x7f) | 0x80
+        db  (%1 >> 7)
+%elif %1 < 0x200000
+        db  ((%1)       & 0x7f) | 0x80
+        db  ((%1 >> 7)  & 0x7f) | 0x80
+        db  ((%1 >> 14))
+%else
+ %error out of range: %1
+%endif
+%endmacro
+
+;; Emits a pair of ULEB128 constants.  Useful for .debug_abbrev.
+%macro DWARF_ULEB128_PAIR 2
+        DWARF_ULEB128 %1
+        DWARF_ULEB128 %2
+%endmacro
+
+
+;; defines a CFA offset by register (%1) + unsigned offset (%2).
+%macro CFA_DEF_CFA 2
+        db      DW_CFA_def_cfa
+        DWARF_ULEB128 %1
+        DWARF_ULEB128 %2
+%endmacro
+
+;; defines the register (%1) value as CFA + unsigned offset (%2) * data_alignment_factor.
+%macro CFA_VAL_OFFSET 2
+        db      DW_CFA_val_offset
+        DWARF_ULEB128 %1
+        DWARF_ULEB128 %2
+%endmacro
+
+;; defines the register (%1) save location as CFA + unsigned offset (%2) * data_alignment_factor.
+%macro CFA_OFFSET 2
+%if %1 < 0x40
+        db      DW_CFA_offset | %1
+%else
+        db      DW_CFA_offset_extended
+        DWARF_ULEB128 %1
+%endif
+        DWARF_ULEB128 %2
+%endmacro
+
+%define MY_ABBREV_CODE_CU       2
+%define MY_ABBREV_CODE_LABEL    3
+
+
+;; Emits a debug info for a label in CODE16.
+;; @param %1    symbol
+%macro DWARF_LABEL_CODE16 1
+%ifdef WITH_DWARF
+segment _debug_info
+        DWARF_ULEB128       MY_ABBREV_CODE_LABEL
+        dd                  %1 wrt CODE16
+        db                  2 ; Hardcoded CODE16 number.
+%defstr tmp_str_conversion %1
+        db                  tmp_str_conversion, 0
+%endif
+segment CODE16
+%endmacro
+
+
+;; Emits a debug info for a label in CODE32.
+;; @param %1    symbol
+%macro DWARF_LABEL_TEXT32 1
+%ifdef WITH_DWARF
+segment _debug_info
+        DWARF_ULEB128       MY_ABBREV_CODE_LABEL
+        dd                  %1 wrt TEXT32
+        db                  3 ; Hardcoded TEXT32 number.
+%defstr tmp_str_conversion %1
+        db                  tmp_str_conversion, 0
+%endif
+segment TEXT32
+%endmacro
+
+
+
+;*********************************************************************************************************************************
+;*  Additional Segment definitions.                                                                                                         *
+;*********************************************************************************************************************************
+%ifdef WITH_DWARF       ; We need to use '_debug_xxx' + dotseg.exe here rather than '.debug_xxx' because some nasm crap.
+segment _debug_frame       public CLASS=DWARF align=4 use32
+g_cie_thunk_back:
+        dd      (g_cie_thunk_end - g_cie_thunk_back - 4)    ; Length
+        dd      0xffffffff                      ; I'm a CIE.
+        db      4                               ; DwARF v4
+        db      0                               ; Augmentation.
+        db      4                               ; Address size.
+        db      4                               ; Segment size.
+        DWARF_LEB128  1                         ; Code alignment factor.
+        DWARF_LEB128 -1                         ; Data alignment factor.
+        DWARF_ULEB128   DWREG_X86_RA            ; Return register column.
+        CFA_DEF_CFA     DWREG_X86_EBP, 8        ; cfa = EBP + 8
+        CFA_OFFSET      DWREG_X86_EBP, 8        ; EBP = [CFA - 8]
+        CFA_OFFSET      DWREG_X86_ESP, 8+10     ; SS  = [CFA - 8 - 10]
+        CFA_OFFSET      DWREG_X86_SS,  8+6      ; SS  = [CFA - 8 - 6]
+        CFA_OFFSET      DWREG_X86_ES,  8+4      ; ES  = [CFA - 8 - 4]
+        CFA_OFFSET      DWREG_X86_DS,  8+2      ; DS  = [CFA - 8 - 2]
+        CFA_OFFSET      DWREG_X86_CS,  2        ; CS  = [CFA - 2]
+;        CFA_OFFSET      DWREG_X86_RA,  4        ; RetAddr = [CFA - 4]
+        align   4, db DW_CFA_nop
+g_cie_thunk_end:
+
+
+segment _debug_abbrev       public CLASS=DWARF align=1 use32
+g_abbrev_compile_unit:
+        DWARF_ULEB128       MY_ABBREV_CODE_CU
+        DWARF_ULEB128_PAIR  DW_TAG_compile_unit, DW_CHILDREN_yes
+        DWARF_ULEB128_PAIR  DW_AT_name, DW_FORM_string
+        db                  0, 0 ; the end.
+g_abbrev_label:
+        db                  MY_ABBREV_CODE_LABEL
+        DWARF_ULEB128_PAIR  DW_TAG_label, DW_CHILDREN_no
+        DWARF_ULEB128_PAIR  DW_AT_low_pc, DW_FORM_addr
+        DWARF_ULEB128_PAIR  DW_AT_segment, DW_FORM_data1
+        DWARF_ULEB128_PAIR  DW_AT_name, DW_FORM_string
+        db                  0, 0 ; the end.
+
+
+segment _debug_info         public CLASS=DWARF align=1 use32
+g_dwarf_compile_unit_header:
+        dd                  g_dwarf_compile_unit_end - g_dwarf_compile_unit_header - 4
+        dw                  2           ; DWARF v2
+        dd                  g_abbrev_compile_unit wrt _debug_abbrev
+        db                  4           ; address_size
+.compile_unit_die:
+        db                  MY_ABBREV_CODE_CU
+        db                  __FILE__, 0
+
+segment TEXT32
+%endif ; WITH_DWARF
+
+
+
+;*********************************************************************************************************************************
+;*  Defined Constants And Macros                                                                                                 *
+;*********************************************************************************************************************************
 %define ERROR_NOT_SUPPORTED         50
 %define ERROR_INVALID_PARAMETER     87
 %define DevHlp_AttachDD             2ah
+
 
 ;;
@@ -53,16 +221,19 @@
 %%my_dbg_str: db %1, 0ah, 0
 segment CODE16
-    push    ax
-    mov     ax, %%my_dbg_str
-    call    NAME(dbgstr16)
-    pop     ax
+        push    ax
+        mov     ax, %%my_dbg_str
+        call    NAME(dbgstr16)
+        pop     ax
 %endif
 %endmacro
 
+;%define RT_STR_QUOTE    "
+;%define RT_STR(a_Label)       RT_STR_QUOTE a_Label RT_STR_QUOTE
 
 %macro VBOXSF_EP16_BEGIN 2
+DWARF_LABEL_CODE16 %1
 global %1
 %1:
-    DEBUG_STR16 {'VBoxSF: ', %2}
+        ;DEBUG_STR16 {'VBoxSF: ', %2}
 
 %endmacro
@@ -74,5 +245,5 @@
 
 ;;
-; Used to taking us to 32-bit and reserving a parameter frame.
+; Used in a 16-bit entrypoint for taking us to 32-bit and reserving a parameter frame.
 ;
 ; @param    %1      The function name
@@ -80,29 +251,41 @@
 ;
 %macro VBOXSF_TO_32 2
-    ; prologue
-    push    ebp
-    mov     ebp, esp                    ; bp
-    push    ds                          ; bp - 2
-    push    es                          ; bp - 4
-
-    ; Reserve the 32-bit parameter and align the stack on a 16 byte
-    ; boundary to make GCC really happy.
-    sub     sp, %2
-    and     sp, 0fff0h
-
-    ;jmp far dword NAME(%i %+ _32) wrt FLAT
-    db      066h
-    db      0eah
-    dd      NAME(%1 %+ _32) ;wrt FLAT
-    dw      TEXT32 wrt FLAT
+        ; prologue
+%ifdef DEBUG
+ %ifndef WITH_DWARF
+        inc     ebp
+ %endif
+%endif
+        push    ebp
+        mov     ebp, esp                    ; bp
+        push    ds                          ; bp - 2
+        push    es                          ; bp - 4
+%ifdef WITH_DWARF
+        push    ss                          ; bp - 6
+        lea     eax, [esp + 3*2 + 4 + 4]    ; bp - 10: return esp (16-bit)
+        push    eax
+%endif
+
+        ; Reserve the 32-bit parameter and align the stack on a 16 byte
+        ; boundary to make GCC really happy.
+        sub     sp, %2
+        and     sp, 0fff0h
+
+        ;jmp far dword NAME(%i %+ _32) wrt FLAT
+        db      066h
+        db      0eah
+        dd      NAME(%1 %+ _32) ;wrt FLAT
+        dw      TEXT32 wrt FLAT
 segment TEXT32
 GLOBALNAME %1 %+ _32
-    mov     ax, DATA32 wrt FLAT
-    mov     ds, ax
-    mov     es, ax
-
-    call    KernThunkStackTo32
-
-%endmacro VBOXSF_TO_32 1
+DWARF_LABEL_TEXT32 NAME(%1 %+ _32)
+        mov     ax, DATA32 wrt FLAT
+        mov     ds, ax
+        mov     es, ax
+
+        call    KernThunkStackTo32
+.vboxsf_to_32_end:
+
+%endmacro ; VBOXSF_TO_32
 
 ;;
@@ -112,38 +295,65 @@
 ;
 %macro VBOXSF_TO_16 1
-    push    eax
-    call    KernThunkStackTo16
-    pop     eax
-
-    ;jmp far dword NAME(%1 %+ _16) wrt CODE16
-    db      066h
-    db      0eah
-    dw      NAME(%1 %+ _16) wrt CODE16
-    dw      CODE16
+.vboxsf_to_16_start:
+        push    eax
+        call    KernThunkStackTo16
+        pop     eax
+
+        ;jmp far dword NAME(%1 %+ _16) wrt CODE16
+        db      066h
+        db      0eah
+        dw      NAME(%1 %+ _16) wrt CODE16
+        dw      CODE16
+.vboxsf_to_16_done_32:
+%ifdef WITH_DWARF
+segment _debug_frame
+.fde_start:
+        dd      (.fde_end - .fde_start) - 4
+        dd      g_cie_thunk_back wrt _debug_frame
+        dd      2 ; TEXT32 idx
+        dd      NAME(%1 %+ _32) wrt TEXT32
+        dd      .vboxsf_to_16_done_32 - NAME(%1 %+ _32)
+        db      DW_CFA_advance_loc | 4
+        db      DW_CFA_advance_loc | 2
+        db      DW_CFA_advance_loc | 2
+        db      DW_CFA_advance_loc | 5
+        db      DW_CFA_advance_loc2                         ; Hack to easily cover the parameter conversion code.
+        dw      .vboxsf_to_16_start - .vboxsf_to_32_end
+        db      DW_CFA_advance_loc | 1
+        db      DW_CFA_advance_loc | 5
+        db      DW_CFA_advance_loc | 1
+        db      DW_CFA_advance_loc | 6
+        align 4, db DW_CFA_nop
+.fde_end:
+ %endif ; WITH_DWARF
 segment CODE16
 GLOBALNAME %1 %+ _16
-
-    ; Epilogue
-    lea     sp, [bp - 4h]
-    pop     es
-    pop     ds
-    mov     esp, ebp
-    pop     ebp
+DWARF_LABEL_CODE16 NAME(%1 %+ _16)
+
+        ; Epilogue
+        lea     sp, [bp - 4h]
+        pop     es
+        pop     ds
+        mov     esp, ebp
+        pop     ebp
+%ifdef DEBUG
+ %ifndef WITH_DWARF
+        dec     ebp
+ %endif
+%endif
 %endmacro
 
 ;;
-; Thunks the given 16:16 pointer to a flat pointer.
-;
-; @param    %1      The negated ebp offset of the input.
+; Thunks the given 16:16 pointer to a flat pointer, NULL is returned as NULL.
+;
+; @param    %1      The ebp offset of the input.
 ; @param    %2      The esp offset of the output.
 ; @users    eax, edx, ecx
 ;
 %macro VBOXSF_FARPTR_2_FLAT 2
-    movzx   eax, word [ebp - (%1) + 2]
-    push    eax
-    call    KernSelToFlat
-    movzx   edx, word [ebp - (%1)]
-    add     eax, edx
-    mov     [esp + (%2)], eax
+        push    dword [ebp + (%1)]
+        call    KernSelToFlat
+        add     esp, 4h
+        mov     [esp + (%2)], eax
 %endmacro
 
@@ -151,15 +361,19 @@
 ; Thunks the given 16:16 struct sffsd pointer to a flat pointer.
 ;
-; @param    %1      The negated ebp offset of the input.
+; @param    %1      The ebp offset of the input.
 ; @param    %2      The esp offset of the output.
 ; @users    eax, ecx
 ;
 %macro VBOXSF_PSFFSD_2_FLAT 2
-    lds     cx, [ebp - (%1)]
-    and     ecx, 0ffffh
-    mov     eax, dword [ecx]
-    mov     cx, DATA32 wrt FLAT
-    mov     [esp + (%2)], eax
-    mov     ds, cx
+%if 1 ; optimize later if we can.
+        VBOXSF_FARPTR_2_FLAT %1, %2
+%else
+        lds     cx, [ebp + (%1)]
+        and     ecx, 0ffffh
+        mov     eax, dword [ecx]
+        mov     cx, DATA32 wrt FLAT
+        mov     [esp + (%2)], eax
+        mov     ds, cx
+%endif
 %endmacro
 
@@ -168,15 +382,19 @@
 ; Thunks the given 16:16 struct cdfsd pointer to a flat pointer.
 ;
-; @param    %1      The negated ebp offset of the input.
+; @param    %1      The ebp offset of the input.
 ; @param    %2      The esp offset of the output.
 ; @users    eax, ecx
 ;
 %macro VBOXSF_PCDFSD_2_FLAT 2
-    lds     cx, [ebp - (%1)]
-    and     ecx, 0ffffh
-    mov     eax, dword [ecx]
-    mov     cx, DATA32 wrt FLAT
-    mov     [esp + (%2)], eax
-    mov     ds, cx
+%if 1 ; optimize later if possible.
+        VBOXSF_FARPTR_2_FLAT %1, %2
+%else
+        lds     cx, [ebp + (%1)]
+        and     ecx, 0ffffh
+        mov     eax, dword [ecx]
+        mov     cx, DATA32 wrt FLAT
+        mov     [esp + (%2)], eax
+        mov     ds, cx
+%endif
 %endmacro
 
@@ -184,22 +402,107 @@
 ; Thunks the given 16:16 struct fsfsd pointer to a flat pointer.
 ;
-; @param    %1      The negated ebp offset of the input.
+; @param    %1      The ebp offset of the input.
 ; @param    %2      The esp offset of the output.
 ; @users    eax, ecx
 ;
 %macro VBOXSF_PFSFSD_2_FLAT 2
-    lds     cx, [ebp - (%1)]
-    and     ecx, 0ffffh
-    mov     eax, dword [ecx]
-    mov     cx, DATA32 wrt FLAT
-    mov     [esp + (%2)], eax
-    mov     ds, cx
+%if 1 ; optimize later if possible.
+        VBOXSF_FARPTR_2_FLAT %1, %2
+%else
+        lds     cx, [ebp + (%1)]
+        and     ecx, 0ffffh
+        mov     eax, dword [ecx]
+        mov     cx, DATA32 wrt FLAT
+        mov     [esp + (%2)], eax
+        mov     ds, cx
+%endif
 %endmacro
 
 
-
-;*******************************************************************************
-;* External Symbols                                                            *
-;*******************************************************************************
+;;
+; Used for taking us from 32-bit and reserving a parameter frame.
+;
+; @param    %1      The function name
+; @param    %2      The number of bytes to reserve
+;
+%macro VBOXSF_FROM_32 2
+        ; prologue
+        push    ebp
+        mov     ebp, esp                    ; ebp
+        push    ds                          ; ebp - 4
+        push    es                          ; ebp - 8
+        push    ebx                         ; ebp - 0ch
+        push    esi                         ; ebp - 10h
+        push    edi                         ; ebp - 14h
+
+        ; Reserve the 32-bit parameter
+        sub     esp, %2
+
+        call    KernThunkStackTo16
+
+        ;jmp far dword NAME(%1 %+ _16) wrt CODE16
+        db      066h
+        db      0eah
+        dw      NAME(%1 %+ _16) wrt CODE16
+        dw      CODE16
+.vboxsf_from_32_end:
+
+segment CODE16
+GLOBALNAME %1 %+ _16
+DWARF_LABEL_CODE16 NAME(%1 %+ _16)
+
+%endmacro
+
+
+;;
+; Partially countering VBOXSF_FROM_32:
+; Take us back to 32-bit mode, but don't do the epilogue stuff.
+;
+; @param    %1      The function name
+;
+%macro VBOXSF_FROM_16_SWITCH 1
+.vboxsf_from_16_start:
+        ;jmp far dword NAME(%i %+ _32) wrt FLAT
+        db      066h
+        db      0eah
+        dd      NAME(%1 %+ _32) ;wrt FLAT
+        dw      TEXT32 wrt FLAT
+.vboxsf_from_16_done_16:
+
+segment TEXT32
+GLOBALNAME %1 %+ _32
+DWARF_LABEL_TEXT32 NAME(%1 %+ _32)
+
+        push    eax
+        call    KernThunkStackTo32
+        mov     ax, DATA32 wrt FLAT
+        mov     ds, eax
+        mov     es, eax
+        pop     eax
+%endmacro
+
+
+;;
+; Does the remaining recovery after VBOXSF_FROM_32.
+;
+%macro VBOXSF_FROM_16_EPILOGUE 0
+        ; Epilogue
+        lea     esp, [ebp - 14h]
+        pop     edi
+        pop     esi
+        pop     ebx
+        pop     es
+        pop     ds
+        cld
+        mov     esp, ebp
+        pop     ebp
+%endmacro
+
+
+
+
+;*********************************************************************************************************************************
+;*  External Symbols                                                                                                             *
+;*********************************************************************************************************************************
 segment CODE32
 extern KernThunkStackTo32
@@ -208,4 +511,5 @@
 segment CODE16
 extern FSH_FORCENOSWAP
+extern FSH_GETVOLPARM
 extern DOS16WRITE
 
@@ -252,4 +556,5 @@
 extern NAME(FS32_SETSWAP)
 extern NAME(FS32_SHUTDOWN)
+extern NAME(FS32_VERIFYUNCNAME)
 extern FS32_WRITE
 
@@ -267,5 +572,5 @@
 global FS_NAME
 FS_NAME:
-    db 'VBOXSF',0
+        db 'VBOXSF',0
 
 ;;
@@ -284,5 +589,5 @@
 FS_ATTRIBUTE:
 FS32_ATTRIBUTE:
-    dd FSA_REMOTE + FSA_LARGEFILE ;+ FSA_LVL7 + FSA_LOCK
+        dd FSA_REMOTE + FSA_LARGEFILE + FSA_UNC + FSA_LVL7 ;+ FSA_LOCK
 
 ;; 64-bit mask.
@@ -291,11 +596,11 @@
 global FS_MPSAFEFLAGS2
 FS_MPSAFEFLAGS2:
-    dd  0
-    dd  0
+        dd  1 | (1<<6)
+        dd  0
 
 ;;
 ; Set after VBoxSFR0Init16Bit has been called.
 GLOBALNAME g_fDoneRing0
-    db 0
+        db 0
 
 align 4
@@ -304,36 +609,36 @@
 ; (This is set by FS_INIT.)
 GLOBALNAME g_fpfnDevHlp
-    dd 0
+        dd 0
 
 ;;
 ; Whether initialization should be verbose or quiet.
 GLOBALNAME g_fVerbose
-    db 1
+        db 1
 
 ;; DEBUGGING DEBUGGING
 GLOBALNAME g_u32Info
-    dd 0
+        dd 0
 
 ;; Far pointer to DOS16WRITE (corrected set before called).
 ; Just a 'temporary' hack to work around a wlink/nasm issue.
 GLOBALNAME g_fpfnDos16Write
-    dw  DOS16WRITE
-    dw  seg DOS16WRITE
+        dw  DOS16WRITE
+        dw  seg DOS16WRITE
 
 ;;
 ; The attach dd data.
 GLOBALNAME g_VBoxGuestAttachDD
-    dd 0
-    dw 0
-    dd 0
-    dw 0
+        dd 0
+        dw 0
+        dd 0
+        dw 0
 ;;
 ; The AttachDD name of the VBoxGuest.sys driver.
 GLOBALNAME g_szVBoxGuestName
-    db VBOXGUEST_DEVICE_NAME_SHORT, 0
+        db VBOXGUEST_DEVICE_NAME_SHORT, 0
 ;;
 ; The VBoxGuest IDC connection data.
 GLOBALNAME g_VBoxGuestIDC
-    times VBGLOS2ATTACHDD_size db 0
+        times VBGLOS2ATTACHDD_size db 0
 
 ;;
@@ -341,5 +646,5 @@
 segment DATA32
 g_pfnDos16Write:
-    dd  DOS16WRITE  ; flat
+        dd  DOS16WRITE  ; flat
 
 
@@ -363,13 +668,13 @@
 VBOXSF_EP16_BEGIN   FS_ALLOCATEPAGESPACE, 'FS_ALLOCATEPAGESPACE'
 VBOXSF_TO_32        FS_ALLOCATEPAGESPACE, 4*4
-    movzx   ecx, word [ebp + 08h]       ; cbWantContig
-    mov     [esp + 3*4], ecx
-    mov     edx, [ebp + 0ah]            ; cb
-    mov     [esp + 2*4], edx
-    VBOXSF_PSFFSD_2_FLAT  0eh, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  12h, 0*4      ; psffsi
-    call    NAME(FS32_ALLOCATEPAGESPACE)
+        movzx   ecx, word [ebp + 08h]       ; cbWantContig
+        mov     [esp + 3*4], ecx
+        mov     edx, [ebp + 0ah]            ; cb
+        mov     [esp + 2*4], edx
+        VBOXSF_PSFFSD_2_FLAT  0eh, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  12h, 0*4      ; psffsi
+        call    NAME(FS32_ALLOCATEPAGESPACE)
 VBOXSF_TO_16        FS_ALLOCATEPAGESPACE
-    retf    0eh
+        retf    0eh
 VBOXSF_EP16_END     FS_ALLOCATEPAGESPACE
 
@@ -378,27 +683,27 @@
 ;
 VBOXSF_EP16_BEGIN   FS_ATTACH, 'FS_ATTACH'
-    ;
-    ; Initialized ring-0 yet? (this is a likely first entry point)
-    ;
-    push    ds
-    mov     ax, DATA16
-    mov     ds, ax
-    test    byte [NAME(g_fDoneRing0)], 1
-    jnz     .DoneRing0
-    call    NAME(VBoxSFR0Init16Bit)
+        ;
+        ; Initialized ring-0 yet? (this is a likely first entry point)
+        ;
+        push    ds
+        mov     ax, DATA16
+        mov     ds, ax
+        test    byte [NAME(g_fDoneRing0)], 1
+        jnz     .DoneRing0
+        call    NAME(VBoxSFR0Init16Bit)
 .DoneRing0:
-    pop     ds
+        pop     ds
 
 VBOXSF_TO_32        FS_ATTACH, 6*4
-    VBOXSF_FARPTR_2_FLAT  08h, 5*4      ; pcbParm
-    VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pszParm
-    VBOXSF_FARPTR_2_FLAT  10h, 3*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  14h, 2*4      ; pvpfsd
-    VBOXSF_FARPTR_2_FLAT  18h, 1*4      ; pszDev
-    movzx   ecx, word [ebp + 1ch]       ; fFlag
-    mov     [esp], ecx
-    call    NAME(FS32_ATTACH)
+        VBOXSF_FARPTR_2_FLAT  08h, 5*4      ; pcbParm
+        VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pszParm
+        VBOXSF_FARPTR_2_FLAT  10h, 3*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  14h, 2*4      ; pvpfsd
+        VBOXSF_FARPTR_2_FLAT  18h, 1*4      ; pszDev
+        movzx   ecx, word [ebp + 1ch]       ; fFlag
+        mov     [esp], ecx
+        call    NAME(FS32_ATTACH)
 VBOXSF_TO_16        FS_ATTACH
-    retf    16h
+        retf    16h
 VBOXSF_EP16_END     FS_ATTACH
 
@@ -408,10 +713,10 @@
 VBOXSF_EP16_BEGIN   FS_CANCELLOCKREQUEST, 'FS_CANCELLOCKREQUEST'
 VBOXSF_TO_32        FS_CANCELLOCKREQUEST, 3*4
-    VBOXSF_FARPTR_2_FLAT  08h, 2*4      ; pLockRange
-    VBOXSF_PSFFSD_2_FLAT  0ch, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  10h, 0*4      ; psffsi
-    call    NAME(FS32_CANCELLOCKREQUEST)
+        VBOXSF_FARPTR_2_FLAT  08h, 2*4      ; pLockRange
+        VBOXSF_PSFFSD_2_FLAT  0ch, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  10h, 0*4      ; psffsi
+        call    NAME(FS32_CANCELLOCKREQUEST)
 VBOXSF_TO_16        FS_CANCELLOCKREQUEST
-    retf    0ch
+        retf    0ch
 VBOXSF_EP16_END     FS_CANCELLOCKREQUEST
 
@@ -421,10 +726,10 @@
 VBOXSF_EP16_BEGIN   FS_CANCELLOCKREQUESTL, 'FS_CANCELLOCKREQUESTL'
 VBOXSF_TO_32        FS_CANCELLOCKREQUESTL, 3*4
-    VBOXSF_FARPTR_2_FLAT  08h, 2*4      ; pLockRange
-    VBOXSF_PSFFSD_2_FLAT  0ch, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  10h, 0*4      ; psffsi
-    call    NAME(FS32_CANCELLOCKREQUESTL)
+        VBOXSF_FARPTR_2_FLAT  08h, 2*4      ; pLockRange
+        VBOXSF_PSFFSD_2_FLAT  0ch, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  10h, 0*4      ; psffsi
+        call    NAME(FS32_CANCELLOCKREQUESTL)
 VBOXSF_TO_16        FS_CANCELLOCKREQUESTL
-    retf    0ch
+        retf    0ch
 VBOXSF_EP16_END     FS_CANCELLOCKREQUESTL
 
@@ -434,14 +739,14 @@
 VBOXSF_EP16_BEGIN   FS_CHDIR, 'FS_CHDIR'
 VBOXSF_TO_32        FS_CHDIR, 5*4
-    movzx   ecx, word [ebp + 08h]       ; iCurDirEnd
-    mov     [esp + 4*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0ah, 3*4      ; pszDir
-    VBOXSF_FARPTR_2_FLAT  0eh, 2*4      ; pcdfsd (use slow thunk here, see flag)
-    VBOXSF_FARPTR_2_FLAT  12h, 1*4      ; pcdfsi
-    movzx   eax, word [ebp + 16h]       ; flag
-    mov     [esp], eax
-    call    NAME(FS32_CHDIR)
+        movsx   ecx, word [ebp + 08h]       ; iCurDirEnd
+        mov     [esp + 4*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0ah, 3*4      ; pszDir
+        VBOXSF_FARPTR_2_FLAT  0eh, 2*4      ; pcdfsd (use slow thunk here, see flag)
+        VBOXSF_FARPTR_2_FLAT  12h, 1*4      ; pcdfsi
+        movzx   eax, word [ebp + 16h]       ; flag
+        mov     [esp], eax
+        call    NAME(FS32_CHDIR)
 VBOXSF_TO_16        FS_CHDIR
-    retf    10h
+        retf    10h
 VBOXSF_EP16_END     FS_CHDIR
 
@@ -450,20 +755,20 @@
 VBOXSF_EP16_BEGIN   FS_CHGFILEPTR, 'FS_CHGFILEPTR'
 VBOXSF_TO_32        FS_CHGFILEPTR, 6*4
-    movzx   ecx, word [ebp + 08h]       ; IOflag
-    mov     [esp + 5*4], ecx
-    movzx   edx, word [ebp + 0ah]       ; usMethod
-    mov     [esp + 4*4], edx
-    mov     eax, [ebp + 0ch]            ; off
-    mov     [esp + 2*4], eax
-    rol     eax, 1                      ; high dword - is there a better way than this?
-    and     eax, 1
-    mov     edx, 0ffffffffh
-    mul     edx
-    mov     [esp + 3*4], eax
-    VBOXSF_PSFFSD_2_FLAT  10h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  14h, 0*4      ; psffsi
-    call    FS32_CHGFILEPTRL
+        movzx   ecx, word [ebp + 08h]       ; IOflag
+        mov     [esp + 5*4], ecx
+        movzx   edx, word [ebp + 0ah]       ; usMethod
+        mov     [esp + 4*4], edx
+        mov     eax, [ebp + 0ch]            ; off
+        mov     [esp + 2*4], eax
+        rol     eax, 1                      ; high dword - is there a better way than this?
+        and     eax, 1
+        mov     edx, 0ffffffffh
+        mul     edx
+        mov     [esp + 3*4], eax
+        VBOXSF_PSFFSD_2_FLAT  10h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  14h, 0*4      ; psffsi
+        call    FS32_CHGFILEPTRL
 VBOXSF_TO_16        FS_CHGFILEPTR
-    retf    10h
+        retf    10h
 VBOXSF_EP16_END     FS_CHGFILEPTR
 
@@ -474,13 +779,13 @@
 VBOXSF_EP16_BEGIN   FS_CLOSE, 'FS_CLOSE'
 VBOXSF_TO_32        FS_CLOSE, 4*4
-    VBOXSF_PSFFSD_2_FLAT  08h, 3*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  0ch, 2*4      ; psffsi
-    movzx   ecx, word [ebp + 10h]       ; IOflag
-    mov     [esp + 1*4], ecx
-    movzx   edx, word [ebp + 12h]       ; type
-    mov     [esp], edx
-    call    NAME(FS32_CLOSE)
+        VBOXSF_PSFFSD_2_FLAT  08h, 3*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  0ch, 2*4      ; psffsi
+        movzx   ecx, word [ebp + 10h]       ; IOflag
+        mov     [esp + 1*4], ecx
+        movzx   edx, word [ebp + 12h]       ; type
+        mov     [esp], edx
+        call    NAME(FS32_CLOSE)
 VBOXSF_TO_16        FS_CLOSE
-    retf    0ch
+        retf    0ch
 VBOXSF_EP16_END     FS_CLOSE
 
@@ -491,13 +796,13 @@
 VBOXSF_EP16_BEGIN   FS_COMMIT, 'FS_COMMIT'
 VBOXSF_TO_32        FS_COMMIT, 4*4
-    VBOXSF_PSFFSD_2_FLAT  08h, 3*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  0ch, 2*4      ; psffsi
-    movzx   ecx, word [ebp + 10h]       ; IOflag
-    mov     [esp + 1*4], ecx
-    movzx   edx, word [ebp + 12h]       ; type
-    mov     [esp], edx
-    call    NAME(FS32_COMMIT)
+        VBOXSF_PSFFSD_2_FLAT  08h, 3*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  0ch, 2*4      ; psffsi
+        movzx   ecx, word [ebp + 10h]       ; IOflag
+        mov     [esp + 1*4], ecx
+        movzx   edx, word [ebp + 12h]       ; type
+        mov     [esp], edx
+        call    NAME(FS32_COMMIT)
 VBOXSF_TO_16        FS_COMMIT
-    retf    0ch
+        retf    0ch
 VBOXSF_EP16_END     FS_COMMIT
 
@@ -507,19 +812,19 @@
 VBOXSF_EP16_BEGIN   FS_COPY, 'FS_COPY'
 VBOXSF_TO_32        FS_COPY, 8*4
-    movzx   ecx, word [ebp + 08h]       ; flag
-    mov     [esp + 7*4], ecx
-    movzx   edx, word [ebp + 0ah]       ; iDstCurDirEnd
-    mov     [esp + 6*4], edx
-    VBOXSF_FARPTR_2_FLAT  0ch, 5*4      ; pszDst
-    movzx   eax, word [ebp + 10h]       ; iSrcCurDirEnd
-    mov     [esp + 4*4], eax
-    VBOXSF_FARPTR_2_FLAT  12h, 3*4      ; pszSrc
-    VBOXSF_PCDFSD_2_FLAT  16h, 2*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  1ah, 1*4      ; psffsi
-    movzx   ecx, word [ebp + 1eh]       ; flag
-    mov     [esp], ecx
-    call    NAME(FS32_COPY)
+        movzx   ecx, word [ebp + 08h]       ; flag
+        mov     [esp + 7*4], ecx
+        movsx   edx, word [ebp + 0ah]       ; iDstCurDirEnd
+        mov     [esp + 6*4], edx
+        VBOXSF_FARPTR_2_FLAT  0ch, 5*4      ; pszDst
+        movsx   eax, word [ebp + 10h]       ; iSrcCurDirEnd
+        mov     [esp + 4*4], eax
+        VBOXSF_FARPTR_2_FLAT  12h, 3*4      ; pszSrc
+        VBOXSF_PCDFSD_2_FLAT  16h, 2*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  1ah, 1*4      ; psffsi
+        movzx   ecx, word [ebp + 1eh]       ; flag
+        mov     [esp], ecx
+        call    NAME(FS32_COPY)
 VBOXSF_TO_16        FS_COPY
-    retf    18h
+        retf    18h
 VBOXSF_EP16_END     FS_COPY
 
@@ -529,12 +834,12 @@
 VBOXSF_EP16_BEGIN   FS_DELETE, 'FS_DELETE'
 VBOXSF_TO_32        FS_DELETE, 4*4
-    movzx   ecx, word [ebp + 08h]       ; iCurDirEnd
-    mov     [esp + 3*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0ah, 2*4      ; pszFile
-    VBOXSF_PCDFSD_2_FLAT  0eh, 1*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  12h, 0*4      ; pcdfsi
-    call    NAME(FS32_DELETE)
+        movsx   ecx, word [ebp + 08h]       ; iCurDirEnd
+        mov     [esp + 3*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0ah, 2*4      ; pszFile
+        VBOXSF_PCDFSD_2_FLAT  0eh, 1*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  12h, 0*4      ; pcdfsi
+        call    NAME(FS32_DELETE)
 VBOXSF_TO_16        FS_DELETE
-    retf    0eh
+        retf    0eh
 VBOXSF_EP16_END FS_DELETE
 
@@ -544,10 +849,10 @@
 VBOXSF_EP16_BEGIN   FS_DOPAGEIO, 'FS_DOPAGEIO'
 VBOXSF_TO_32        FS_DOPAGEIO, 3*4
-    VBOXSF_FARPTR_2_FLAT  08h, 2*4      ; pList
-    VBOXSF_PSFFSD_2_FLAT  0ch, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  10h, 0*4      ; psffsi
-    call    NAME(FS32_DOPAGEIO)
+        VBOXSF_FARPTR_2_FLAT  08h, 2*4      ; pList
+        VBOXSF_PSFFSD_2_FLAT  0ch, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  10h, 0*4      ; psffsi
+        call    NAME(FS32_DOPAGEIO)
 VBOXSF_TO_16        FS_DOPAGEIO
-    retf    0ch
+        retf    0ch
 VBOXSF_EP16_END     FS_DOPAGEIO
 
@@ -555,26 +860,26 @@
 ; @cproto void FS_EXIT(USHORT uid, USHORT pid, USHORT pdb)
 VBOXSF_EP16_BEGIN   FS_EXIT, 'FS_EXIT'
-    ;
-    ; Initialized ring-0 yet? (this is a likely first entry point)
-    ;
-    push    ds
-    mov     ax, DATA16
-    mov     ds, ax
-    test    byte [NAME(g_fDoneRing0)], 1
-    jnz     .DoneRing0
-    call    NAME(VBoxSFR0Init16Bit)
+        ;
+        ; Initialized ring-0 yet? (this is a likely first entry point)
+        ;
+        push    ds
+        mov     ax, DATA16
+        mov     ds, ax
+        test    byte [NAME(g_fDoneRing0)], 1
+        jnz     .DoneRing0
+        call    NAME(VBoxSFR0Init16Bit)
 .DoneRing0:
-    pop     ds
+        pop     ds
 
 VBOXSF_TO_32        FS_EXIT, 3*4
-    movzx   ecx, word [ebp + 08h]       ; pdb
-    mov     [esp + 2*4], ecx
-    movzx   edx, word [ebp + 0ah]       ; pib
-    mov     [esp + 1*4], edx
-    movzx   eax, word [ebp + 0ch]       ; uid
-    mov     [esp], eax
-    call    NAME(FS32_EXIT)
+        movzx   ecx, word [ebp + 08h]       ; pdb
+        mov     [esp + 2*4], ecx
+        movzx   edx, word [ebp + 0ah]       ; pib
+        mov     [esp + 1*4], edx
+        movzx   eax, word [ebp + 0ch]       ; uid
+        mov     [esp], eax
+        call    NAME(FS32_EXIT)
 VBOXSF_TO_16        FS_EXIT
-    retf    6h
+        retf    6h
 VBOXSF_EP16_END     FS_EXIT
 
@@ -585,15 +890,15 @@
 VBOXSF_EP16_BEGIN   FS_FILEATTRIBUTE, 'FS_FILEATTRIBUTE'
 VBOXSF_TO_32        FS_FILEATTRIBUTE, 6*4
-    VBOXSF_FARPTR_2_FLAT  08h, 5*4      ; pAttr
-    movzx   ecx, word [ebp + 0ch]       ; iCurDirEnd
-    mov     [esp + 4*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0eh, 3*4      ; pszName
-    VBOXSF_PCDFSD_2_FLAT  12h, 2*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  16h, 1*4      ; pcdfsi
-    movzx   edx, word [ebp + 1ah]       ; flag
-    mov     [esp], edx
-    call    NAME(FS32_FILEATTRIBUTE)
+        VBOXSF_FARPTR_2_FLAT  08h, 5*4      ; pAttr
+        movsx   ecx, word [ebp + 0ch]       ; iCurDirEnd - caller may pass 0xffff, so sign extend.
+        mov     [esp + 4*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0eh, 3*4      ; pszName
+        VBOXSF_PCDFSD_2_FLAT  12h, 2*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  16h, 1*4      ; pcdfsi
+        movzx   edx, word [ebp + 1ah]       ; flag
+        mov     [esp], edx
+        call    NAME(FS32_FILEATTRIBUTE)
 VBOXSF_TO_16        FS_FILEATTRIBUTE
-    retf    14h
+        retf    14h
 VBOXSF_EP16_END     FS_FILEATTRIBUTE
 
@@ -604,18 +909,18 @@
 VBOXSF_EP16_BEGIN   FS_FILEINFO, 'FS_FILEINFO'
 VBOXSF_TO_32        FS_FILEINFO, 7*4
-    movzx   ecx, word [ebp + 08h]       ; IOflag
-    mov     [esp + 6*4], ecx
-    movzx   edx, word [ebp + 0ah]       ; cbData
-    mov     [esp + 5*4], edx
-    VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pData
-    movzx   eax, word [ebp + 10h]       ; level
-    mov     [esp + 3*4], eax
-    VBOXSF_PSFFSD_2_FLAT  12h, 2*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  16h, 1*4      ; psffsi
-    movzx   ecx, word [ebp + 1ah]       ; flag
-    mov     [esp], ecx
-    call    NAME(FS32_FILEINFO)
+        movzx   ecx, word [ebp + 08h]       ; IOflag
+        mov     [esp + 6*4], ecx
+        movzx   edx, word [ebp + 0ah]       ; cbData
+        mov     [esp + 5*4], edx
+        VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pData
+        movzx   eax, word [ebp + 10h]       ; level
+        mov     [esp + 3*4], eax
+        VBOXSF_PSFFSD_2_FLAT  12h, 2*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  16h, 1*4      ; psffsi
+        movzx   ecx, word [ebp + 1ah]       ; flag
+        mov     [esp], ecx
+        call    NAME(FS32_FILEINFO)
 VBOXSF_TO_16        FS_FILEINFO
-    retf    14h
+        retf    14h
 VBOXSF_EP16_END     FS_FILEINFO
 
@@ -626,15 +931,15 @@
 VBOXSF_EP16_BEGIN   FS_FILEIO, 'FS_FILEIO'
 VBOXSF_TO_32        FS_FILEIO, 6*4
-    movzx   ecx, word [ebp + 08h]       ; IOFlag
-    mov     [esp + 5*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0ah, 4*4      ; poError
-    movzx   edx, word [ebp + 0eh]       ; cbCmdList
-    mov     [esp + 3*4], edx
-    VBOXSF_FARPTR_2_FLAT  10h, 2*4      ; pCmdList
-    VBOXSF_PSFFSD_2_FLAT  14h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  18h, 0*4      ; psffsi
-    call    NAME(FS32_FILEIO)
+        movzx   ecx, word [ebp + 08h]       ; IOFlag
+        mov     [esp + 5*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0ah, 4*4      ; poError
+        movzx   edx, word [ebp + 0eh]       ; cbCmdList
+        mov     [esp + 3*4], edx
+        VBOXSF_FARPTR_2_FLAT  10h, 2*4      ; pCmdList
+        VBOXSF_PSFFSD_2_FLAT  14h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  18h, 0*4      ; psffsi
+        call    NAME(FS32_FILEIO)
 VBOXSF_TO_16        FS_FILEIO
-    retf    14h
+        retf    14h
 VBOXSF_EP16_END     FS_FILEIO
 
@@ -645,15 +950,15 @@
 VBOXSF_EP16_BEGIN   FS_FILELOCKS, 'FS_FILELOCKS'
 VBOXSF_TO_32        FS_FILELOCKS, 6*4
-    mov     ecx, [ebp + 08h]            ; flags
-    mov     [esp + 5*4], ecx
-    mov     edx, [ebp + 0ch]            ; timeout
-    mov     [esp + 4*4], edx
-    VBOXSF_FARPTR_2_FLAT  10h, 3*4      ; pLockRange
-    VBOXSF_FARPTR_2_FLAT  14h, 2*4      ; pUnLockRange
-    VBOXSF_PSFFSD_2_FLAT  18h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  1ch, 0*4      ; psffsi
-    call    NAME(FS32_FILELOCKS)
+        mov     ecx, [ebp + 08h]            ; flags
+        mov     [esp + 5*4], ecx
+        mov     edx, [ebp + 0ch]            ; timeout
+        mov     [esp + 4*4], edx
+        VBOXSF_FARPTR_2_FLAT  10h, 3*4      ; pLockRange
+        VBOXSF_FARPTR_2_FLAT  14h, 2*4      ; pUnLockRange
+        VBOXSF_PSFFSD_2_FLAT  18h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  1ch, 0*4      ; psffsi
+        call    NAME(FS32_FILELOCKS)
 VBOXSF_TO_16        FS_FILELOCKS
-    retf    18h
+        retf    18h
 VBOXSF_EP16_END     FS_FILELOCKS
 
@@ -664,15 +969,15 @@
 VBOXSF_EP16_BEGIN   FS_FILELOCKSL, 'FS_FILELOCKSL'
 VBOXSF_TO_32        FS_FILELOCKSL, 6*4
-    mov     ecx, [ebp + 08h]            ; flags
-    mov     [esp + 5*4], ecx
-    mov     edx, [ebp + 0ch]            ; timeout
-    mov     [esp + 4*4], edx
-    VBOXSF_FARPTR_2_FLAT  10h, 3*4      ; pLockRange
-    VBOXSF_FARPTR_2_FLAT  14h, 2*4      ; pUnLockRange
-    VBOXSF_PSFFSD_2_FLAT  18h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  1ch, 0*4      ; psffsi
-    call    NAME(FS32_FILELOCKS)
+        mov     ecx, [ebp + 08h]            ; flags
+        mov     [esp + 5*4], ecx
+        mov     edx, [ebp + 0ch]            ; timeout
+        mov     [esp + 4*4], edx
+        VBOXSF_FARPTR_2_FLAT  10h, 3*4      ; pLockRange
+        VBOXSF_FARPTR_2_FLAT  14h, 2*4      ; pUnLockRange
+        VBOXSF_PSFFSD_2_FLAT  18h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  1ch, 0*4      ; psffsi
+        call    NAME(FS32_FILELOCKS)
 VBOXSF_TO_16        FS_FILELOCKSL
-    retf    18h
+        retf    18h
 VBOXSF_EP16_END     FS_FILELOCKSL
 
@@ -683,9 +988,9 @@
 VBOXSF_EP16_BEGIN   FS_FINDCLOSE, 'FS_FINDCLOSE'
 VBOXSF_TO_32        FS_FINDCLOSE, 2*4
-    VBOXSF_PFSFSD_2_FLAT  08h, 1*4      ; pfsfsd
-    VBOXSF_FARPTR_2_FLAT  0ch, 0*4      ; pfsfsi
-    call    NAME(FS32_FINDCLOSE)
+        VBOXSF_PFSFSD_2_FLAT  08h, 1*4      ; pfsfsd
+        VBOXSF_FARPTR_2_FLAT  0ch, 0*4      ; pfsfsi
+        call    NAME(FS32_FINDCLOSE)
 VBOXSF_TO_16        FS_FINDCLOSE
-    retf    8h
+        retf    8h
 VBOXSF_EP16_END     FS_FINDCLOSE
 
@@ -698,24 +1003,24 @@
 VBOXSF_EP16_BEGIN   FS_FINDFIRST, 'FS_FINDFIRST'
 VBOXSF_TO_32        FS_FINDFIRST, 12*4
-    movzx   ecx, word [ebp + 08h]       ; flags
-    mov     [esp + 11*4], ecx
-    movzx   edx, word [ebp + 0ah]       ; level
-    mov     [esp + 10*4], edx
-    VBOXSF_FARPTR_2_FLAT  0ch, 9*4      ; pcMatch
-    movzx   eax, word [ebp + 10h]       ; cbData
-    mov     [esp + 8*4], eax
-    VBOXSF_FARPTR_2_FLAT  12h, 7*4      ; pbData
-    VBOXSF_FARPTR_2_FLAT  16h, 6*4      ; pfsfsd
-    VBOXSF_FARPTR_2_FLAT  1ah, 5*4      ; pfsfsi
-    movzx   ecx, word [ebp + 1eh]       ; attr
-    mov     [esp + 4*4], ecx
-    movzx   edx, word [ebp + 20h]       ; iCurDirEnd
-    mov     [esp + 3*4], edx
-    VBOXSF_FARPTR_2_FLAT  22h, 2*4      ; pszName
-    VBOXSF_PCDFSD_2_FLAT  26h, 1*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  2ah, 0*4      ; pcdfsi
-    call    NAME(FS32_FINDFIRST)
+        movzx   ecx, word [ebp + 08h]       ; flags
+        mov     [esp + 11*4], ecx
+        movzx   edx, word [ebp + 0ah]       ; level
+        mov     [esp + 10*4], edx
+        VBOXSF_FARPTR_2_FLAT  0ch, 9*4      ; pcMatch
+        movzx   eax, word [ebp + 10h]       ; cbData
+        mov     [esp + 8*4], eax
+        VBOXSF_FARPTR_2_FLAT  12h, 7*4      ; pbData
+        VBOXSF_FARPTR_2_FLAT  16h, 6*4      ; pfsfsd
+        VBOXSF_FARPTR_2_FLAT  1ah, 5*4      ; pfsfsi
+        movzx   ecx, word [ebp + 1eh]       ; attr
+        mov     [esp + 4*4], ecx
+        movsx   edx, word [ebp + 20h]       ; iCurDirEnd
+        mov     [esp + 3*4], edx
+        VBOXSF_FARPTR_2_FLAT  22h, 2*4      ; pszName
+        VBOXSF_PCDFSD_2_FLAT  26h, 1*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  2ah, 0*4      ; pcdfsi
+        call    NAME(FS32_FINDFIRST)
 VBOXSF_TO_16        FS_FINDFIRST
-    retf    26h
+        retf    26h
 VBOXSF_EP16_END FS_FINDFIRST
 
@@ -727,20 +1032,20 @@
 VBOXSF_EP16_BEGIN   FS_FINDFROMNAME, 'FS_FINDFROMNAME'
 VBOXSF_TO_32        FS_FINDFROMNAME, 9*4
-    movzx   ecx, word [ebp + 08h]       ; flags
-    mov     [esp + 8*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0ah, 7*4      ; pszName
-    mov     edx, [ebp + 0eh]            ; position
-    mov     [esp + 6*4], edx
-    movzx   eax, word [ebp + 12h]       ; level
-    mov     [esp + 5*4], eax
-    VBOXSF_FARPTR_2_FLAT  14h, 4*4      ; pcMatch
-    movzx   eax, word [ebp + 18h]       ; cbData
-    mov     [esp + 3*4], eax
-    VBOXSF_FARPTR_2_FLAT  1ah, 2*4      ; pbData
-    VBOXSF_PFSFSD_2_FLAT  1eh, 1*4      ; pfsfsd
-    VBOXSF_FARPTR_2_FLAT  22h, 0*4      ; pfsfsi
-    call    NAME(FS32_FINDFROMNAME)
+        movzx   ecx, word [ebp + 08h]       ; flags
+        mov     [esp + 8*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0ah, 7*4      ; pszName
+        mov     edx, [ebp + 0eh]            ; position
+        mov     [esp + 6*4], edx
+        movzx   eax, word [ebp + 12h]       ; level
+        mov     [esp + 5*4], eax
+        VBOXSF_FARPTR_2_FLAT  14h, 4*4      ; pcMatch
+        movzx   eax, word [ebp + 18h]       ; cbData
+        mov     [esp + 3*4], eax
+        VBOXSF_FARPTR_2_FLAT  1ah, 2*4      ; pbData
+        VBOXSF_PFSFSD_2_FLAT  1eh, 1*4      ; pfsfsd
+        VBOXSF_FARPTR_2_FLAT  22h, 0*4      ; pfsfsi
+        call    NAME(FS32_FINDFROMNAME)
 VBOXSF_TO_16        FS_FINDFROMNAME
-    retf    1eh
+        retf    1eh
 VBOXSF_EP16_END     FS_FINDFROMNAME
 
@@ -752,17 +1057,17 @@
 VBOXSF_EP16_BEGIN   FS_FINDNEXT, 'FS_FINDNEXT'
 VBOXSF_TO_32        FS_FINDNEXT, 7*4
-    movzx   ecx, word [ebp + 08h]       ; flags
-    mov     [esp + 6*4], ecx
-    movzx   eax, word [ebp + 0ah]       ; level
-    mov     [esp + 5*4], eax
-    VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pcMatch
-    movzx   eax, word [ebp + 10h]       ; cbData
-    mov     [esp + 3*4], eax
-    VBOXSF_FARPTR_2_FLAT  12h, 2*4      ; pbData
-    VBOXSF_PFSFSD_2_FLAT  16h, 1*4      ; pfsfsd
-    VBOXSF_FARPTR_2_FLAT  1ah, 0*4      ; pfsfsi
-    call    NAME(FS32_FINDNEXT)
+        movzx   ecx, word [ebp + 08h]       ; flags
+        mov     [esp + 6*4], ecx
+        movzx   eax, word [ebp + 0ah]       ; level
+        mov     [esp + 5*4], eax
+        VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pcMatch
+        movzx   eax, word [ebp + 10h]       ; cbData
+        mov     [esp + 3*4], eax
+        VBOXSF_FARPTR_2_FLAT  12h, 2*4      ; pbData
+        VBOXSF_PFSFSD_2_FLAT  16h, 1*4      ; pfsfsd
+        VBOXSF_FARPTR_2_FLAT  1ah, 0*4      ; pfsfsi
+        call    NAME(FS32_FINDNEXT)
 VBOXSF_TO_16        FS_FINDNEXT
-    retf    16h
+        retf    16h
 VBOXSF_EP16_END     FS_FINDNEXT
 
@@ -773,9 +1078,9 @@
 VBOXSF_EP16_BEGIN   FS_FINDNOTIFYCLOSE, 'FS_FINDNOTIFYCLOSE'
 VBOXSF_TO_32        FS_FINDNOTIFYCLOSE, 1*4
-    movzx   ecx, word [ebp + 08h]       ; handle
-    mov     [esp], ecx
-    call    NAME(FS32_FINDNOTIFYCLOSE)
+        movzx   ecx, word [ebp + 08h]       ; handle
+        mov     [esp], ecx
+        call    NAME(FS32_FINDNOTIFYCLOSE)
 VBOXSF_TO_16        FS_FINDNOTIFYCLOSE
-    retf    2h
+        retf    2h
 VBOXSF_EP16_END     FS_FINDNOTIFYCLOSE
 
@@ -788,23 +1093,23 @@
 VBOXSF_EP16_BEGIN   FS_FINDNOTIFYFIRST, 'FS_FINDNOTIFYFIRST'
 VBOXSF_TO_32        FS_FINDNOTIFYFIRST, 11*4
-    movzx   ecx, word [ebp + 08h]       ; flags
-    mov     [esp + 10*4], ecx
-    movzx   edx, word [ebp + 0ah]       ; level
-    mov     [esp + 9*4], edx
-    VBOXSF_FARPTR_2_FLAT  0ch, 8*4      ; pcMatch
-    movzx   eax, word [ebp + 10h]       ; cbData
-    mov     [esp + 7*4], eax
-    VBOXSF_FARPTR_2_FLAT  12h, 6*4      ; pbData
-    VBOXSF_FARPTR_2_FLAT  16h, 5*4      ; pHandle
-    movzx   ecx, word [ebp + 1ah]       ; attr
-    mov     [esp + 4*4], ecx
-    movzx   edx, word [ebp + 1ch]       ; iCurDirEnd
-    mov     [esp + 3*4], edx
-    VBOXSF_FARPTR_2_FLAT  1eh, 2*4      ; pszName
-    VBOXSF_PCDFSD_2_FLAT  22h, 1*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  26h, 0*4      ; pcdfsi
-    call    NAME(FS32_FINDNOTIFYFIRST)
+        movzx   ecx, word [ebp + 08h]       ; flags
+        mov     [esp + 10*4], ecx
+        movzx   edx, word [ebp + 0ah]       ; level
+        mov     [esp + 9*4], edx
+        VBOXSF_FARPTR_2_FLAT  0ch, 8*4      ; pcMatch
+        movzx   eax, word [ebp + 10h]       ; cbData
+        mov     [esp + 7*4], eax
+        VBOXSF_FARPTR_2_FLAT  12h, 6*4      ; pbData
+        VBOXSF_FARPTR_2_FLAT  16h, 5*4      ; pHandle
+        movzx   ecx, word [ebp + 1ah]       ; attr
+        mov     [esp + 4*4], ecx
+        movsx   edx, word [ebp + 1ch]       ; iCurDirEnd
+        mov     [esp + 3*4], edx
+        VBOXSF_FARPTR_2_FLAT  1eh, 2*4      ; pszName
+        VBOXSF_PCDFSD_2_FLAT  22h, 1*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  26h, 0*4      ; pcdfsi
+        call    NAME(FS32_FINDNOTIFYFIRST)
 VBOXSF_TO_16        FS_FINDNOTIFYFIRST
-    retf    22h
+        retf    22h
 VBOXSF_EP16_END     FS_FINDNOTIFYFIRST
 
@@ -816,17 +1121,17 @@
 VBOXSF_EP16_BEGIN FS_FINDNOTIFYNEXT, 'FS_FINDNOTIFYNEXT'
 VBOXSF_TO_32        FS_FINDNOTIFYNEXT, 6*4
-    mov     ecx, [ebp + 08h]            ; timeout
-    mov     [esp + 5*4], ecx
-    movzx   edx, word [ebp + 0ch]       ; level
-    mov     [esp + 4*4], edx
-    VBOXSF_FARPTR_2_FLAT  0eh, 3*4      ; pcMatch
-    movzx   eax, word [ebp + 12h]       ; cbData
-    mov     [esp + 2*4], eax
-    VBOXSF_FARPTR_2_FLAT  14h, 1*4      ; pbData
-    movzx   ecx, word [ebp + 18h]       ; handle
-    mov     [esp], ecx
-    call    NAME(FS32_FINDNOTIFYNEXT)
+        mov     ecx, [ebp + 08h]            ; timeout
+        mov     [esp + 5*4], ecx
+        movzx   edx, word [ebp + 0ch]       ; level
+        mov     [esp + 4*4], edx
+        VBOXSF_FARPTR_2_FLAT  0eh, 3*4      ; pcMatch
+        movzx   eax, word [ebp + 12h]       ; cbData
+        mov     [esp + 2*4], eax
+        VBOXSF_FARPTR_2_FLAT  14h, 1*4      ; pbData
+        movzx   ecx, word [ebp + 18h]       ; handle
+        mov     [esp], ecx
+        call    NAME(FS32_FINDNOTIFYNEXT)
 VBOXSF_TO_16        FS_FINDNOTIFYNEXT
-    retf    12h
+        retf    12h
 VBOXSF_EP16_END     FS_FINDNOTIFYNEXT
 
@@ -835,11 +1140,11 @@
 VBOXSF_EP16_BEGIN FS_FLUSHBUF, 'FS_FLUSHBUF'
 VBOXSF_TO_32        FS_FLUSHBUF, 2*4
-    movzx   edx, word [ebp + 08h]       ; flag
-    mov     [esp + 1*4], edx
-    movzx   eax, word [ebp + 0ch]       ; hVPB
-    mov     [esp + 0*4], eax
-    call    NAME(FS32_FLUSHBUF)
+        movzx   edx, word [ebp + 08h]       ; flag
+        mov     [esp + 1*4], edx
+        movzx   eax, word [ebp + 0ch]       ; hVPB
+        mov     [esp + 0*4], eax
+        call    NAME(FS32_FLUSHBUF)
 VBOXSF_TO_16        FS_FLUSHBUF
-    retf    4h
+        retf    4h
 VBOXSF_EP16_END FS_FLUSHBUF
 
@@ -849,33 +1154,33 @@
 ;                       PVOID pData, USHORT lenData, PUSHORT plenDataIO);
 VBOXSF_EP16_BEGIN FS_FSCTL, 'FS_FSCTL'
-    ;
-    ; Initialized ring-0 yet? (this is a likely first entry point)
-    ;
-    push    ds
-    mov     ax, DATA16
-    mov     ds, ax
-    test    byte [NAME(g_fDoneRing0)], 1
-    jnz     .DoneRing0
-    call    NAME(VBoxSFR0Init16Bit)
+        ;
+        ; Initialized ring-0 yet? (this is a likely first entry point)
+        ;
+        push    ds
+        mov     ax, DATA16
+        mov     ds, ax
+        test    byte [NAME(g_fDoneRing0)], 1
+        jnz     .DoneRing0
+        call    NAME(VBoxSFR0Init16Bit)
 .DoneRing0:
-    pop     ds
+        pop     ds
 
 VBOXSF_TO_32        FS_FSCTL, 9*4
-    VBOXSF_FARPTR_2_FLAT  08h, 8*4      ; plenDataIO
-    movzx   ecx, word [ebp + 0ch]       ; lenData
-    mov     [esp + 7*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0eh, 6*4      ; pData
-    VBOXSF_FARPTR_2_FLAT  12h, 5*4      ; plenDataIO
-    movzx   ecx, word [ebp + 16h]       ; lenData
-    mov     [esp + 4*4], ecx
-    VBOXSF_FARPTR_2_FLAT  18h, 3*4      ; pData
-    movzx   edx, word [ebp + 1ch]       ; func
-    mov     [esp + 2*4], edx
-    movzx   eax, word [ebp + 1eh]       ; iArgType
-    mov     [esp + 1*4], eax
-    VBOXSF_FARPTR_2_FLAT  20h, 0*4      ; pArgdat
-    call    NAME(FS32_FSCTL)
+        VBOXSF_FARPTR_2_FLAT  08h, 8*4      ; plenDataIO
+        movzx   ecx, word [ebp + 0ch]       ; lenData
+        mov     [esp + 7*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0eh, 6*4      ; pData
+        VBOXSF_FARPTR_2_FLAT  12h, 5*4      ; plenDataIO
+        movzx   ecx, word [ebp + 16h]       ; lenData
+        mov     [esp + 4*4], ecx
+        VBOXSF_FARPTR_2_FLAT  18h, 3*4      ; pData
+        movzx   edx, word [ebp + 1ch]       ; func
+        mov     [esp + 2*4], edx
+        movzx   eax, word [ebp + 1eh]       ; iArgType
+        mov     [esp + 1*4], eax
+        VBOXSF_FARPTR_2_FLAT  20h, 0*4      ; pArgdat
+        call    NAME(FS32_FSCTL)
 VBOXSF_TO_16        FS_FSCTL
-    retf    1ch
+        retf    1ch
 VBOXSF_EP16_END FS_FSCTL
 
@@ -884,16 +1189,16 @@
 VBOXSF_EP16_BEGIN FS_FSINFO, 'FS_FSINFO'
 VBOXSF_TO_32        FS_FSINFO, 5*4
-    movzx   ecx, word [ebp + 08h]       ; level
-    mov     [esp + 10h], ecx
-    movzx   edx, word [ebp + 0ah]       ; cbData
-    mov     [esp + 0ch], edx
-    VBOXSF_FARPTR_2_FLAT  0ch, 2*4      ; pbData
-    movzx   edx, word [ebp + 10h]       ; hVPB
-    mov     [esp], edx
-    movzx   eax, word [ebp + 12h]       ; flag
-    mov     [esp], eax
-    call    NAME(FS32_FSINFO)
+        movzx   ecx, word [ebp + 08h]       ; level
+        mov     [esp + 10h], ecx
+        movzx   edx, word [ebp + 0ah]       ; cbData
+        mov     [esp + 0ch], edx
+        VBOXSF_FARPTR_2_FLAT  0ch, 2*4      ; pbData
+        movzx   edx, word [ebp + 10h]       ; hVPB
+        mov     [esp + 4], edx
+        movzx   eax, word [ebp + 12h]       ; flag
+        mov     [esp], eax
+        call    NAME(FS32_FSINFO)
 VBOXSF_TO_16        FS_FSINFO
-    retf    14h
+        retf    14h
 VBOXSF_EP16_END     FS_FSINFO
 
@@ -905,21 +1210,21 @@
 VBOXSF_EP16_BEGIN   FS_IOCTL, 'FS_IOCTL'
 VBOXSF_TO_32        FS_IOCTL, 10*4
-    VBOXSF_FARPTR_2_FLAT  08h, 9*4      ; plenDataIO
-    movzx   ecx, word [ebp + 0ch]       ; lenData
-    mov     [esp + 8*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0eh, 7*4      ; pData
-    VBOXSF_FARPTR_2_FLAT  12h, 6*4      ; plenDataIO
-    movzx   ecx, word [ebp + 16h]       ; lenData
-    mov     [esp + 5*4], ecx
-    VBOXSF_FARPTR_2_FLAT  18h, 4*4      ; pData
-    movzx   edx, word [ebp + 1ch]       ; cat
-    mov     [esp + 3*4], edx
-    movzx   eax, word [ebp + 1eh]       ; func
-    mov     [esp + 2*4], eax
-    VBOXSF_PSFFSD_2_FLAT  20h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  24h, 0*4      ; pData
-    call    NAME(FS32_IOCTL)
+        VBOXSF_FARPTR_2_FLAT  08h, 9*4      ; plenDataIO
+        movzx   ecx, word [ebp + 0ch]       ; lenData
+        mov     [esp + 8*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0eh, 7*4      ; pData
+        VBOXSF_FARPTR_2_FLAT  12h, 6*4      ; plenDataIO
+        movzx   ecx, word [ebp + 16h]       ; lenData
+        mov     [esp + 5*4], ecx
+        VBOXSF_FARPTR_2_FLAT  18h, 4*4      ; pData
+        movzx   edx, word [ebp + 1ch]       ; cat
+        mov     [esp + 3*4], edx
+        movzx   eax, word [ebp + 1eh]       ; func
+        mov     [esp + 2*4], eax
+        VBOXSF_PSFFSD_2_FLAT  20h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  24h, 0*4      ; pData
+        call    NAME(FS32_IOCTL)
 VBOXSF_TO_16        FS_IOCTL
-    retf    20h
+        retf    20h
 VBOXSF_EP16_END     FS_IOCTL
 
@@ -930,15 +1235,15 @@
 VBOXSF_EP16_BEGIN   FS_MKDIR, 'FS_MKDIR'
 VBOXSF_TO_32        FS_MKDIR, 6*4
-    movzx   ecx, word [ebp + 08h]       ; flag
-    mov     [esp + 5*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0ah, 4*4      ; pEABuf
-    movzx   edx, word [ebp + 0eh]       ; iCurDirEnd
-    mov     [esp + 3*4], edx
-    VBOXSF_FARPTR_2_FLAT  10h, 2*4      ; pszName
-    VBOXSF_PCDFSD_2_FLAT  14h, 1*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  18h, 0*4      ; pcdfsi
-    call    NAME(FS32_MKDIR)
+        movzx   ecx, word [ebp + 08h]       ; flag
+        mov     [esp + 5*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0ah, 4*4      ; pEABuf
+        movsx   edx, word [ebp + 0eh]       ; iCurDirEnd
+        mov     [esp + 3*4], edx
+        VBOXSF_FARPTR_2_FLAT  10h, 2*4      ; pszName
+        VBOXSF_PCDFSD_2_FLAT  14h, 1*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  18h, 0*4      ; pcdfsi
+        call    NAME(FS32_MKDIR)
 VBOXSF_TO_16        FS_MKDIR
-    retf    14h
+        retf    14h
 VBOXSF_EP16_END     FS_MKDIR
 
@@ -947,27 +1252,27 @@
 ; @cproto int FS_MOUNT(USHORT flag, PVPFSI pvpfsi, PVBOXSFVP pvpfsd, USHORT hVPB, PCSZ pszBoot)
 VBOXSF_EP16_BEGIN   FS_MOUNT, 'FS_MOUNT'
-    ;
-    ; Initialized ring-0 yet? (this is a likely first entry point)
-    ;
-    push    ds
-    mov     ax, DATA16
-    mov     ds, ax
-    test    byte [NAME(g_fDoneRing0)], 1
-    jnz     .DoneRing0
-    call    NAME(VBoxSFR0Init16Bit)
+        ;
+        ; Initialized ring-0 yet? (this is a likely first entry point)
+        ;
+        push    ds
+        mov     ax, DATA16
+        mov     ds, ax
+        test    byte [NAME(g_fDoneRing0)], 1
+        jnz     .DoneRing0
+        call    NAME(VBoxSFR0Init16Bit)
 .DoneRing0:
-    pop     ds
+        pop     ds
 
 VBOXSF_TO_32        FS_MOUNT, 5*4
-    VBOXSF_FARPTR_2_FLAT  08h, 4*4      ; pszBoot
-    movzx   ecx, word [ebp + 0ch]       ; hVPB
-    mov     [esp + 3*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0eh, 2*4      ; pvpfsd
-    VBOXSF_FARPTR_2_FLAT  12h, 1*4      ; pvpfsi
-    movzx   ecx, word [ebp + 16h]       ; flag
-    mov     [esp], ecx
-    call    NAME(FS32_MOUNT)
+        VBOXSF_FARPTR_2_FLAT  08h, 4*4      ; pszBoot
+        movzx   ecx, word [ebp + 0ch]       ; hVPB
+        mov     [esp + 3*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0eh, 2*4      ; pvpfsd
+        VBOXSF_FARPTR_2_FLAT  12h, 1*4      ; pvpfsi
+        movzx   ecx, word [ebp + 16h]       ; flag
+        mov     [esp], ecx
+        call    NAME(FS32_MOUNT)
 VBOXSF_TO_16        FS_MOUNT
-    retf    10h
+        retf    10h
 VBOXSF_EP16_END     FS_MOUNT
 
@@ -978,17 +1283,17 @@
 VBOXSF_EP16_BEGIN   FS_MOVE, 'FS_MOVE'
 VBOXSF_TO_32        FS_MOVE, 7*4
-    movzx   ecx, word [ebp + 08h]       ; type
-    mov     [esp + 6*4], ecx
-    movzx   edx, word [ebp + 0ah]       ; iDstCurDirEnd
-    mov     [esp + 5*4], edx
-    VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pszDst
-    movzx   eax, word [ebp + 10h]       ; iSrcCurDirEnd
-    mov     [esp + 3*4], eax
-    VBOXSF_FARPTR_2_FLAT  12h, 2*4      ; pszSrc
-    VBOXSF_PCDFSD_2_FLAT  16h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  1ah, 0*4      ; psffsi
-    call    NAME(FS32_MOVE)
+        movzx   ecx, word [ebp + 08h]       ; type
+        mov     [esp + 6*4], ecx
+        movzx   edx, word [ebp + 0ah]       ; iDstCurDirEnd
+        mov     [esp + 5*4], edx
+        VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pszDst
+        movzx   eax, word [ebp + 10h]       ; iSrcCurDirEnd
+        mov     [esp + 3*4], eax
+        VBOXSF_FARPTR_2_FLAT  12h, 2*4      ; pszSrc
+        VBOXSF_PCDFSD_2_FLAT  16h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  1ah, 0*4      ; psffsi
+        call    NAME(FS32_MOVE)
 VBOXSF_TO_16        FS_MOVE
-    retf    16h
+        retf    16h
 VBOXSF_EP16_END     FS_MOVE
 
@@ -998,14 +1303,14 @@
 VBOXSF_EP16_BEGIN   FS_NEWSIZE, 'FS_NEWSIZE'
 VBOXSF_TO_32        FS_NEWSIZE, 5*4     ; thunking to longlong edition.
-    movzx   ecx, word [ebp + 08h]       ; IOflag
-    mov     [esp + 4*4], ecx
-    mov     eax, [ebp + 0ah]            ; cbFile (ULONG -> LONGLONG)
-    mov     dword [esp + 3*4], 0
-    mov     [esp + 2*4], eax
-    VBOXSF_PSFFSD_2_FLAT  0eh, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  12h, 0*4      ; psffsi
-    call            NAME(FS32_NEWSIZEL)
+        movzx   ecx, word [ebp + 08h]       ; IOflag
+        mov     [esp + 4*4], ecx
+        mov     eax, [ebp + 0ah]            ; cbFile (ULONG -> LONGLONG)
+        mov     dword [esp + 3*4], 0
+        mov     [esp + 2*4], eax
+        VBOXSF_PSFFSD_2_FLAT  0eh, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  12h, 0*4      ; psffsi
+        call            NAME(FS32_NEWSIZEL)
 VBOXSF_TO_16        FS_NEWSIZE
-    retf    0eh
+        retf    0eh
 VBOXSF_EP16_END     FS_NEWSIZE
 
@@ -1015,15 +1320,15 @@
 VBOXSF_EP16_BEGIN FS_NEWSIZEL, 'FS_NEWSIZEL'
 VBOXSF_TO_32        FS_NEWSIZEL, 5*4
-    movzx   ecx, word [ebp + 08h]       ; IOflag
-    mov     [esp + 4*4], ecx
-    mov     eax, [ebp + 0ah]            ; cbFile
-    mov     edx, [ebp + 0eh]
-    mov     [esp + 3*4], edx
-    mov     [esp + 2*4], eax
-    VBOXSF_PSFFSD_2_FLAT  12h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  16h, 0*4      ; psffsi
-    call            NAME(FS32_NEWSIZEL)
+        movzx   ecx, word [ebp + 08h]       ; IOflag
+        mov     [esp + 4*4], ecx
+        mov     eax, [ebp + 0ah]            ; cbFile
+        mov     edx, [ebp + 0eh]
+        mov     [esp + 3*4], edx
+        mov     [esp + 2*4], eax
+        VBOXSF_PSFFSD_2_FLAT  12h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  16h, 0*4      ; psffsi
+        call            NAME(FS32_NEWSIZEL)
 VBOXSF_TO_16        FS_NEWSIZEL
-    retf    12h
+        retf    12h
 VBOXSF_EP16_END FS_NEWSIZEL
 
@@ -1034,14 +1339,14 @@
 VBOXSF_EP16_BEGIN   FS_NMPIPE, 'FS_NMPIPE'
 VBOXSF_TO_32        FS_NMPIPE, 6*4
-    VBOXSF_FARPTR_2_FLAT  08h, 5*4      ; pszName
-    VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pData
-    VBOXSF_FARPTR_2_FLAT  10h, 3*4      ; pOpRec
-    movzx   ecx, word [ebp + 14h]       ; OpType
-    mov     [esp + 2*4], ecx
-    VBOXSF_FARPTR_2_FLAT  16h, 1*4      ; psffsd (take care...)
-    VBOXSF_FARPTR_2_FLAT  1ah, 0*4      ; psffsi
-    call            NAME(FS32_NMPIPE)
+        VBOXSF_FARPTR_2_FLAT  08h, 5*4      ; pszName
+        VBOXSF_FARPTR_2_FLAT  0ch, 4*4      ; pData
+        VBOXSF_FARPTR_2_FLAT  10h, 3*4      ; pOpRec
+        movzx   ecx, word [ebp + 14h]       ; OpType
+        mov     [esp + 2*4], ecx
+        VBOXSF_FARPTR_2_FLAT  16h, 1*4      ; psffsd (take care...)
+        VBOXSF_FARPTR_2_FLAT  1ah, 0*4      ; psffsi
+        call            NAME(FS32_NMPIPE)
 VBOXSF_TO_16        FS_NMPIPE
-    retf    16h
+        retf    16h
 VBOXSF_EP16_END     FS_NMPIPE
 
@@ -1053,23 +1358,23 @@
 VBOXSF_EP16_BEGIN   FS_OPENCREATE, 'FS_OPENCREATE'
 VBOXSF_TO_32        FS_OPENCREATE, 12*4
-    VBOXSF_FARPTR_2_FLAT  08h, 11*4     ; pfgenflag
-    VBOXSF_FARPTR_2_FLAT  0ch, 10*4     ; pcEABuf
-    movzx   ecx, word [ebp + 10h]       ; usAttr
-    mov     [esp + 9*4], ecx
-    VBOXSF_FARPTR_2_FLAT  12h, 8*4      ; pusAction
-    movzx   edx, word [ebp + 16h]       ; usOpenFlag
-    mov     [esp + 7*4], edx
-    mov     eax, [ebp + 18h]            ; ulOpenMode
-    mov     [esp + 6*4], eax
-    VBOXSF_FARPTR_2_FLAT  1ch, 5*4      ; psffsd (new, no short cuts)
-    VBOXSF_FARPTR_2_FLAT  20h, 4*4      ; psffsi
-    movzx   ecx, word [ebp + 24h]       ; iCurDirEnd
-    mov     [esp + 3*4], ecx
-    VBOXSF_FARPTR_2_FLAT  26h, 2*4      ; pszName
-    VBOXSF_PCDFSD_2_FLAT  2ah, 1*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  2eh, 0*4      ; pcdfsi
-    call    NAME(FS32_OPENCREATE)
+        VBOXSF_FARPTR_2_FLAT  08h, 11*4     ; pfgenflag
+        VBOXSF_FARPTR_2_FLAT  0ch, 10*4     ; pcEABuf
+        movzx   ecx, word [ebp + 10h]       ; usAttr
+        mov     [esp + 9*4], ecx
+        VBOXSF_FARPTR_2_FLAT  12h, 8*4      ; pusAction
+        movzx   edx, word [ebp + 16h]       ; usOpenFlag
+        mov     [esp + 7*4], edx
+        mov     eax, [ebp + 18h]            ; ulOpenMode
+        mov     [esp + 6*4], eax
+        VBOXSF_FARPTR_2_FLAT  1ch, 5*4      ; psffsd (new, no short cuts)
+        VBOXSF_FARPTR_2_FLAT  20h, 4*4      ; psffsi
+        movsx   ecx, word [ebp + 24h]       ; iCurDirEnd
+        mov     [esp + 3*4], ecx
+        VBOXSF_FARPTR_2_FLAT  26h, 2*4      ; pszName
+        VBOXSF_PCDFSD_2_FLAT  2ah, 1*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  2eh, 0*4      ; pcdfsi
+        call    NAME(FS32_OPENCREATE)
 VBOXSF_TO_16        FS_OPENCREATE
-    retf    42
+        retf    42
 VBOXSF_EP16_END FS_OPENCREATE
 
@@ -1080,20 +1385,20 @@
 VBOXSF_EP16_BEGIN   FS_OPENPAGEFILE, 'FS_OPENPAGEFILE'
 VBOXSF_TO_32        FS_OPENPAGEFILE, 9*4
-    mov     ecx, [ebp + 08h]            ; Reserved
-    mov     [esp + 8*4], ecx
-    movzx   edx, word [ebp + 0ch]       ; usAttr
-    mov     [esp + 7*4], edx
-    movzx   eax, word [ebp + 0eh]       ; usOpenFlag
-    mov     [esp + 6*4], eax
-    movzx   ecx, word [ebp + 10h]       ; usOpenMode
-    mov     [esp + 5*4], ecx
-    VBOXSF_FARPTR_2_FLAT  12h, 4*4      ; psffsd (new, no short cuts)
-    VBOXSF_FARPTR_2_FLAT  16h, 3*4      ; psffsi
-    VBOXSF_FARPTR_2_FLAT  1ah, 2*4      ; pszName
-    VBOXSF_FARPTR_2_FLAT  1eh, 1*4      ; pcMaxReq
-    VBOXSF_FARPTR_2_FLAT  22h, 0*4      ; pFlag
-    call    NAME(FS32_OPENPAGEFILE)
+        mov     ecx, [ebp + 08h]            ; Reserved
+        mov     [esp + 8*4], ecx
+        movzx   edx, word [ebp + 0ch]       ; usAttr
+        mov     [esp + 7*4], edx
+        movzx   eax, word [ebp + 0eh]       ; usOpenFlag
+        mov     [esp + 6*4], eax
+        movzx   ecx, word [ebp + 10h]       ; usOpenMode
+        mov     [esp + 5*4], ecx
+        VBOXSF_FARPTR_2_FLAT  12h, 4*4      ; psffsd (new, no short cuts)
+        VBOXSF_FARPTR_2_FLAT  16h, 3*4      ; psffsi
+        VBOXSF_FARPTR_2_FLAT  1ah, 2*4      ; pszName
+        VBOXSF_FARPTR_2_FLAT  1eh, 1*4      ; pcMaxReq
+        VBOXSF_FARPTR_2_FLAT  22h, 0*4      ; pFlag
+        call    NAME(FS32_OPENPAGEFILE)
 VBOXSF_TO_16        FS_OPENPAGEFILE
-    retf    1eh
+        retf    1eh
 VBOXSF_EP16_END     FS_OPENPAGEFILE
 
@@ -1104,19 +1409,19 @@
 VBOXSF_EP16_BEGIN   FS_PATHINFO, 'FS_PATHINFO'
 VBOXSF_TO_32        FS_PATHINFO, 8*4
-    movzx   ecx, word [ebp + 08h]       ; cbData
-    mov     [esp + 7*4], ecx
-    VBOXSF_FARPTR_2_FLAT  0ah, 6*4      ; pData
-    movzx   edx, word [ebp + 0eh]       ; level
-    mov     [esp + 5*4], edx
-    movzx   eax, word [ebp + 10h]       ; iCurDirEnd
-    mov     [esp + 4*4], eax
-    VBOXSF_FARPTR_2_FLAT  12h, 3*4      ; pszName
-    VBOXSF_PCDFSD_2_FLAT  16h, 2*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  1ah, 1*4      ; pcdfsi
-    movzx   edx, word [ebp + 1eh]       ; flag
-    mov     [esp], edx
-    call    NAME(FS32_PATHINFO)
+        movzx   ecx, word [ebp + 08h]       ; cbData
+        mov     [esp + 7*4], ecx
+        VBOXSF_FARPTR_2_FLAT  0ah, 6*4      ; pData
+        movzx   edx, word [ebp + 0eh]       ; level
+        mov     [esp + 5*4], edx
+        movsx   eax, word [ebp + 10h]       ; iCurDirEnd
+        mov     [esp + 4*4], eax
+        VBOXSF_FARPTR_2_FLAT  12h, 3*4      ; pszName
+        VBOXSF_PCDFSD_2_FLAT  16h, 2*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  1ah, 1*4      ; pcdfsi
+        movzx   edx, word [ebp + 1eh]       ; flag
+        mov     [esp], edx
+        call    NAME(FS32_PATHINFO)
 VBOXSF_TO_16        FS_PATHINFO
-    retf    18h
+        retf    18h
 VBOXSF_EP16_END     FS_PATHINFO
 
@@ -1125,8 +1430,8 @@
 VBOXSF_EP16_BEGIN FS_PROCESSNAME, 'FS_PROCESSNAME'
 VBOXSF_TO_32        FS_PROCESSNAME, 1*4
-    VBOXSF_FARPTR_2_FLAT  08h, 0*4      ; pszName
-    call    NAME(FS32_PROCESSNAME)
+        VBOXSF_FARPTR_2_FLAT  08h, 0*4      ; pszName
+        call    NAME(FS32_PROCESSNAME)
 VBOXSF_TO_16        FS_PROCESSNAME
-    retf    4h
+        retf    4h
 VBOXSF_EP16_END FS_PROCESSNAME
 
@@ -1136,30 +1441,28 @@
 VBOXSF_EP16_BEGIN   FS_READ, 'FS_READ'
 VBOXSF_TO_32        FS_READ, 6*4        ; extra local for ULONG cbDataTmp.
-    movzx   ecx, word [ebp + 08h]       ; IOflag
-    mov     [esp + 4*4], ecx
-    les     dx, [ebp + 0ah]             ; cbDataTmp = *pcbData;
-    movzx   edx, dx
-    lea     ecx, [esp + 5*4]            ; pcbData = &cbDataTmp
-    movzx   eax, word [es:edx]
-    mov     [ecx], eax
-    mov     [esp + 3*4], ecx
-    mov     edx, DATA32
-    mov     es, edx
-    VBOXSF_FARPTR_2_FLAT  0eh, 2*4      ; pbData
-    VBOXSF_PSFFSD_2_FLAT  12h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  16h, 0*4      ; psffsi
-    call    FS32_READ
-
-    les     dx, [ebp + 0ah]             ; *pcbData = cbDataTmp;
-    movzx   edx, dx
-    mov     cx, [esp + 5*4]
-    mov     [es:edx], cx
-    mov     edx, DATA32
-    mov     es, edx
+        movzx   ecx, word [ebp + 08h]       ; IOflag
+        mov     [esp + 4*4], ecx
+        les     dx, [ebp + 0ah]             ; cbDataTmp = *pcbData;
+        movzx   edx, dx
+        lea     ecx, [esp + 5*4]            ; pcbData = &cbDataTmp
+        movzx   eax, word [es:edx]
+        mov     [ecx], eax
+        mov     [esp + 3*4], ecx
+        mov     edx, DATA32
+        mov     es, edx
+        VBOXSF_FARPTR_2_FLAT  0eh, 2*4      ; pbData
+        VBOXSF_PSFFSD_2_FLAT  12h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  16h, 0*4      ; psffsi
+        call    FS32_READ
+
+        les     dx, [ebp + 0ah]             ; *pcbData = cbDataTmp;
+        movzx   edx, dx
+        mov     cx, [esp + 5*4]
+        mov     [es:edx], cx
+        mov     edx, DATA32
+        mov     es, edx
 
 VBOXSF_TO_16        FS_READ
-
-    pop     es
-    retf    12h
+        retf    12h
 VBOXSF_EP16_END     FS_READ
 
@@ -1170,12 +1473,12 @@
 VBOXSF_EP16_BEGIN   FS_RMDIR, 'FS_RMDIR'
 VBOXSF_TO_32        FS_RMDIR, 4*4
-    movzx   edx, word [ebp + 08h]       ; iCurDirEnd
-    mov     [esp + 3*4], edx
-    VBOXSF_FARPTR_2_FLAT  0ah, 2*4      ; pszName
-    VBOXSF_PCDFSD_2_FLAT  0eh, 1*4      ; pcdfsd
-    VBOXSF_FARPTR_2_FLAT  12h, 0*4      ; pcdfsi
-    call    NAME(FS32_RMDIR)
+        movsx   edx, word [ebp + 08h]       ; iCurDirEnd
+        mov     [esp + 3*4], edx
+        VBOXSF_FARPTR_2_FLAT  0ah, 2*4      ; pszName
+        VBOXSF_PCDFSD_2_FLAT  0eh, 1*4      ; pcdfsd
+        VBOXSF_FARPTR_2_FLAT  12h, 0*4      ; pcdfsi
+        call    NAME(FS32_RMDIR)
 VBOXSF_TO_16        FS_RMDIR
-    retf    14h
+        retf    0eh
 VBOXSF_EP16_END     FS_RMDIR
 
@@ -1186,9 +1489,9 @@
 VBOXSF_EP16_BEGIN FS_SETSWAP, 'FS_SETSWAP'
 VBOXSF_TO_32        FS_SETSWAP, 2*4
-    VBOXSF_PSFFSD_2_FLAT  08h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  0ch, 0*4      ; psffsi
-    call    NAME(FS32_SETSWAP)
+        VBOXSF_PSFFSD_2_FLAT  08h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  0ch, 0*4      ; psffsi
+        call    NAME(FS32_SETSWAP)
 VBOXSF_TO_16        FS_SETSWAP
-    retf    8h
+        retf    8h
 VBOXSF_EP16_END     FS_SETSWAP
 
@@ -1199,12 +1502,26 @@
 VBOXSF_EP16_BEGIN   FS_SHUTDOWN, 'FS_SHUTDOWN'
 VBOXSF_TO_32        FS_SHUTDOWN, 3*4
-    mov     ecx, [ebp + 08h]            ; type
-    mov     [esp + 1*4], edx
-    movzx   edx, word [ebp + 0ah]       ; reserved
-    mov     [esp], eax
-    call    NAME(FS32_SHUTDOWN)
+        mov     ecx, [ebp + 0ch]            ; type
+        mov     [esp + 1*4], edx
+        movzx   edx, word [ebp + 08h]       ; reserved
+        mov     [esp], eax
+        call    NAME(FS32_SHUTDOWN)
 VBOXSF_TO_16        FS_SHUTDOWN
-    retf    6h
+        retf    6h
 VBOXSF_EP16_END     FS_SHUTDOWN
+
+
+;;
+; @cproto int FS_VERIFYUNCNAME(USHORT type, PCSZ pszName);
+;
+VBOXSF_EP16_BEGIN   FS_VERIFYUNCNAME, 'FS_VERIFYUNCNAME'
+VBOXSF_TO_32        FS_VERIFYUNCNAME, 3*4
+        VBOXSF_FARPTR_2_FLAT  08h, 1*4      ; pszDev
+        movzx   ecx, word [ebp + 0ch]       ; fFlag
+        mov     [esp], ecx
+        call    NAME(FS32_VERIFYUNCNAME)
+VBOXSF_TO_16        FS_VERIFYUNCNAME
+        retf    6h
+VBOXSF_EP16_END     FS_VERIFYUNCNAME
 
 
@@ -1213,32 +1530,85 @@
 VBOXSF_EP16_BEGIN   FS_WRITE, 'FS_WRITE'
 VBOXSF_TO_32        FS_WRITE, 6*4       ; extra local for ULONG cbDataTmp.
-    movzx   ecx, word [ebp + 08h]       ; IOflag
-    mov     [esp + 4*4], ecx
-    les     dx, [ebp + 0ah]             ; cbDataTmp = *pcbData;
-    movzx   edx, dx
-    lea     ecx, [esp + 5*4]            ; pcbData = &cbDataTmp
-    movzx   eax, word [es:edx]
-    mov     [ecx], eax
-    mov     [esp + 3*4], ecx
-    mov     edx, DATA32
-    mov     es, edx
-    VBOXSF_FARPTR_2_FLAT  0eh, 2*4      ; pbData
-    VBOXSF_PSFFSD_2_FLAT  12h, 1*4      ; psffsd
-    VBOXSF_FARPTR_2_FLAT  16h, 0*4      ; psffsi
-    call    FS32_WRITE
-
-    les     dx, [ebp + 0ah]             ; *pcbData = cbDataTmp;
-    movzx   edx, dx
-    mov     cx, [esp + 5*4]
-    mov     [es:edx], cx
-    mov     edx, DATA32
-    mov     es, edx
+        movzx   ecx, word [ebp + 08h]       ; IOflag
+        mov     [esp + 4*4], ecx
+        les     dx, [ebp + 0ah]             ; cbDataTmp = *pcbData;
+        movzx   edx, dx
+        lea     ecx, [esp + 5*4]            ; pcbData = &cbDataTmp
+        movzx   eax, word [es:edx]
+        mov     [ecx], eax
+        mov     [esp + 3*4], ecx
+        mov     edx, DATA32
+        mov     es, edx
+        VBOXSF_FARPTR_2_FLAT  0eh, 2*4      ; pbData
+        VBOXSF_PSFFSD_2_FLAT  12h, 1*4      ; psffsd
+        VBOXSF_FARPTR_2_FLAT  16h, 0*4      ; psffsi
+        call    FS32_WRITE
+
+        les     dx, [ebp + 0ah]             ; *pcbData = cbDataTmp;
+        movzx   edx, dx
+        mov     cx, [esp + 5*4]
+        mov     [es:edx], cx
+        mov     edx, DATA32
+        mov     es, edx
 
 VBOXSF_TO_16        FS_WRITE
-
-    pop     es
-    retf    12h
+        retf    12h
 VBOXSF_EP16_END     FS_WRITE
 
+
+;
+;
+; Calling 16-bit kernel code.
+;
+;
+
+BEGINCODE
+
+;;
+; Wrapper around FSH_GETVOLPARM.
+;
+; @returns  VPBFSD.
+; @param    hVbp            The volume handle to resolve.
+; @param    ppVbpFsi
+;
+BEGINPROC       Fsh32GetVolParams
+VBOXSF_FROM_32  Fsh32GetVolParams, 2*4
+        mov     di, sp                  ; make the top of the stack addressable via di
+
+        mov     [ss:di], eax            ; clear the return variables
+        mov     [ss:di + 4], eax
+
+        mov     ax, [bp + 8]            ; hVbp
+        push    ax
+
+        lea     ax, [ss:di]             ; &hvfsi
+        push    ss
+        push    ax
+
+        lea     ax, [ss:di + 4]         ; &hvfsd
+        push    ss
+        push    ax
+
+        call far FSH_GETVOLPARM
+
+        mov     sp, di                  ; paranoia (pascal pops params)
+
+VBOXSF_FROM_16_SWITCH Fsh32GetVolParams
+
+        ; Convert vpfsi to flat and store it in return location.
+        mov     ebx, [ebp + 0ch]
+        test    ebx, ebx
+        jz      .no_vpfsi
+        call    KernSelToFlat
+        mov     [ebx], eax
+.no_vpfsi:
+        add     esp, 4
+
+        ; Convert vpfsd to flat and return it.
+        call    KernSelToFlat
+
+VBOXSF_FROM_16_EPILOGUE
+        ret
+ENDPROC     Fsh32GetVolParams
 
 
@@ -1264,123 +1634,123 @@
 VBOXSF_EP16_BEGIN FS_INIT, 'FS_INIT'
 ;    DEBUG_STR16 'VBoxSF: FS_INIT - enter'
-    push    ebp
-    mov     ebp, esp
-    push    ds                          ; bp - 02h
-    push    es                          ; bp - 04h
-    push    esi                         ; bp - 08h
-    push    edi                         ; bp - 0ch
-
-    mov     ax, DATA16
-    mov     ds, ax
-    mov     es, ax
-
-    ;
-    ; Save the device help entry point.
-    ;
-    mov     eax, [bp + 0ch]
-    mov     [NAME(g_fpfnDevHlp)], eax
-
-    ;
-    ; Parse the command line.
-    ; Doing this in assembly is kind of ugly...
-    ;
-    cmp     word [bp + 10h + 2], 3
-    jbe near .no_command_line
-    lds     si, [bp + 10h]              ; ds:si -> command line iterator.
+        push    ebp
+        mov     ebp, esp
+        push    ds                          ; bp - 02h
+        push    es                          ; bp - 04h
+        push    esi                         ; bp - 08h
+        push    edi                         ; bp - 0ch
+
+        mov     ax, DATA16
+        mov     ds, ax
+        mov     es, ax
+
+        ;
+        ; Save the device help entry point.
+        ;
+        mov     eax, [bp + 0ch]
+        mov     [NAME(g_fpfnDevHlp)], eax
+
+        ;
+        ; Parse the command line.
+        ; Doing this in assembly is kind of ugly...
+        ;
+        cmp     word [bp + 10h + 2], 3
+        jbe near .no_command_line
+        lds     si, [bp + 10h]              ; ds:si -> command line iterator.
 .parse_next:
 
-    ; skip leading blanks.
+        ; skip leading blanks.
 .parse_next_char:
-    mov     di, si                      ; DI = start of argument.
-    lodsb
-    cmp     al, ' '
-    je      .parse_next_char
-    cmp     al, 9                       ; tab
-    je      .parse_next_char
-    cmp     al, 0
-    je near .parse_done
-
-    ; check for '/' or '-'
-    cmp     al, '/'
-    je      .parse_switch
-    cmp     al, '-'
-    je      .parse_switch
-    jmp     .parse_error
-
-    ; parse switches.
+        mov     di, si                      ; DI = start of argument.
+        lodsb
+        cmp     al, ' '
+        je      .parse_next_char
+        cmp     al, 9                       ; tab
+        je      .parse_next_char
+        cmp     al, 0
+        je near .parse_done
+
+        ; check for '/' or '-'
+        cmp     al, '/'
+        je      .parse_switch
+        cmp     al, '-'
+        je      .parse_switch
+        jmp     .parse_error
+
+        ; parse switches.
 .parse_switch:
-    lodsb
-    cmp     al, 0
-    je      .parse_error
-    and     al, ~20h                    ; uppercase
-
-    cmp     al, 'V'                     ; /V - verbose
-    je      .parse_verbose
-    cmp     al, 'Q'                     ; /Q - quiet.
-    je      .parse_quiet
-    jmp     .parse_error
+        lodsb
+        cmp     al, 0
+        je      .parse_error
+        and     al, ~20h                    ; uppercase
+
+        cmp     al, 'V'                     ; /V - verbose
+        je      .parse_verbose
+        cmp     al, 'Q'                     ; /Q - quiet.
+        je      .parse_quiet
+        jmp     .parse_error
 
 .parse_verbose:
-    mov     byte [es:NAME(g_fVerbose)], 1
-    jmp     .parse_next
+        mov     byte [es:NAME(g_fVerbose)], 1
+        jmp     .parse_next
 
 .parse_quiet:
-    mov     byte [es:NAME(g_fVerbose)], 0
-    jmp     .parse_next
+        mov     byte [es:NAME(g_fVerbose)], 0
+        jmp     .parse_next
 
 .parse_error:
 segment DATA16
 .szSyntaxError:
-    db 0dh, 0ah, 'VBoxSF.ifs: command line parse error at: ', 0
+        db 0dh, 0ah, 'VBoxSF.ifs: command line parse error at: ', 0
 .szNewLine:
-    db 0dh, 0ah, 0dh, 0ah, 0
+        db 0dh, 0ah, 0dh, 0ah, 0
 segment CODE16
-    mov     bx, .szSyntaxError
-    call    NAME(FS_INIT_FPUTS)
-
-    push    es
-    push    ds
-    pop     es
-    mov     bx, di
-    call    NAME(FS_INIT_FPUTS)
-    pop     es
-
-    mov     bx, .szNewLine
-    call    NAME(FS_INIT_FPUTS)
-
-    mov     ax, ERROR_INVALID_PARAMETER
-    jmp     .done
+        mov     bx, .szSyntaxError
+        call    NAME(FS_INIT_FPUTS)
+
+        push    es
+        push    ds
+        pop     es
+        mov     bx, di
+        call    NAME(FS_INIT_FPUTS)
+        pop     es
+
+        mov     bx, .szNewLine
+        call    NAME(FS_INIT_FPUTS)
+
+        mov     ax, ERROR_INVALID_PARAMETER
+        jmp     .done
 
 .parse_done:
-    mov     ax, DATA16
-    mov     ds, ax
+        mov     ax, DATA16
+        mov     ds, ax
 .no_command_line:
 
-    ;
-    ; Write our greeting to STDOUT.
-    ; APIRET  _Pascal DosWrite(HFILE hf, PVOID pvBuf, USHORT cbBuf, PUSHORT pcbBytesWritten);
-    ;
-    cmp     byte [NAME(g_fVerbose)], 0
-    je near .quiet
+        ;
+        ; Write our greeting to STDOUT.
+        ; APIRET  _Pascal DosWrite(HFILE hf, PVOID pvBuf, USHORT cbBuf, PUSHORT pcbBytesWritten);
+        ;
+        cmp     byte [NAME(g_fVerbose)], 0
+        je near .quiet
 segment DATA16
 .szMessage:
-    db 'VirtualBox Guest Additions IFS for OS/2', 0dh, 0ah, 0
+        db 'VirtualBox Guest Additions IFS for OS/2', 0dh, 0ah, 0
 segment CODE16
-    mov     bx, .szMessage
-    call    NAME(FS_INIT_FPUTS)
+        mov     bx, .szMessage
+        call    NAME(FS_INIT_FPUTS)
 .quiet:
 
-    ; return success.
-    xor     eax, eax
+        ; return success.
+        xor     eax, eax
 .done:
-    lea     sp, [bp - 0ch]
-    pop     edi
-    pop     esi
-    pop     es
-    pop     ds
-    mov     esp, ebp
-    pop     ebp
-    DEBUG_STR16 'VBoxSF: FS_INIT - leave'
-    retf    0ch
+        lea     sp, [bp - 0ch]
+        pop     edi
+        pop     esi
+        pop     es
+        pop     ds
+        mov     esp, ebp
+        pop     ebp
+        DEBUG_STR16 'VBoxSF: FS_INIT - leave'
+        retf    0ch
 VBOXSF_EP16_END FS_INIT
 
@@ -1392,61 +1762,61 @@
 ; @uses     nothing.
 GLOBALNAME FS_INIT_FPUTS
-    push    bp
-    mov     bp, sp
-    push    es                          ; bp - 02h
-    push    ds                          ; bp - 04h
-    push    ax                          ; bp - 06h
-    push    bx                          ; bp - 08h
-    push    cx                          ; bp - 0ah
-    push    dx                          ; bp - 0ch
-    push    si                          ; bp - 0eh
-    push    di                          ; bp - 10h
-
-    ; cx = strlen(es:bx)
-    xor     al, al
-    mov     di, bx
-    mov     cx, 0ffffh
-    cld
-    repne scasb
-    not     cx
-    dec     cx
-
-    ; APIRET  _Pascal DosWrite(HFILE hf, PVOID pvBuf, USHORT cbBuf, PUSHORT pcbBytesWritten);
-    push    cx
-    mov     ax, sp                      ; cbBytesWritten
-    push    1                           ; STDOUT
-    push    es                          ; pvBuf
-    push    bx
-    push    cx                          ; cbBuf
-    push    ss                          ; pcbBytesWritten
-    push    ax
+        push    bp
+        mov     bp, sp
+        push    es                          ; bp - 02h
+        push    ds                          ; bp - 04h
+        push    ax                          ; bp - 06h
+        push    bx                          ; bp - 08h
+        push    cx                          ; bp - 0ah
+        push    dx                          ; bp - 0ch
+        push    si                          ; bp - 0eh
+        push    di                          ; bp - 10h
+
+        ; cx = strlen(es:bx)
+        xor     al, al
+        mov     di, bx
+        mov     cx, 0ffffh
+        cld
+        repne scasb
+        not     cx
+        dec     cx
+
+        ; APIRET  _Pascal DosWrite(HFILE hf, PVOID pvBuf, USHORT cbBuf, PUSHORT pcbBytesWritten);
+        push    cx
+        mov     ax, sp                      ; cbBytesWritten
+        push    1                           ; STDOUT
+        push    es                          ; pvBuf
+        push    bx
+        push    cx                          ; cbBuf
+        push    ss                          ; pcbBytesWritten
+        push    ax
 %if 0 ; wlink/nasm generates a non-aliased fixup here which results in 16-bit offset with the flat 32-bit selector.
-    call far DOS16WRITE
+        call far DOS16WRITE
 %else
-    ; convert flat pointer to a far pointer using the tiled algorithm.
-    mov     ax, DATA32 wrt FLAT
-    mov     ds, ax
-    mov     eax, g_pfnDos16Write wrt FLAT
-    movzx   eax, word [eax + 2]                     ; High word of the flat address (in DATA32).
-    shl     ax, 3
-    or      ax, 0007h
-    mov     dx, DATA16
-    mov     ds, dx
-    mov     [NAME(g_fpfnDos16Write) + 2], ax        ; Update the selector (in DATA16).
-    ; do the call
-    call far [NAME(g_fpfnDos16Write)]
+        ; convert flat pointer to a far pointer using the tiled algorithm.
+        mov     ax, DATA32 wrt FLAT
+        mov     ds, ax
+        mov     eax, g_pfnDos16Write wrt FLAT
+        movzx   eax, word [eax + 2]                     ; High word of the flat address (in DATA32).
+        shl     ax, 3
+        or      ax, 0007h
+        mov     dx, DATA16
+        mov     ds, dx
+        mov     [NAME(g_fpfnDos16Write) + 2], ax        ; Update the selector (in DATA16).
+        ; do the call
+        call far [NAME(g_fpfnDos16Write)]
 %endif
 
-    lea     sp, [bp - 10h]
-    pop     di
-    pop     si
-    pop     dx
-    pop     cx
-    pop     bx
-    pop     ax
-    pop     ds
-    pop     es
-    pop     bp
-    ret
+        lea     sp, [bp - 10h]
+        pop     di
+        pop     si
+        pop     dx
+        pop     cx
+        pop     bx
+        pop     ax
+        pop     ds
+        pop     es
+        pop     bp
+        ret
 ENDPROC FS_INIT_FPUTS
 
@@ -1459,87 +1829,87 @@
 ;
 GLOBALNAME VBoxSFR0Init16Bit
-    DEBUG_STR16 'VBoxSF: VBoxSFR0Init16Bit - enter'
-    push    ds
-    push    es
-    push    fs
-    push    gs
-    push    esi
-    push    edi
-    push    ebp
-    mov     ebp, esp
-    and     sp, 0fffch
-
-    ;
-    ; Only try once.
-    ;
-    mov     ax, DATA16
-    mov     ds, ax
-    mov     byte [NAME(g_fDoneRing0)], 1
-
-    ;
-    ; Try attach to the VBoxGuest driver.
-    ;
-    mov     bx, NAME(g_szVBoxGuestName)
-    mov     di, NAME(g_VBoxGuestAttachDD)
-    mov     dl, DevHlp_AttachDD
-    call far [NAME(g_fpfnDevHlp)]
-    jc      .attach_attempt_done
-
-    push    seg NAME(g_VBoxGuestIDC)
-    push    NAME(g_VBoxGuestIDC)
-    call far [NAME(g_VBoxGuestAttachDD) + 6]
+        DEBUG_STR16 'VBoxSF: VBoxSFR0Init16Bit - enter'
+        push    ds
+        push    es
+        push    fs
+        push    gs
+        push    esi
+        push    edi
+        push    ebp
+        mov     ebp, esp
+        and     sp, 0fffch
+
+        ;
+        ; Only try once.
+        ;
+        mov     ax, DATA16
+        mov     ds, ax
+        mov     byte [NAME(g_fDoneRing0)], 1
+
+        ;
+        ; Try attach to the VBoxGuest driver.
+        ;
+        mov     bx, NAME(g_szVBoxGuestName)
+        mov     di, NAME(g_VBoxGuestAttachDD)
+        mov     dl, DevHlp_AttachDD
+        call far [NAME(g_fpfnDevHlp)]
+        jc      .attach_attempt_done
+
+        push    seg NAME(g_VBoxGuestIDC)
+        push    NAME(g_VBoxGuestIDC)
+        call far [NAME(g_VBoxGuestAttachDD) + 6]
 .attach_attempt_done:
 
 %ifndef DONT_LOCK_SEGMENTS
-    ;
-    ; Lock the two 16-bit segments.
-    ;
-    push    DATA16
-    call far FSH_FORCENOSWAP
-    push    CODE16
-    call far FSH_FORCENOSWAP
-    ; Wonder if this'll work if wlink could mark the two segments as ALIASed...
-    ;push DATA32
-    ;call far FSH_FORCENOSWAP
-    ;push TEXT32
-    ;call far FSH_FORCENOSWAP
+        ;
+        ; Lock the two 16-bit segments.
+        ;
+        push    DATA16
+        call far FSH_FORCENOSWAP
+        push    CODE16
+        call far FSH_FORCENOSWAP
+        ; Wonder if this'll work if wlink could mark the two segments as ALIASed...
+        ;push DATA32
+        ;call far FSH_FORCENOSWAP
+        ;push TEXT32
+        ;call far FSH_FORCENOSWAP
 %endif
 
-    ;
-    ; Do 32-bit ring-0 init.
-    ;
-    ;jmp far dword NAME(VBoxSFR0Init16Bit_32) wrt FLAT
-    db      066h
-    db      0eah
-    dd      NAME(VBoxSFR0Init16Bit_32) ;wrt FLAT
-    dw      TEXT32 wrt FLAT
+        ;
+        ; Do 32-bit ring-0 init.
+        ;
+        ;jmp far dword NAME(VBoxSFR0Init16Bit_32) wrt FLAT
+        db      066h
+        db      0eah
+        dd      NAME(VBoxSFR0Init16Bit_32) ;wrt FLAT
+        dw      TEXT32 wrt FLAT
 segment TEXT32
 GLOBALNAME VBoxSFR0Init16Bit_32
-    mov     ax, DATA32 wrt FLAT
-    mov     ds, ax
-    mov     es, ax
-
-    call    KernThunkStackTo32
-    call    NAME(VBoxSFR0Init)
-    call    KernThunkStackTo16
-
-    ;jmp far dword NAME(VBoxSFR0Init16Bit_16) wrt CODE16
-    db      066h
-    db      0eah
-    dw      NAME(VBoxSFR0Init16Bit_16) wrt CODE16
-    dw      CODE16
+        mov     ax, DATA32 wrt FLAT
+        mov     ds, ax
+        mov     es, ax
+
+        call    KernThunkStackTo32
+        call    NAME(VBoxSFR0Init)
+        call    KernThunkStackTo16
+
+        ;jmp far dword NAME(VBoxSFR0Init16Bit_16) wrt CODE16
+        db      066h
+        db      0eah
+        dw      NAME(VBoxSFR0Init16Bit_16) wrt CODE16
+        dw      CODE16
 segment CODE16
 GLOBALNAME VBoxSFR0Init16Bit_16
 
-    mov     esp, ebp
-    pop     ebp
-    pop     edi
-    pop     esi
-    pop     gs
-    pop     fs
-    pop     es
-    pop     ds
-    DEBUG_STR16 'VBoxSF: VBoxSFR0Init16Bit - leave'
-    ret
+        mov     esp, ebp
+        pop     ebp
+        pop     edi
+        pop     esi
+        pop     gs
+        pop     fs
+        pop     es
+        pop     ds
+        DEBUG_STR16 'VBoxSF: VBoxSFR0Init16Bit - leave'
+        ret
 ENDPROC VBoxSFR0Init16Bit
 
@@ -1551,27 +1921,37 @@
 ;
 GLOBALNAME dbgstr16
-    push    ds
-    push    ebx
-    push    edx
-
-    mov     bx, ax
-    mov     dx, 0504h                   ; RTLOG_DEBUG_PORT
-    mov     ax, DATA16
-    mov     ds, ax
+        push    ds
+        push    ebx
+        push    edx
+
+        mov     bx, ax
+        mov     dx, 0504h                   ; RTLOG_DEBUG_PORT
+        mov     ax, DATA16
+        mov     ds, ax
 
 .next:
-    mov     al, [bx]
-    or      al, al
-    jz      .done
-    inc     bx
-    out     dx, al
-    jmp     .next
+        mov     al, [bx]
+        or      al, al
+        jz      .done
+        inc     bx
+        out     dx, al
+        jmp     .next
 
 .done:
-    pop     edx
-    pop     ebx
-    pop     ds
-    ret
+        pop     edx
+        pop     ebx
+        pop     ds
+        ret
 ENDPROC dbgstr16
 %endif
 
+
+%ifdef WITH_DWARF
+;
+; Close debug info
+;
+segment _debug_info
+        db  0
+g_dwarf_compile_unit_end:
+%endif
+
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp	(revision 75336)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp	(revision 75337)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ * Copyright (c) 2007-2018 knut st. osmundsen <bird-src-spam@anduin.net>
  *
  * Permission is hereby granted, free of charge, to any person
@@ -37,86 +37,844 @@
 
 #include <VBox/log.h>
+#include <iprt/asm.h>
 #include <iprt/assert.h>
-
-
-
-DECLASM(int)
-FS32_OPENCREATE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd,
-                PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG uOpenMode, USHORT fOpenFlag,
-                PUSHORT puAction, USHORT fAttr, PBYTE pbEABuf, PUSHORT pfGenFlag)
-{
-    NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszName); NOREF(iCurDirEnd); NOREF(psffsi); NOREF(psffsd); NOREF(uOpenMode);
-    NOREF(fOpenFlag); NOREF(puAction); NOREF(fAttr); NOREF(pbEABuf); NOREF(pfGenFlag);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_CLOSE(ULONG uType, ULONG fIoFlags, PSFFSI psffsi, PVBOXSFFSD psffsd)
-{
-    NOREF(uType); NOREF(fIoFlags); NOREF(psffsi); NOREF(psffsd);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_COMMIT(ULONG uType, ULONG fIoFlags, PSFFSI psffsi, PVBOXSFFSD psffsd)
-{
-    NOREF(uType); NOREF(fIoFlags); NOREF(psffsi); NOREF(psffsd);
-    return ERROR_NOT_SUPPORTED;
+#include <iprt/mem.h>
+
+
+
+DECLASM(APIRET)
+FS32_OPENCREATE(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszName, LONG offCurDirEnd,
+                PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, ULONG fOpenMode, USHORT fOpenFlags,
+                PUSHORT puAction, ULONG fAttribs, BYTE const *pbEaBuf, PUSHORT pfGenFlag)
+{
+    LogFlow(("FS32_OPENCREATE: pCdFsi=%p pCdFsd=%p pszName=%p:{%s} offCurDirEnd=%d pSfFsi=%p pSfFsd=%p fOpenMode=%#x fOpenFlags=%#x puAction=%p fAttribs=%#x pbEaBuf=%p pfGenFlag=%p\n",
+             pCdFsi, pCdFsd, pszName, pszName, offCurDirEnd, pSfFsi, pSfFsd, fOpenMode, fOpenFlags, puAction, fAttribs, pbEaBuf, pfGenFlag));
+    RT_NOREF(pfGenFlag);
+
+    /*
+     * Validate and convert parameters.
+     */
+    /* No EAs. */
+    if (!pbEaBuf)
+    { /* likely */ }
+    else
+    {
+        LogRel(("FS32_OPENCREATE: Returns ERROR_EAS_NOT_SUPPORTED [%p];\n", pbEaBuf));
+        return ERROR_EAS_NOT_SUPPORTED;
+    }
+
+    /* No direct access. */
+    if (!(fOpenMode & OPEN_FLAGS_DASD))
+    { /* likely */ }
+    else
+    {
+        LogRel(("FS32_OPENCREATE: Returns ERROR_ACCESS_DENIED [DASD];\n"));
+        return ERROR_ACCESS_DENIED;
+    }
+
+    SHFLCREATEPARMS *pParams = (SHFLCREATEPARMS *)VbglR0PhysHeapAlloc(sizeof(*pParams));
+    if (!pParams)
+        return ERROR_NOT_ENOUGH_MEMORY;
+    RT_ZERO(*pParams);
+
+    /* access: */
+    if (fOpenMode & OPEN_ACCESS_READWRITE)
+        pParams->CreateFlags = SHFL_CF_ACCESS_READWRITE | SHFL_CF_ACCESS_ATTR_READWRITE;
+    else if (fOpenMode & OPEN_ACCESS_WRITEONLY)
+        pParams->CreateFlags = SHFL_CF_ACCESS_WRITE     | SHFL_CF_ACCESS_ATTR_WRITE;
+    else
+        pParams->CreateFlags = SHFL_CF_ACCESS_READ      | SHFL_CF_ACCESS_ATTR_READ; /* read or/and exec */
+
+    /* Sharing: */
+    switch (fOpenMode & (OPEN_SHARE_DENYNONE | OPEN_SHARE_DENYREADWRITE | OPEN_SHARE_DENYREAD | OPEN_SHARE_DENYWRITE))
+    {
+        case OPEN_SHARE_DENYNONE:       pParams->CreateFlags |= SHFL_CF_ACCESS_DENYNONE; break;
+        case OPEN_SHARE_DENYWRITE:      pParams->CreateFlags |= SHFL_CF_ACCESS_DENYWRITE; break;
+        case OPEN_SHARE_DENYREAD:       pParams->CreateFlags |= SHFL_CF_ACCESS_DENYREAD; break;
+        case OPEN_SHARE_DENYREADWRITE:  pParams->CreateFlags |= SHFL_CF_ACCESS_DENYALL; break;
+        case 0:                         pParams->CreateFlags |= SHFL_CF_ACCESS_DENYWRITE; break; /* compatibility */
+        default:
+            LogRel(("FS32_OPENCREATE: Invalid file sharing mode: %#x\n", fOpenMode));
+            VbglR0PhysHeapFree(pParams);
+            return VERR_INVALID_PARAMETER;
+
+    }
+
+    /* How to open the file: */
+    switch (fOpenFlags & 0x13)
+    {
+        case                        OPEN_ACTION_FAIL_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW:      /* 0x00 */
+            pParams->CreateFlags |= SHFL_CF_ACT_FAIL_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW;
+            break;
+        case                        OPEN_ACTION_FAIL_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW:    /* 0x10 */
+            pParams->CreateFlags |= SHFL_CF_ACT_FAIL_IF_EXISTS | SHFL_CF_ACT_CREATE_IF_NEW;
+            break;
+        case                        OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW:      /* 0x01 */
+            pParams->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW;
+            break;
+        case                        OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW:    /* 0x11 */
+            pParams->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_CREATE_IF_NEW;
+            break;
+        case                        OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW:   /* 0x02 */
+            pParams->CreateFlags |= SHFL_CF_ACT_REPLACE_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW;
+            break;
+        case                        OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW: /* 0x12 */
+            pParams->CreateFlags |= SHFL_CF_ACT_REPLACE_IF_EXISTS | SHFL_CF_ACT_CREATE_IF_NEW;
+            break;
+        default:
+            LogRel(("FS32_OPENCREATE: Invalid file open flags: %#x\n", fOpenFlags));
+            VbglR0PhysHeapFree(pParams);
+            return VERR_INVALID_PARAMETER;
+    }
+
+    /* Misc: cache, etc? There seems to be no API for that. */
+
+    /* Attributes: */
+    pParams->Info.Attr.fMode = ((uint32_t)fAttribs << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_OS2;
+
+    /* Initial size: */
+    if (pSfFsi->sfi_sizel > 0)
+        pParams->Info.cbObject = pSfFsi->sfi_sizel;
+
+    /*
+     * Resolve path to a folder and folder relative path.
+     */
+    PVBOXSFFOLDER pFolder;
+    PSHFLSTRING   pStrFolderPath;
+    RT_NOREF(pCdFsi);
+    APIRET rc = vboxSfOs2ResolvePath(pszName, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
+    LogFlow(("FS32_OPENCREATE: vboxSfOs2ResolvePath: -> %u pFolder=%p\n", rc, pFolder));
+    if (rc == NO_ERROR)
+    {
+        /*
+         * Try open the file.
+         */
+        int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
+        LogFlow(("FS32_OPENCREATE: VbglR0SfCreate -> %Rrc Result=%d fMode=%#x\n", vrc, pParams->Result, pParams->Info.Attr.fMode));
+        if (RT_SUCCESS(vrc))
+        {
+            switch (pParams->Result)
+            {
+                case SHFL_FILE_EXISTS:
+                    if (pParams->Handle == SHFL_HANDLE_NIL)
+                    {
+                        rc = ERROR_FILE_EXISTS;
+                        break;
+                    }
+                    RT_FALL_THRU();
+                case SHFL_FILE_CREATED:
+                case SHFL_FILE_REPLACED:
+                    if (   pParams->Info.cbObject < _2G
+                        || (fOpenMode & OPEN_FLAGS_LARGEFILE))
+                    {
+                        pSfFsd->u32Magic    = VBOXSFSYFI_MAGIC;
+                        pSfFsd->pSelf       = pSfFsd;
+                        pSfFsd->hHostFile   = pParams->Handle;
+                        pSfFsd->pFolder     = pFolder;
+
+                        uint32_t cOpenFiles = ASMAtomicIncU32(&pFolder->cOpenFiles);
+                        Assert(cOpenFiles < _32K);
+                        pFolder = NULL; /* Reference now taken by pSfFsd->pFolder. */
+
+                        pSfFsi->sfi_sizel   = pParams->Info.cbObject;
+                        pSfFsi->sfi_type    = STYPE_FILE;
+                        pSfFsi->sfi_DOSattr = (uint8_t)((pParams->Info.Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT);
+                        int16_t cMinLocalTimeDelta = vboxSfOs2GetLocalTimeDelta();
+                        vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_cdate, &pSfFsi->sfi_ctime, pParams->Info.BirthTime,        cMinLocalTimeDelta);
+                        vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_adate, &pSfFsi->sfi_atime, pParams->Info.AccessTime,       cMinLocalTimeDelta);
+                        vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_mdate, &pSfFsi->sfi_mtime, pParams->Info.ModificationTime, cMinLocalTimeDelta);
+                        if (pParams->Result == SHFL_FILE_CREATED)
+                            pSfFsi->sfi_tstamp |= ST_PCREAT | ST_SCREAT | ST_PWRITE | ST_SWRITE | ST_PREAD | ST_SREAD;
+
+                        *puAction = pParams->Result == SHFL_FILE_CREATED ? FILE_CREATED
+                                  : pParams->Result == SHFL_FILE_EXISTS  ? FILE_EXISTED
+                                  :                                        FILE_TRUNCATED;
+
+                        Log(("FS32_OPENCREATE: hHandle=%#RX64 for '%s'\n", pSfFsd->hHostFile, pszName));
+                        rc = NO_ERROR;
+                    }
+                    else
+                    {
+                        LogRel(("FS32_OPENCREATE: cbObject=%#RX64 no OPEN_FLAGS_LARGEFILE (%s)\n", pParams->Info.cbObject, pszName));
+                        VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, pParams->Handle);
+                        rc = ERROR_ACCESS_DENIED;
+                    }
+                    break;
+
+                case SHFL_PATH_NOT_FOUND:
+                    rc = ERROR_PATH_NOT_FOUND;
+                    break;
+
+                default:
+                case SHFL_FILE_NOT_FOUND:
+                    rc = ERROR_FILE_NOT_FOUND;
+                    break;
+            }
+        }
+        else if (rc == VERR_ALREADY_EXISTS)
+            rc = ERROR_ACCESS_DENIED;
+        else
+            rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_PATH_NOT_FOUND);
+        vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
+    }
+    VbglR0PhysHeapFree(pParams);
+    LogFlow(("FS32_OPENCREATE: returns %u\n", rc));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_CLOSE(ULONG uType, ULONG fIoFlags, PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd)
+{
+    LogFlow(("FS32_CLOSE: uType=%#x fIoFlags=%#x pSfFsi=%p pSfFsd=%p:{%#x}\n", uType, fIoFlags, pSfFsi, pSfFsd, pSfFsd->u32Magic));
+
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+
+    /*
+     * We only care for when the system is done truly with the file
+     * and we can close it.
+     */
+    if (uType != FS_CL_FORSYS)
+        return NO_ERROR;
+
+    /** @todo flush file if fIoFlags says so? */
+    RT_NOREF(fIoFlags);
+
+    int vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile);
+    AssertRC(vrc);
+
+    pSfFsd->hHostFile = SHFL_HANDLE_NIL;
+    pSfFsd->pSelf     = NULL;
+    pSfFsd->u32Magic  = ~VBOXSFSYFI_MAGIC;
+    pSfFsd->pFolder   = NULL;
+
+    ASMAtomicDecU32(&pFolder->cOpenFiles);
+    vboxSfOs2ReleaseFolder(pFolder);
+
+    RT_NOREF(pSfFsi);
+    LogFlow(("FS32_CLOSE: returns NO_ERROR\n"));
+    return NO_ERROR;
+}
+
+
+DECLASM(APIRET)
+FS32_COMMIT(ULONG uType, ULONG fIoFlags, PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd)
+{
+    LogFlow(("FS32_COMMIT: uType=%#x fIoFlags=%#x pSfFsi=%p pSfFsd=%p:{%#x}\n", uType, fIoFlags, pSfFsi, pSfFsd, pSfFsd->u32Magic));
+
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    /*
+     * We only need to flush writable files.
+     */
+    if (   (pSfFsi->sfi_mode & SFMODE_OPEN_ACCESS) == SFMODE_OPEN_WRITEONLY
+        || (pSfFsi->sfi_mode & SFMODE_OPEN_ACCESS) == SFMODE_OPEN_READWRITE)
+    {
+        int vrc = VbglR0SfFlush(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile);
+        if (RT_FAILURE(vrc))
+        {
+            LogRel(("FS32_COMMIT: VbglR0SfFlush failed: %Rrc\n", vrc));
+            return ERROR_FLUSHBUF_FAILED;
+        }
+    }
+
+    NOREF(uType); NOREF(fIoFlags); NOREF(pSfFsi);
+    LogFlow(("FS32_COMMIT: returns NO_ERROR\n"));
+    return NO_ERROR;
 }
 
 
 extern "C" APIRET APIENTRY
-FS32_CHGFILEPTRL(PSFFSI psffsi, PVBOXSFFSD psffsd, LONGLONG off, ULONG uMethod, ULONG fIoFlags)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(off); NOREF(uMethod); NOREF(fIoFlags);
-    return ERROR_NOT_SUPPORTED;
+FS32_CHGFILEPTRL(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, LONGLONG off, ULONG uMethod, ULONG fIoFlags)
+{
+    LogFlow(("FS32_CHGFILEPTRL: pSfFsi=%p pSfFsd=%p off=%RI64 (%#RX64) uMethod=%u fIoFlags=%#x\n",
+             pSfFsi, pSfFsd, off, off, uMethod, fIoFlags));
+
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+
+    /*
+     * Calc absolute offset.
+     */
+    int64_t offNew;
+    switch (uMethod)
+    {
+        case CFP_RELBEGIN:
+            if (off >= 0)
+            {
+                offNew = off;
+                break;
+            }
+            Log(("FS32_CHGFILEPTRL: Negative seek (BEGIN): %RI64\n", off));
+            return ERROR_NEGATIVE_SEEK;
+
+        case CFP_RELCUR:
+            offNew = pSfFsi->sfi_positionl + off;
+            if (offNew >= 0)
+                break;
+            Log(("FS32_CHGFILEPTRL: Negative seek (RELCUR): %RU64 + %RI64\n", pSfFsi->sfi_positionl, off));
+            return ERROR_NEGATIVE_SEEK;
+
+        case CFP_RELEND:
+        {
+            /* Have to consult the host to get the current file size. */
+
+            PSHFLFSOBJINFO pObjInfo = (PSHFLFSOBJINFO)VbglR0PhysHeapAlloc(sizeof(*pObjInfo));
+            if (!pObjInfo)
+                return ERROR_NOT_ENOUGH_MEMORY;
+            RT_ZERO(*pObjInfo);
+            uint32_t cbObjInfo = sizeof(*pObjInfo);
+
+            int vrc = VbglR0SfFsInfo(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
+                                     SHFL_INFO_FILE | SHFL_INFO_GET, &cbObjInfo, (PSHFLDIRINFO)pObjInfo);
+            if (RT_SUCCESS(vrc))
+            {
+                if (pSfFsi->sfi_mode & SFMODE_LARGE_FILE)
+                    pSfFsi->sfi_sizel = pObjInfo->cbObject;
+                else
+                    pSfFsi->sfi_sizel = RT_MIN(pObjInfo->cbObject, _2G - 1);
+            }
+            else
+                LogRel(("FS32_CHGFILEPTRL/CFP_RELEND: VbglR0SfFsInfo failed: %Rrc\n", vrc));
+            VbglR0PhysHeapFree(pObjInfo);
+
+            offNew = pSfFsi->sfi_sizel + off;
+            if (offNew >= 0)
+                break;
+            Log(("FS32_CHGFILEPTRL: Negative seek (CFP_RELEND): %RI64 + %RI64\n", pSfFsi->sfi_sizel, off));
+            return ERROR_NEGATIVE_SEEK;
+        }
+
+
+        default:
+            LogRel(("FS32_CHGFILEPTRL: Unknown seek method: %#x\n", uMethod));
+            return ERROR_INVALID_FUNCTION;
+    }
+
+    /*
+     * Commit the seek.
+     */
+    pSfFsi->sfi_positionl = offNew;
+    LogFlow(("FS32_CHGFILEPTRL: returns; sfi_positionl=%RI64\n", offNew));
+    RT_NOREF_PV(fIoFlags);
+    return NO_ERROR;
 }
 
 
 /** Forwards the call to FS32_CHGFILEPTRL. */
+DECLASM(APIRET)
+FS32_CHGFILEPTR(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, LONG off, ULONG uMethod, ULONG fIoFlags)
+{
+    return FS32_CHGFILEPTRL(pSfFsi, pSfFsd, off, uMethod, fIoFlags);
+}
+
+
+/**
+ * Worker for FS32_PATHINFO that handles file stat setting.
+ *
+ * @returns OS/2 status code
+ * @param   pFolder         The folder.
+ * @param   pSfFsi          The file system independent file structure.  We'll
+ *                          update the timestamps and size here.
+ * @param   pSfFsd          Out file data.
+ * @param   uLevel          The information level.
+ * @param   pbData          The stat data to set.
+ * @param   cbData          The uLevel specific input size.
+ */
+static APIRET
+vboxSfOs2SetFileInfo(PVBOXSFFOLDER pFolder, PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, ULONG uLevel, PBYTE pbData, ULONG cbData)
+{
+    APIRET rc;
+
+    /*
+     * Data buffer both for caching user data and for issuing the
+     * change request to the host.
+     */
+    struct SetFileInfoBuf
+    {
+        union
+        {
+            FILESTATUS      Lvl1;
+            FILESTATUS3L    Lvl1L;
+        };
+        SHFLFSOBJINFO ObjInfo;
+
+    } *pBuf = (struct SetFileInfoBuf *)VbglR0PhysHeapAlloc(sizeof(*pBuf));
+    if (pBuf)
+    {
+        /* Copy in the data. */
+        rc = KernCopyIn(&pBuf->Lvl1, pbData, cbData);
+        if (rc == NO_ERROR)
+        {
+            /*
+             * Join paths with FS32_PATHINFO and FS32_FILEATTRIBUTE.
+             */
+            rc = vboxSfOs2SetInfoCommonWorker(pFolder, pSfFsd->hHostFile,
+                                              uLevel == FI_LVL_STANDARD ? pBuf->Lvl1.attrFile : pBuf->Lvl1L.attrFile,
+                                              &pBuf->Lvl1, &pBuf->ObjInfo);
+            if (rc == NO_ERROR)
+            {
+                /*
+                 * Update the timestamps in the independent file data with what
+                 * the host returned:
+                 */
+                pSfFsi->sfi_tstamp |= ST_PCREAT | ST_PWRITE | ST_PREAD;
+                pSfFsi->sfi_tstamp &= ~(ST_SCREAT | ST_SWRITE| ST_SREAD);
+                uint16_t cDelta = vboxSfOs2GetLocalTimeDelta();
+                vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_cdate, &pSfFsi->sfi_ctime, pBuf->ObjInfo.BirthTime,        cDelta);
+                vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_adate, &pSfFsi->sfi_atime, pBuf->ObjInfo.AccessTime,       cDelta);
+                vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_mdate, &pSfFsi->sfi_mtime, pBuf->ObjInfo.ModificationTime, cDelta);
+
+                /* And the size field as we're at it: */
+                pSfFsi->sfi_sizel = pBuf->ObjInfo.cbObject;
+            }
+            else
+                rc = ERROR_INVALID_PARAMETER;
+        }
+
+        VbglR0PhysHeapFree(pBuf);
+    }
+    else
+        rc = ERROR_NOT_ENOUGH_MEMORY;
+    return rc;
+}
+
+
+/**
+ * Worker for FS32_PATHINFO that handles file stat queries.
+ *
+ * @returns OS/2 status code
+ * @param   pFolder         The folder.
+ * @param   pSfFsi          The file system independent file structure.  We'll
+ *                          update the timestamps and size here.
+ * @param   pSfFsd          Out file data.
+ * @param   uLevel          The information level.
+ * @param   pbData          Where to return the data (user address).
+ * @param   cbData          The amount of data to produce.
+ */
+static APIRET
+vboxSfOs2QueryFileInfo(PVBOXSFFOLDER pFolder, PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, ULONG uLevel, PBYTE pbData, ULONG cbData)
+{
+    APIRET rc;
+    PSHFLFSOBJINFO pObjInfo = (PSHFLFSOBJINFO)VbglR0PhysHeapAlloc(sizeof(*pObjInfo));
+    if (pObjInfo)
+    {
+        RT_ZERO(*pObjInfo);
+        uint32_t cbObjInfo = sizeof(*pObjInfo);
+
+        int vrc = VbglR0SfFsInfo(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
+                                SHFL_INFO_FILE | SHFL_INFO_GET, &cbObjInfo, (PSHFLDIRINFO)pObjInfo);
+        if (RT_SUCCESS(vrc))
+        {
+            rc = vboxSfOs2FileStatusFromObjInfo(pbData, cbData, uLevel, pObjInfo);
+            if (rc == NO_ERROR)
+            {
+                /* Update the timestamps in the independent file data: */
+                int16_t cMinLocalTimeDelta = vboxSfOs2GetLocalTimeDelta();
+                vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_cdate, &pSfFsi->sfi_ctime, pObjInfo->BirthTime,        cMinLocalTimeDelta);
+                vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_adate, &pSfFsi->sfi_atime, pObjInfo->AccessTime,       cMinLocalTimeDelta);
+                vboxSfOs2DateTimeFromTimeSpec(&pSfFsi->sfi_mdate, &pSfFsi->sfi_mtime, pObjInfo->ModificationTime, cMinLocalTimeDelta);
+
+                /* And the size field as we're at it: */
+                pSfFsi->sfi_sizel = pObjInfo->cbObject;
+            }
+        }
+        else
+        {
+            Log(("vboxSfOs2QueryFileInfo: VbglR0SfFsInfo failed: %Rrc\n", vrc));
+            rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_GEN_FAILURE);
+        }
+    }
+    else
+        rc = ERROR_NOT_ENOUGH_MEMORY;
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_FILEINFO(ULONG fFlags, PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, ULONG uLevel,
+              PBYTE pbData, ULONG cbData, ULONG fIoFlags)
+{
+    LogFlow(("FS32_FILEINFO: fFlags=%#x pSfFsi=%p pSfFsd=%p uLevel=%p pbData=%p cbData=%#x fIoFlags=%#x\n",
+             fFlags, pSfFsi, pSfFsd, uLevel, pbData, cbData, fIoFlags));
+
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+
+    /*
+     * Check the level.
+     * Note! See notes in FS32_PATHINFO.
+     */
+    ULONG cbMinData;
+    switch (uLevel)
+    {
+        case FI_LVL_STANDARD:
+            cbMinData = sizeof(FILESTATUS);
+            AssertCompileSize(FILESTATUS,  0x16);
+            break;
+        case FI_LVL_STANDARD_64:
+            cbMinData = sizeof(FILESTATUS3L);
+            AssertCompileSize(FILESTATUS3L, 0x20); /* cbFile and cbFileAlloc are misaligned. */
+            break;
+        case FI_LVL_STANDARD_EASIZE:
+            cbMinData = sizeof(FILESTATUS2);
+            AssertCompileSize(FILESTATUS2, 0x1a);
+            break;
+        case FI_LVL_STANDARD_EASIZE_64:
+            cbMinData = sizeof(FILESTATUS4L);
+            AssertCompileSize(FILESTATUS4L, 0x24); /* cbFile and cbFileAlloc are misaligned. */
+            break;
+        case FI_LVL_EAS_FROM_LIST:
+        case FI_LVL_EAS_FULL:
+        case FI_LVL_EAS_FULL_5:
+        case FI_LVL_EAS_FULL_8:
+            cbMinData = sizeof(EAOP);
+            break;
+        default:
+            LogRel(("FS32_PATHINFO: Unsupported info level %u!\n", uLevel));
+            return ERROR_INVALID_LEVEL;
+    }
+    if (cbData < cbMinData || pbData == NULL)
+    {
+        Log(("FS32_FILEINFO: ERROR_BUFFER_OVERFLOW (cbMinData=%#x, cbData=%#x)\n", cbMinData, cbData));
+        return ERROR_BUFFER_OVERFLOW;
+    }
+
+    /*
+     * Query information.
+     */
+    APIRET rc;
+    if (fFlags == FI_RETRIEVE)
+    {
+        switch (uLevel)
+        {
+            case FI_LVL_STANDARD:
+            case FI_LVL_STANDARD_EASIZE:
+            case FI_LVL_STANDARD_64:
+            case FI_LVL_STANDARD_EASIZE_64:
+                rc = vboxSfOs2QueryFileInfo(pFolder, pSfFsi, pSfFsd, uLevel, pbData, cbMinData);
+                break;
+
+            /*
+             * We don't do EAs and we "just" need to return no-EAs.
+             * However, that's not as easy as you might think.
+             */
+            case FI_LVL_EAS_FROM_LIST:
+            case FI_LVL_EAS_FULL:
+            case FI_LVL_EAS_FULL_5:
+            case FI_LVL_EAS_FULL_8:
+                rc = vboxSfOs2MakeEmptyEaList((PEAOP)pbData, uLevel);
+                break;
+
+            default:
+                AssertFailed();
+                rc = ERROR_GEN_FAILURE;
+                break;
+        }
+    }
+    /*
+     * Update information.
+     */
+    else if (fFlags == FI_SET)
+    {
+        switch (uLevel)
+        {
+            case FI_LVL_STANDARD:
+            case FI_LVL_STANDARD_64:
+                rc = vboxSfOs2SetFileInfo(pFolder, pSfFsi, pSfFsd, uLevel, pbData, cbMinData);
+                break;
+
+            case FI_LVL_STANDARD_EASIZE:
+                rc = ERROR_EAS_NOT_SUPPORTED;
+                break;
+
+            case FI_LVL_STANDARD_EASIZE_64:
+            case FI_LVL_EAS_FROM_LIST:
+            case FI_LVL_EAS_FULL:
+            case FI_LVL_EAS_FULL_5:
+            case FI_LVL_EAS_FULL_8:
+                rc = ERROR_INVALID_LEVEL;
+                break;
+
+            default:
+                AssertFailed();
+                rc = ERROR_GEN_FAILURE;
+                break;
+        }
+    }
+    else
+    {
+        LogRel(("FS32_FILEINFO: Unknown flags value: %#x\n", fFlags));
+        rc = ERROR_INVALID_PARAMETER;
+    }
+    RT_NOREF_PV(fIoFlags);
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_NEWSIZEL(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, LONGLONG cbFile, ULONG fIoFlags)
+{
+    LogFlow(("FS32_NEWSIZEL: pSfFsi=%p pSfFsd=%p cbFile=%RI64 (%#RX64) fIoFlags=%#x\n", pSfFsi, pSfFsd, cbFile, cbFile, fIoFlags));
+
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    if (cbFile < 0)
+    {
+        LogRel(("FS32_NEWSIZEL: Negative size: %RI64\n", cbFile));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    /*
+     * This should only be possible on a file that is writable.
+     */
+    APIRET rc;
+    if (   (pSfFsi->sfi_mode & SFMODE_OPEN_ACCESS) == SFMODE_OPEN_WRITEONLY
+        || (pSfFsi->sfi_mode & SFMODE_OPEN_ACCESS) == SFMODE_OPEN_READWRITE)
+    {
+        /*
+         * Call the host.  We need a full object info structure here to pass
+         * a 64-bit unsigned integer value.  Sigh.
+         */
+        /** @todo Shared folders: New SET_FILE_SIZE API. */
+        PSHFLFSOBJINFO pObjInfo = (PSHFLFSOBJINFO)VbglR0PhysHeapAlloc(sizeof(*pObjInfo));
+        if (pObjInfo)
+        {
+            RT_ZERO(*pObjInfo);
+            pObjInfo->cbObject = cbFile;
+            uint32_t cbObjInfo = sizeof(*pObjInfo);
+            int vrc = VbglR0SfFsInfo(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
+                                     SHFL_INFO_SIZE | SHFL_INFO_SET, &cbObjInfo, (PSHFLDIRINFO)pObjInfo);
+            if (RT_SUCCESS(vrc))
+            {
+                pSfFsi->sfi_sizel = cbFile;
+                rc = NO_ERROR;
+            }
+            else
+            {
+                LogRel(("FS32_NEWSIZEL: VbglR0SfFsInfo failed: %Rrc\n", vrc));
+                if (vrc == VERR_DISK_FULL)
+                    rc = ERROR_DISK_FULL;
+                else
+                    rc = ERROR_GEN_FAILURE;
+            }
+            VbglR0PhysHeapFree(pObjInfo);
+        }
+        else
+            rc = ERROR_NOT_ENOUGH_MEMORY;
+    }
+    else
+        rc = ERROR_ACCESS_DENIED;
+    LogFlow(("FS32_NEWSIZEL: returns %u\n", rc));
+    return rc;
+}
+
+
 extern "C" APIRET APIENTRY
-FS32_CHGFILEPTR(PSFFSI psffsi, PVBOXSFFSD psffsd, LONG off, ULONG uMethod, ULONG fIoFlags)
-{
-    return FS32_CHGFILEPTRL(psffsi, psffsd, off, uMethod, fIoFlags);
-}
-
-DECLASM(int)
-FS32_FILEINFO(ULONG fFlag, PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG uLevel,
-              PBYTE pbData, ULONG cbData, ULONG fIoFlags)
-{
-    NOREF(fFlag); NOREF(psffsi); NOREF(psffsd); NOREF(uLevel); NOREF(pbData); NOREF(cbData); NOREF(fIoFlags);
-    return ERROR_NOT_SUPPORTED;
-}
-
-DECLASM(int)
-FS32_NEWSIZEL(PSFFSI psffsi, PVBOXSFFSD psffsd, LONGLONG cbFile, ULONG fIoFlags)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(cbFile); NOREF(fIoFlags);
-    return ERROR_NOT_SUPPORTED;
+FS32_READ(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, PVOID pvData, PULONG pcb, ULONG fIoFlags)
+{
+    LogFlow(("FS32_READ: pSfFsi=%p pSfFsd=%p pvData=%p pcb=%p:{%#x} fIoFlags=%#x\n", pSfFsi, pSfFsd, pvData, pcb, *pcb, fIoFlags));
+
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    /*
+     * If the read request is small enough, go thru a temporary buffer to
+     * avoid locking/unlocking user memory.
+     */
+    uint64_t offRead  = pSfFsi->sfi_positionl;
+    uint32_t cbRead   = *pcb;
+    uint32_t cbActual = cbRead;
+    if (cbRead <= _8K - ALLOC_HDR_SIZE)
+    {
+        void *pvBuf = VbglR0PhysHeapAlloc(cbRead);
+        if (pvBuf != NULL)
+        {
+            APIRET rc;
+            int vrc = VbglR0SfRead(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
+                                   offRead, &cbActual, (uint8_t *)pvBuf, true /*fLocked*/);
+            if (RT_SUCCESS(vrc))
+            {
+                AssertStmt(cbActual <= cbRead, cbActual = cbRead);
+                rc = KernCopyOut(pvData, pvBuf, cbActual);
+                if (rc == NO_ERROR)
+                {
+                    *pcb = cbActual;
+                    pSfFsi->sfi_positionl = offRead + cbActual;
+                    if (pSfFsi->sfi_sizel < offRead + cbActual)
+                        pSfFsi->sfi_sizel = offRead + cbActual;
+                    pSfFsi->sfi_tstamp   |= ST_SREAD | ST_PREAD;
+                    LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [copy]\n", cbActual, pSfFsi->sfi_positionl));
+                }
+            }
+            else
+            {
+                Log(("FS32_READ: VbglR0SfRead(off=%#x,cb=%#x) -> %Rrc [copy]\n", offRead, cbRead, vrc));
+                rc = ERROR_BAD_NET_RESP;
+            }
+            VbglR0PhysHeapFree(pvBuf);
+            return rc;
+        }
+    }
+
+    /*
+     * Do the read directly on the buffer, Vbgl will do the locking for us.
+     */
+    int vrc = VbglR0SfRead(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
+                           offRead, &cbActual, (uint8_t *)pvData, false /*fLocked*/);
+    if (RT_SUCCESS(vrc))
+    {
+        AssertStmt(cbActual <= cbRead, cbActual = cbRead);
+        *pcb = cbActual;
+        pSfFsi->sfi_positionl = offRead + cbActual;
+        if (pSfFsi->sfi_sizel < offRead + cbActual)
+            pSfFsi->sfi_sizel = offRead + cbActual;
+        pSfFsi->sfi_tstamp   |= ST_SREAD | ST_PREAD;
+        LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [direct]\n", cbActual, pSfFsi->sfi_positionl));
+        return NO_ERROR;
+    }
+    Log(("FS32_READ: VbglR0SfRead(off=%#x,cb=%#x) -> %Rrc [direct]\n", offRead, cbRead, vrc));
+    RT_NOREF_PV(fIoFlags);
+    return ERROR_BAD_NET_RESP;
 }
 
 
 extern "C" APIRET APIENTRY
-FS32_READ(PSFFSI psffsi, PVBOXSFFSD psffsd, PVOID pvData, PULONG pcb, ULONG fIoFlags)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(pvData); NOREF(pcb); NOREF(fIoFlags);
-    return ERROR_NOT_SUPPORTED;
+FS32_WRITE(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, void const *pvData, PULONG pcb, ULONG fIoFlags)
+{
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    /*
+     * If the write request is small enough, go thru a temporary buffer to
+     * avoid locking/unlocking user memory.
+     */
+    uint64_t offWrite = pSfFsi->sfi_positionl;
+    uint32_t cbWrite  = *pcb;
+    uint32_t cbActual = cbWrite;
+    if (cbWrite <= _8K - ALLOC_HDR_SIZE)
+    {
+        void *pvBuf = VbglR0PhysHeapAlloc(cbWrite);
+        if (pvBuf != NULL)
+        {
+            APIRET rc = KernCopyIn(pvBuf, pvData, cbWrite);
+            if (rc == NO_ERROR)
+            {
+                int vrc = VbglR0SfWrite(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
+                                        offWrite, &cbActual, (uint8_t *)pvBuf, true /*fLocked*/);
+                if (RT_SUCCESS(vrc))
+                {
+                    AssertStmt(cbActual <= cbWrite, cbActual = cbWrite);
+                    *pcb = cbActual;
+                    pSfFsi->sfi_positionl = offWrite + cbActual;
+                    if (pSfFsi->sfi_sizel < offWrite + cbActual)
+                        pSfFsi->sfi_sizel = offWrite + cbActual;
+                    pSfFsi->sfi_tstamp   |= ST_SWRITE | ST_PWRITE;
+                    LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [copy]\n", cbActual, pSfFsi->sfi_positionl));
+                }
+                else
+                {
+                    Log(("FS32_READ: VbglR0SfWrite(off=%#x,cb=%#x) -> %Rrc [copy]\n", offWrite, cbWrite, vrc));
+                    rc = ERROR_BAD_NET_RESP;
+                }
+            }
+            VbglR0PhysHeapFree(pvBuf);
+            return rc;
+        }
+    }
+
+    /*
+     * Do the write directly on the buffer, Vbgl will do the locking for us.
+     */
+    int vrc = VbglR0SfWrite(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
+                            offWrite, &cbActual, (uint8_t *)pvData, false /*fLocked*/);
+    if (RT_SUCCESS(vrc))
+    {
+        AssertStmt(cbActual <= cbWrite, cbActual = cbWrite);
+        *pcb = cbActual;
+        pSfFsi->sfi_positionl = offWrite + cbActual;
+        if (pSfFsi->sfi_sizel < offWrite + cbActual)
+            pSfFsi->sfi_sizel = offWrite + cbActual;
+        pSfFsi->sfi_tstamp   |= ST_SWRITE | ST_PWRITE;
+        LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [direct]\n", cbActual, pSfFsi->sfi_positionl));
+        return NO_ERROR;
+    }
+    Log(("FS32_READ: VbglR0SfWrite(off=%#x,cb=%#x) -> %Rrc [direct]\n", offWrite, cbWrite, vrc));
+    RT_NOREF_PV(fIoFlags);
+    return ERROR_BAD_NET_RESP;
 }
 
 
 extern "C" APIRET APIENTRY
-FS32_WRITE(PSFFSI psffsi, PVBOXSFFSD psffsd, PVOID pvData, PULONG pcb, ULONG fIoFlags)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(pvData); NOREF(pcb); NOREF(fIoFlags);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-extern "C" APIRET APIENTRY
-FS32_READFILEATCACHE(PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG fIoFlags, LONGLONG off, ULONG pcb, KernCacheList_t **ppCacheList)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(fIoFlags); NOREF(off); NOREF(pcb); NOREF(ppCacheList);
+FS32_READFILEATCACHE(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, ULONG fIoFlags, LONGLONG off, ULONG pcb, KernCacheList_t **ppCacheList)
+{
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    /* I think this is used for sendfile(). */
+
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(fIoFlags); NOREF(off); NOREF(pcb); NOREF(ppCacheList);
     return ERROR_NOT_SUPPORTED;
 }
@@ -133,44 +891,99 @@
 /* oddments */
 
-DECLASM(int)
-FS32_CANCELLOCKREQUESTL(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelockl *pLockRange)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(pLockRange);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_CANCELLOCKREQUEST(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelock *pLockRange)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(pLockRange);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_FILELOCKSL(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelockl *pUnLockRange,
+DECLASM(APIRET)
+FS32_CANCELLOCKREQUESTL(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, struct filelockl *pLockRange)
+{
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(pLockRange);
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(APIRET)
+FS32_CANCELLOCKREQUEST(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, struct filelock *pLockRange)
+{
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(pLockRange);
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(APIRET)
+FS32_FILELOCKSL(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, struct filelockl *pUnLockRange,
                 struct filelockl *pLockRange, ULONG cMsTimeout, ULONG fFlags)
 {
-    NOREF(psffsi); NOREF(psffsd); NOREF(pUnLockRange); NOREF(pLockRange); NOREF(cMsTimeout); NOREF(fFlags);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_FILELOCKS(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelock *pUnLockRange,
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(pUnLockRange); NOREF(pLockRange); NOREF(cMsTimeout); NOREF(fFlags);
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(APIRET)
+FS32_FILELOCKS(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, struct filelock *pUnLockRange,
                struct filelock *pLockRange, ULONG cMsTimeout, ULONG fFlags)
 {
-    NOREF(psffsi); NOREF(psffsd); NOREF(pUnLockRange); NOREF(pLockRange); NOREF(cMsTimeout); NOREF(fFlags);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_IOCTL(PSFFSI psffsi, PVBOXSFFSD psffsd, USHORT uCategory, USHORT uFunction,
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(pUnLockRange); NOREF(pLockRange); NOREF(cMsTimeout); NOREF(fFlags);
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(APIRET)
+FS32_IOCTL(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, USHORT uCategory, USHORT uFunction,
            PVOID pvParm, USHORT cbParm, PUSHORT pcbParmIO,
            PVOID pvData, USHORT cbData, PUSHORT pcbDataIO)
 {
-    NOREF(psffsi); NOREF(psffsd); NOREF(uCategory); NOREF(uFunction); NOREF(pvParm); NOREF(cbParm); NOREF(pcbParmIO);
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(uCategory); NOREF(uFunction); NOREF(pvParm); NOREF(cbParm); NOREF(pcbParmIO);
     NOREF(pvData); NOREF(cbData); NOREF(pcbDataIO);
     return ERROR_NOT_SUPPORTED;
@@ -178,27 +991,38 @@
 
 
-DECLASM(int)
-FS32_FILEIO(PSFFSI psffsi, PVBOXSFFSD psffsd, PBYTE pbCmdList, USHORT cbCmdList,
+DECLASM(APIRET)
+FS32_FILEIO(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, PBYTE pbCmdList, USHORT cbCmdList,
             PUSHORT poffError, USHORT fIoFlag)
 {
-    NOREF(psffsi); NOREF(psffsd); NOREF(pbCmdList); NOREF(cbCmdList); NOREF(poffError); NOREF(fIoFlag);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_NMPIPE(PSFFSI psffsi, PVBOXSFFSD psffsd, USHORT uOpType, union npoper *pOpRec,
+    /*
+     * Validate input.
+     */
+    AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
+    AssertReturn(pSfFsd->pSelf    == pSfFsd, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pSfFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenFiles > 0);
+    RT_NOREF(pFolder);
+
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(pbCmdList); NOREF(cbCmdList); NOREF(poffError); NOREF(fIoFlag);
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(APIRET)
+FS32_NMPIPE(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, USHORT uOpType, union npoper *pOpRec,
             PBYTE pbData, PCSZ pszName)
 {
-    NOREF(psffsi); NOREF(psffsd); NOREF(uOpType); NOREF(pOpRec); NOREF(pbData); NOREF(pszName);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_OPENPAGEFILE(PULONG pfFlags, PULONG pcMaxReq, PCSZ pszName, PSFFSI psffsi, PVBOXSFFSD psffsd,
-                  USHORT uOpenMode, USHORT fOpenFlags, USHORT fAttr, ULONG uReserved)
-{
-    NOREF(pfFlags); NOREF(pcMaxReq); NOREF(pszName); NOREF(psffsi); NOREF(psffsd); NOREF(uOpenMode); NOREF(fOpenFlags);
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(uOpType); NOREF(pOpRec); NOREF(pbData); NOREF(pszName);
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(APIRET)
+FS32_OPENPAGEFILE(PULONG pfFlags, PULONG pcMaxReq, PCSZ pszName, PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd,
+                  USHORT fOpenMode, USHORT fOpenFlags, USHORT fAttr, ULONG uReserved)
+{
+    NOREF(pfFlags); NOREF(pcMaxReq); NOREF(pszName); NOREF(pSfFsi); NOREF(pSfFsd); NOREF(fOpenMode); NOREF(fOpenFlags);
     NOREF(fAttr); NOREF(uReserved);
     return ERROR_NOT_SUPPORTED;
@@ -206,25 +1030,25 @@
 
 
-DECLASM(int)
-FS32_SETSWAP(PSFFSI psffsi, PVBOXSFFSD psffsd)
-{
-    NOREF(psffsi); NOREF(psffsd);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_ALLOCATEPAGESPACE(PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG cb, USHORT cbWantContig)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(cb); NOREF(cbWantContig);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_DOPAGEIO(PSFFSI psffsi, PVBOXSFFSD psffsd, struct PageCmdHeader *pList)
-{
-    NOREF(psffsi); NOREF(psffsd); NOREF(pList);
-    return ERROR_NOT_SUPPORTED;
-}
-
+DECLASM(APIRET)
+FS32_SETSWAP(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd)
+{
+    NOREF(pSfFsi); NOREF(pSfFsd);
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(APIRET)
+FS32_ALLOCATEPAGESPACE(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, ULONG cb, USHORT cbWantContig)
+{
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(cb); NOREF(cbWantContig);
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(APIRET)
+FS32_DOPAGEIO(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, struct PageCmdHeader *pList)
+{
+    NOREF(pSfFsi); NOREF(pSfFsd); NOREF(pList);
+    return ERROR_NOT_SUPPORTED;
+}
+
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFind.cpp
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFind.cpp	(revision 75336)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFind.cpp	(revision 75337)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ * Copyright (c) 2007-2018 knut st. osmundsen <bird-src-spam@anduin.net>
  *
  * Permission is hereby granted, free of charge, to any person
@@ -37,70 +37,851 @@
 
 #include <VBox/log.h>
+#include <iprt/asm.h>
 #include <iprt/assert.h>
-
-
-DECLASM(int)
-FS32_FINDFIRST(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, USHORT fAttr,
-               PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
-               USHORT uLevel, USHORT fFlags)
-{
-    NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszName); NOREF(iCurDirEnd); NOREF(fAttr); NOREF(pfsfsi); NOREF(pfsfsd); NOREF(pbData);
-    NOREF(cbData); NOREF(pcMatch); NOREF(uLevel); NOREF(fFlags);
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/err.h>
+
+
+
+/**
+ * Checks if the given name is 8-dot-3 compatible.
+ *
+ * @returns true if compatible, false if not.
+ * @param   pszName             The name to inspect (UTF-8).
+ */
+static bool vboxSfOs2IsUtf8Name8dot3(const char *pszName, size_t cchName, PRTUTF16 pwszTmp, size_t cwcTmp)
+{
+    /* Reject names that must be too long when using maximum UTF-8 encoding. */
+    if (cchName > (8 + 1 + 3) * 4)
+        return false;
+
+    /* First char cannot be a dot. */
+    if (*pszName == '.' || !*pszName)
+        return false;
+
+    /*
+     * To basic checks on code point level before doing full conversion.
+     */
+    const char *pszCursor = pszName;
+    for (uint32_t cuc = 0; ; cuc++)
+    {
+        RTUNICP uCp;
+        RTStrGetCpEx(&pszCursor, &uCp);
+        if (uCp == '.')
+        {
+            for (uint32_t cuc = 0; ; cuc++)
+            {
+                RTUNICP uCp;
+                RTStrGetCpEx(&pszCursor, &uCp);
+                if (!uCp)
+                    break;
+                if (uCp == '.')
+                    return false;
+                if (cuc >= 3)
+                    return false;
+            }
+            break;
+        }
+        if (!uCp)
+            break;
+        if (cuc >= 8)
+            return false;
+    }
+
+    /*
+     * Convert to UTF-16 and then to native codepage.
+     */
+    size_t cwcActual = cwcTmp;
+    int rc = RTStrToUtf16Ex(pszName, cchName, &pwszTmp, cwcTmp, &cwcActual);
+    if (RT_SUCCESS(rc))
+    {
+        char *pszTmp = (char *)&pwszTmp[cwcActual + 1];
+        rc = KernStrFromUcs(NULL, pszTmp, pwszTmp, (cwcTmp - cwcActual - 1) * sizeof(RTUTF16), cwcActual);
+        if (rc != NO_ERROR)
+        {
+            LogRel(("vboxSfOs2IsUtf8Name8dot3: KernStrFromUcs failed: %d\n", rc));
+            return false;
+        }
+
+        /*
+         * Redo the check.
+         * Note! This could be bogus if a DBCS leadin sequence collides with '.'.
+         */
+        for (uint32_t cch = 0; ; cch++)
+        {
+            char ch = *pszTmp++;
+            if (ch == '.')
+                break;
+            if (ch == '\0')
+                return true;
+            if (cch >= 8)
+                return false;
+        }
+        for (uint32_t cch = 0; ; cch++)
+        {
+            char ch = *pszTmp++;
+            if (ch == '\0')
+                return true;
+            if (ch != '.')
+                return false;
+            if (cch >= 3)
+                return false;
+        }
+    }
+    else
+        LogRel(("vboxSfOs2IsUtf8Name8dot3: RTStrToUtf16Ex failed: %Rrc\n", rc));
+    return false;
+}
+
+
+/**
+ * @returns Updated pbDst on success, NULL on failure.
+ */
+static uint8_t *vboxSfOs2CopyUtf8Name(uint8_t *pbDst, PRTUTF16 pwszTmp, size_t cwcTmp, const char *pszSrc, size_t cchSrc)
+{
+    /* Convert UTF-8 to UTF-16: */
+    int rc = RTStrToUtf16Ex(pszSrc, cchSrc, &pwszTmp, cwcTmp, &cwcTmp);
+    if (RT_SUCCESS(rc))
+    {
+        char *pszDst = (char *)(pbDst + 1);
+        rc = KernStrFromUcs(NULL, pszDst, pwszTmp, CCHMAXPATHCOMP, cwcTmp);
+        if (rc == NO_ERROR)
+        {
+            size_t cchDst = strlen(pszDst);
+            *pbDst++ = (uint8_t)cchDst;
+            pbDst   += cchDst;
+            *pbDst++ = '\0';
+            return pbDst;
+        }
+        LogRel(("vboxSfOs2CopyUtf8Name: KernStrFromUcs failed: %d\n", rc));
+    }
+    else
+        LogRel(("vboxSfOs2CopyUtf8Name: RTStrToUtf16Ex failed: %Rrc\n", rc));
+    return NULL;
+}
+
+
+/**
+ * @returns Updated pbDst on success, NULL on failure.
+ */
+static uint8_t *vboxSfOs2CopyUtf8NameAndUpperCase(uint8_t *pbDst, PRTUTF16 pwszTmp, size_t cwcTmp, const char *pszSrc, size_t cchSrc)
+{
+    /* Convert UTF-8 to UTF-16: */
+    int rc = RTStrToUtf16Ex(pszSrc, cchSrc, &pwszTmp, cwcTmp, &cwcTmp);
+    if (RT_SUCCESS(rc))
+    {
+        char *pszDst = (char *)(pbDst + 1);
+        rc = KernStrFromUcs(NULL, pszDst, RTUtf16ToUpper(pwszTmp), CCHMAXPATHCOMP, cwcTmp);
+        if (rc == NO_ERROR)
+        {
+            size_t cchDst = strlen(pszDst);
+            *pbDst++ = (uint8_t)cchDst;
+            pbDst   += cchDst;
+            *pbDst++ = '\0';
+            return pbDst;
+        }
+        LogRel(("vboxSfOs2CopyUtf8NameAndUpperCase: KernStrFromUcs failed: %d\n", rc));
+    }
+    else
+        LogRel(("vboxSfOs2CopyUtf8NameAndUpperCase: RTStrToUtf16Ex failed: %Rrc\n", rc));
+    return NULL;
+}
+
+
+/**
+ * @returns Updated pbDst on success, NULL on failure.
+ */
+static uint8_t *vboxSfOs2CopyUtf16NameAndUpperCase(uint8_t *pbDst, PRTUTF16 pwszSrc, size_t cwcSrc)
+{
+    char *pszDst = (char *)(pbDst + 1);
+    APIRET rc = KernStrFromUcs(NULL, pszDst, RTUtf16ToUpper(pwszSrc), CCHMAXPATHCOMP, cwcSrc);
+    if (rc == NO_ERROR)
+    {
+        size_t cchDst = strlen(pszDst);
+        *pbDst++ = (uint8_t)cchDst;
+        pbDst   += cchDst;
+        *pbDst++ = '\0';
+        return pbDst;
+    }
+    LogRel(("vboxSfOs2CopyUtf16NameAndUpperCase: KernStrFromUcs failed: %#x\n", rc));
+    return NULL;
+}
+
+
+
+/**
+ * Worker for FS32_FINDFIRST, FS32_FINDNEXT and FS32_FINDFROMNAME.
+ *
+ * @returns OS/2 status code.
+ * @param   pFolder     The folder we're working on.
+ * @param   pFsFsd      The search handle data.
+ * @param   pDataBuf    The search data buffer (some handle data there too).
+ * @param   uLevel      The info level to return.
+ * @param   fFlags      Position flag.
+ * @param   pbData      The output buffer.
+ * @param   cbData      The size of the output buffer.
+ * @param   cMaxMatches The maximum number of matches to return.
+ * @param   pcMatches   Where to set the number of returned matches.
+ */
+static APIRET vboxSfOs2ReadDirEntries(PVBOXSFFOLDER pFolder, PVBOXSFFS pFsFsd, PVBOXSFFSBUF pDataBuf, ULONG uLevel, ULONG fFlags,
+                                      PBYTE pbData, ULONG cbData, USHORT cMaxMatches, PUSHORT pcMatches)
+{
+    APIRET rc = NO_ERROR;
+
+    /*
+     * If we're doing EAs, the buffer starts with an EAOP structure.
+     */
+    EAOP    EaOp;
+    PEAOP   pEaOpUser;
+    switch (uLevel)
+    {
+        case FI_LVL_EAS_FROM_LIST:
+        case FI_LVL_EAS_FROM_LIST_64:
+        case FI_LVL_EAS_FULL:
+        case FI_LVL_EAS_FULL_5:
+        case FI_LVL_EAS_FULL_8:
+            if (cbData >= sizeof(EaOp))
+            {
+                rc = KernCopyIn(&EaOp, pbData, sizeof(EaOp));
+                if (rc == NO_ERROR)
+                {
+                    EaOp.fpGEAList = (PGEALIST)KernSelToFlat((uintptr_t)EaOp.fpGEAList);
+                    EaOp.fpFEAList = NULL;
+
+                    pEaOpUser = (PEAOP)pbData;
+                    pbData += sizeof(*pEaOpUser);
+                    cbData -= sizeof(*pEaOpUser);
+                    break;
+                }
+            }
+            else
+                rc = ERROR_BUFFER_OVERFLOW;
+            Log(("vboxSfOs2ReadDirEntries: Failed to read EAOP: %u\n", rc));
+            return rc;
+    }
+
+    /*
+     * Do the reading.
+     */
+    USHORT cMatches;
+    for (cMatches = 0; cMatches < cMaxMatches;)
+    {
+        /*
+         * Do we need to fetch more directory entries?
+         */
+        PSHFLDIRINFO pEntry = pDataBuf->pEntry;
+        if (   pDataBuf->cEntriesLeft == 0
+            || pEntry == NULL /* paranoia */)
+        {
+            pDataBuf->pEntry  = pEntry = (PSHFLDIRINFO)(pDataBuf + 1);
+            pDataBuf->cbValid = pDataBuf->cbBuf - sizeof(*pDataBuf);
+            int vrc = VbglR0SfDirInfo(&g_SfClient, &pFolder->hHostFolder, pFsFsd->hHostDir, pDataBuf->pFilter,
+                                      cMaxMatches == 1 ? SHFL_LIST_RETURN_ONE : 0, 0 /*index*/, &pDataBuf->cbValid,
+                                      pEntry, &pDataBuf->cEntriesLeft);
+            if (RT_SUCCESS(vrc))
+            {
+                AssertReturn(pDataBuf->cbValid >= RT_UOFFSETOF(SHFLDIRINFO, name.String), ERROR_SYS_INTERNAL);
+                AssertReturn(pDataBuf->cbValid >= RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size, ERROR_SYS_INTERNAL);
+                Log4(("vboxSfOs2ReadDirEntries: VbglR0SfDirInfo returned %#x matches in %#x bytes\n", pDataBuf->cEntriesLeft, pDataBuf->cbValid));
+            }
+            else
+            {
+                if (vrc == VERR_NO_MORE_FILES)
+                    Log(("vboxSfOs2ReadDirEntries: VbglR0SfDirInfo failed %Rrc (%d,%d)\n", vrc, pDataBuf->cEntriesLeft, pDataBuf->cbValid));
+                else
+                    Log4(("vboxSfOs2ReadDirEntries: VbglR0SfDirInfo returned VERR_NO_MORE_FILES (%d,%d)\n", pDataBuf->cEntriesLeft, pDataBuf->cbValid));
+                pDataBuf->pEntry       = NULL;
+                pDataBuf->cEntriesLeft = 0;
+                if (cMatches == 0)
+                {
+                    if (vrc == VERR_NO_MORE_FILES)
+                        rc = ERROR_NO_MORE_FILES;
+                    else
+                        rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_GEN_FAILURE);
+                }
+                break;
+            }
+        }
+
+        /*
+         * Do matching and stuff the return buffer.
+         */
+        if (   !((pEntry->Info.Attr.fMode >> RTFS_DOS_SHIFT) & pDataBuf->fExcludedAttribs)
+            && ((pEntry->Info.Attr.fMode >> RTFS_DOS_SHIFT) & pDataBuf->fMustHaveAttribs) == pDataBuf->fMustHaveAttribs
+            && (   pDataBuf->fLongFilenames
+                || pEntry->cucShortName
+                || vboxSfOs2IsUtf8Name8dot3((char *)pEntry->name.String.utf8, pEntry->name.u16Length,
+                                            pDataBuf->wszTmp, sizeof(pDataBuf->wszTmp))))
+        {
+            /*
+             * We stages all but FEAs (level 3, 4, 13 and 14).
+             */
+            PBYTE const pbUserBufStart = pbData; /* In case we need to skip a bad name. */
+            uint8_t    *pbToCopy       = pDataBuf->abStaging;
+            uint8_t    *pbDst          = pbToCopy;
+
+            /* Position (originally used for FS32_FINDFROMNAME 'position', but since reused
+               for FILEFINDBUF3::oNextEntryOffset and FILEFINDBUF4::oNextEntryOffset): */
+            if (fFlags & FF_GETPOS)
+            {
+                *(uint32_t *)pbDst = pFsFsd->offLastFile + 1;
+                pbDst += sizeof(uint32_t);
+            }
+
+            /* Dates: Creation, Access, Write */
+            vboxSfOs2DateTimeFromTimeSpec((FDATE *)pbDst, (FTIME *)(pbDst + 2), pEntry->Info.BirthTime, pDataBuf->cMinLocalTimeDelta);
+            pbDst += sizeof(FDATE) + sizeof(FTIME);
+            vboxSfOs2DateTimeFromTimeSpec((FDATE *)pbDst, (FTIME *)(pbDst + 2), pEntry->Info.AccessTime, pDataBuf->cMinLocalTimeDelta);
+            pbDst += sizeof(FDATE) + sizeof(FTIME);
+            vboxSfOs2DateTimeFromTimeSpec((FDATE *)pbDst, (FTIME *)(pbDst + 2), pEntry->Info.ModificationTime, pDataBuf->cMinLocalTimeDelta);
+            pbDst += sizeof(FDATE) + sizeof(FTIME);
+
+            /* File size, allocation size, attributes: */
+            if (uLevel >= FI_LVL_STANDARD_64)
+            {
+                *(uint64_t *)pbDst = pEntry->Info.cbObject;
+                pbDst += sizeof(uint64_t);
+                *(uint64_t *)pbDst = pEntry->Info.cbAllocated;
+                pbDst += sizeof(uint64_t);
+                *(uint32_t *)pbDst = (pEntry->Info.Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT;
+                pbDst += sizeof(uint32_t);
+            }
+            else
+            {
+                *(uint32_t *)pbDst = (uint32_t)RT_MIN(pEntry->Info.cbObject, _2G - 1);
+                pbDst += sizeof(uint32_t);
+                *(uint32_t *)pbDst = (uint32_t)RT_MIN(pEntry->Info.cbAllocated, _2G - 1);
+                pbDst += sizeof(uint32_t);
+                *(uint16_t *)pbDst = (uint16_t)((pEntry->Info.Attr.fMode & RTFS_DOS_MASK_OS2) >> RTFS_DOS_SHIFT);
+                pbDst += sizeof(uint16_t); /* (Curious: Who is expanding this to 32-bits for 32-bit callers? */
+            }
+
+            /* Extra EA related fields: */
+            if (   uLevel == FI_LVL_STANDARD
+                || uLevel == FI_LVL_STANDARD_64)
+            { /* nothing */ }
+            else if (   uLevel == FI_LVL_STANDARD_EASIZE
+                     || uLevel == FI_LVL_STANDARD_EASIZE_64)
+            {
+                /* EA size: */
+                *(uint32_t *)pbDst = 0;
+                pbDst += sizeof(uint32_t);
+            }
+            else
+            {
+                /* Empty FEALIST - flush pending data first: */
+                uint32_t cbToCopy = pbDst - pbToCopy;
+                if (cbToCopy < cbData)
+                {
+                    rc = KernCopyOut(pbData, pbToCopy, cbToCopy);
+                    if (rc == NO_ERROR)
+                    {
+                        pbData += cbToCopy;
+                        cbData -= cbToCopy;
+                        pbDst   = pbToCopy;
+
+                        uint32_t cbWritten = 0;
+                        EaOp.fpFEAList = (PFEALIST)pbData;
+                        rc = vboxSfOs2MakeEmptyEaListEx(&EaOp, uLevel, &cbWritten, &pEaOpUser->oError);
+                        if (rc == NO_ERROR)
+                        {
+                            cbData -= cbWritten;
+                            pbData += cbWritten;
+                        }
+                    }
+                }
+                else
+                    rc = ERROR_BUFFER_OVERFLOW;
+                if (rc != NO_ERROR)
+                    break;
+            }
+
+            /* The length prefixed filename. */
+            if (pDataBuf->fLongFilenames)
+                pbDst = vboxSfOs2CopyUtf8Name(pbDst, pDataBuf->wszTmp, sizeof(pDataBuf->wszTmp),
+                                           (char *)pEntry->name.String.utf8, pEntry->name.u16Length);
+            else if (pEntry->cucShortName == 0)
+                pbDst = vboxSfOs2CopyUtf8NameAndUpperCase(pbDst, pDataBuf->wszTmp, sizeof(pDataBuf->wszTmp),
+                                                       (char *)pEntry->name.String.utf8, pEntry->name.u16Length);
+            else
+                pbDst = vboxSfOs2CopyUtf16NameAndUpperCase(pbDst, pEntry->uszShortName, pEntry->cucShortName);
+            if (pbDst)
+            {
+                /*
+                 * Copy out the staged data.
+                 */
+                uint32_t cbToCopy = pbDst - pbToCopy;
+                if (cbToCopy <= cbData)
+                {
+                    rc = KernCopyOut(pbData, pbToCopy, cbToCopy);
+                    if (rc == NO_ERROR)
+                    {
+                        Log4(("vboxSfOs2ReadDirEntries: match #%u LB %#x: '%s'\n", cMatches, cbToCopy, pEntry->name.String.utf8));
+                        Log4(("%.*Rhxd\n", cbToCopy, pbToCopy));
+
+                        pbData += cbToCopy;
+                        cbData -= cbToCopy;
+                        pbDst   = pbToCopy;
+
+                        cMatches++;
+                        pFsFsd->offLastFile++;
+                    }
+                    else
+                        break;
+                }
+                else
+                {
+                    rc = ERROR_BUFFER_OVERFLOW;
+                    break;
+                }
+            }
+            else
+            {
+                /* Name conversion issue, just skip the entry. */
+                Log3(("vboxSfOs2ReadDirEntries: Skipping '%s' due to name conversion issue.\n", pEntry->name.String.utf8));
+                cbData -= pbUserBufStart - pbData;
+                pbData  = pbUserBufStart;
+            }
+        }
+        else
+            Log3(("vboxSfOs2ReadDirEntries: fMode=%#x filter out by %#x/%#x; '%s'\n",
+                  pEntry->Info.Attr.fMode, pDataBuf->fMustHaveAttribs, pDataBuf->fExcludedAttribs, pEntry->name.String.utf8));
+
+        /*
+         * Advance to the next directory entry from the host.
+         */
+        if (pDataBuf->cEntriesLeft-- > 1)
+        {
+            pDataBuf->pEntry = pEntry = (PSHFLDIRINFO)&pEntry->name.String.utf8[pEntry->name.u16Size];
+            uintptr_t offEntry = (uintptr_t)pEntry - (uintptr_t)(pDataBuf + 1);
+            AssertMsgReturn(offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) <= pDataBuf->cbValid,
+                            ("offEntry=%#x cbValid=%#x\n", offEntry, pDataBuf->cbValid), ERROR_SYS_INTERNAL);
+            AssertMsgReturn(offEntry + RT_UOFFSETOF(SHFLDIRINFO, name.String) + pEntry->name.u16Size <= pDataBuf->cbValid,
+                            ("offEntry=%#x cbValid=%#x\n", offEntry, pDataBuf->cbValid), ERROR_SYS_INTERNAL);
+        }
+        else
+            pDataBuf->pEntry = NULL;
+    }
+
+    *pcMatches = cMatches;
+
+    /* Ignore buffer overflows if we've got matches to return. */
+    if (rc == ERROR_BUFFER_OVERFLOW && cMatches > 0)
+        rc = NO_ERROR;
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_FINDFIRST(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszPath, LONG offCurDirEnd, ULONG fAttribs,
+               PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches, ULONG uLevel, ULONG fFlags)
+{
+    LogFlow(("pCdFsi=%p pCdFsd=%p pszPath=%p:{%s} offCurDirEnd=%d fAttribs=%#x pFsFsi=%p pFsFsd=%p pbData=%p cbData=%#x pcMatches=%p:{%#x} uLevel=%#x fFlags=%#x\n",
+             pCdFsi, pCdFsd, pszPath, pszPath, offCurDirEnd, fAttribs, pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, fFlags));
+    USHORT const cMaxMatches = *pcMatches;
+    *pcMatches = 0;
+
+    /*
+     * Input validation.
+     */
+    switch (uLevel)
+    {
+        case FI_LVL_STANDARD:
+        case FI_LVL_STANDARD_64:
+        case FI_LVL_STANDARD_EASIZE:
+        case FI_LVL_STANDARD_EASIZE_64:
+            break;
+
+        case FI_LVL_EAS_FROM_LIST:
+        case FI_LVL_EAS_FROM_LIST_64:
+            if (cbData < sizeof(EAOP))
+            {
+                Log(("FS32_FINDFIRST: Buffer smaller than EAOP: %#x\n", cbData));
+                return ERROR_BUFFER_OVERFLOW;
+            }
+            break;
+
+        default:
+            LogRel(("FS32_FINDFIRST: Unsupported info level %u!\n", uLevel));
+            return ERROR_INVALID_LEVEL;
+    }
+
+    /*
+     * Resolve path to a folder and folder relative path.
+     */
+    PVBOXSFFOLDER pFolder;
+    PSHFLSTRING   pStrFolderPath;
+    RT_NOREF(pCdFsi);
+    APIRET rc = vboxSfOs2ResolvePath(pszPath, pCdFsd, offCurDirEnd, &pFolder, &pStrFolderPath);
+    LogFlow(("FS32_FINDFIRST: vboxSfOs2ResolvePath: -> %u pFolder=%p\n", rc, pFolder));
+    if (rc == NO_ERROR)
+    {
+        /*
+         * Look for a wildcard filter at the end of the path, saving it all for
+         * later in NT filter speak if present.
+         */
+        PSHFLSTRING pFilter = NULL;
+        char *pszFilter = RTPathFilename((char *)pStrFolderPath->String.utf8);
+        if (   pszFilter
+            && (   strchr(pszFilter, '*') != NULL
+                || strchr(pszFilter, '?') != NULL))
+        {
+            if (strcmp(pszFilter, "*.*") == 0)
+            {
+                /* All files, no filtering needed. Just drop the filter expression from the directory path. */
+                *pszFilter = '\0';
+                pStrFolderPath->u16Length = (uint16_t)((uint8_t *)pszFilter - &pStrFolderPath->String.utf8[0]);
+            }
+            else
+            {
+                /* Duplicate the whole path. */
+                pFilter = vboxSfOs2StrDup(pStrFolderPath->String.ach, pStrFolderPath->u16Length);
+                if (pFilter)
+                {
+                    /* Drop filter from directory path. */
+                    *pszFilter = '\0';
+                    pStrFolderPath->u16Length = (uint16_t)((uint8_t *)pszFilter - &pStrFolderPath->String.utf8[0]);
+
+                    /* Convert filter part of the copy to NT speak. */
+                    pszFilter = (char *)&pFilter->String.utf8[(uint8_t *)pszFilter - &pStrFolderPath->String.utf8[0]];
+                    for (;;)
+                    {
+                        char ch = *pszFilter;
+                        if (ch == '?')
+                            *pszFilter = '>';       /* The DOS question mark: Matches one char, but dots and end-of-name eats them. */
+                        else if (ch == '.')
+                        {
+                            char ch2 = pszFilter[1];
+                            if (ch2 == '*' || ch2 == '?')
+                                *pszFilter = '"';   /* The DOS dot: Matches a dot or end-of-name. */
+                        }
+                        else if (ch == '*')
+                        {
+                            if (pszFilter[1] == '.')
+                                *pszFilter = '<';   /* The DOS star: Matches zero or more chars except the DOS dot.*/
+                        }
+                        else if (ch == '\0')
+                            break;
+                        pszFilter++;
+                    }
+                }
+                else
+                    rc = ERROR_NOT_ENOUGH_MEMORY;
+            }
+        }
+        /*
+         * When no wildcard is specified, we're supposed to return a single entry
+         * with the name in the final component.  Exception is the root, where we
+         * always list the whole thing.
+         *
+         * Not sure if we'll ever see a trailing slash here (pszFilter == NULL),
+         * but if we do we should accept it only for the root.
+         */
+        else if (pszFilter)
+        {
+            pFilter = pStrFolderPath;
+            pStrFolderPath = vboxSfOs2StrDup(pFilter->String.ach, pszFilter - pFilter->String.ach);
+            if (!pStrFolderPath)
+                rc = ERROR_NOT_ENOUGH_MEMORY;
+        }
+        else if (!pszFilter && pStrFolderPath->u16Length > 1)
+        {
+            LogFlow(("FS32_FINDFIRST: Trailing slash (%s)\n", pStrFolderPath->String.utf8));
+            rc = ERROR_PATH_NOT_FOUND;
+        }
+        else
+            LogFlow(("FS32_FINDFIRST: Root dir (%s)\n", pStrFolderPath->String.utf8));
+
+        /*
+         * Make sure we've got a buffer for keeping unused search results.
+         */
+        PVBOXSFFSBUF pDataBuf = NULL;
+        if (rc == NO_ERROR)
+        {
+            pDataBuf = (PVBOXSFFSBUF)RTMemAlloc(cMaxMatches == 1 ? VBOXSFFSBUF_MIN_SIZE : _16K - ALLOC_HDR_SIZE);
+            if (pDataBuf)
+                pDataBuf->cbBuf = cMaxMatches == 1 ? VBOXSFFSBUF_MIN_SIZE : _16K - ALLOC_HDR_SIZE;
+            else
+            {
+                pDataBuf = (PVBOXSFFSBUF)RTMemAlloc(VBOXSFFSBUF_MIN_SIZE);
+                if (pDataBuf)
+                    pDataBuf->cbBuf = VBOXSFFSBUF_MIN_SIZE;
+                else
+                    rc = ERROR_NOT_ENOUGH_MEMORY;
+            }
+        }
+        if (rc == NO_ERROR)
+        {
+            /*
+             * Now, try open the directory for reading.
+             * We pre-use the data buffer for parameter passin to avoid
+             * wasting any stack space.
+             */
+            PSHFLCREATEPARMS pParams = (PSHFLCREATEPARMS)(pDataBuf + 1);
+            RT_ZERO(*pParams);
+            pParams->CreateFlags = SHFL_CF_DIRECTORY   | SHFL_CF_ACT_FAIL_IF_NEW  | SHFL_CF_ACT_OPEN_IF_EXISTS
+                                 | SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_ATTR_READ | SHFL_CF_ACCESS_DENYNONE;
+            int vrc = VbglR0SfCreate(&g_SfClient, &pFolder->hHostFolder, pStrFolderPath, pParams);
+            LogFlow(("FS32_FINDFIRST: VbglR0SfCreate(%s) -> %Rrc Result=%d fMode=%#x hHandle=%#RX64\n",
+                     pStrFolderPath->String.ach, vrc, pParams->Result, pParams->Info.Attr.fMode, pParams->Handle));
+            if (RT_SUCCESS(vrc))
+            {
+                switch (pParams->Result)
+                {
+                    case SHFL_FILE_EXISTS:
+                        if (pParams->Handle != SHFL_HANDLE_NIL)
+                        {
+                            /*
+                             * Initialize the structures.
+                             */
+                            pFsFsd->hHostDir        = pParams->Handle;
+                            pFsFsd->u32Magic        = VBOXSFFS_MAGIC;
+                            pFsFsd->pFolder         = pFolder;
+                            pFsFsd->pBuf            = pDataBuf;
+                            pFsFsd->offLastFile     = 0;
+                            pDataBuf->u32Magic      = VBOXSFFSBUF_MAGIC;
+                            pDataBuf->cbValid       = 0;
+                            pDataBuf->cEntriesLeft  = 0;
+                            pDataBuf->pEntry        = NULL;
+                            pDataBuf->pFilter       = pFilter;
+                            pDataBuf->fMustHaveAttribs   = (uint8_t)((fAttribs >> 8) & (RTFS_DOS_MASK_OS2 >> RTFS_DOS_SHIFT));
+                            pDataBuf->fExcludedAttribs   = (uint8_t)(~fAttribs
+                                                                     & (  (RTFS_DOS_MASK_OS2 & ~(RTFS_DOS_ARCHIVED | RTFS_DOS_READONLY)
+                                                                        >> RTFS_DOS_SHIFT)));
+                            pDataBuf->fLongFilenames     = RT_BOOL(fAttribs & FF_ATTR_LONG_FILENAME);
+                            pDataBuf->cMinLocalTimeDelta = vboxSfOs2GetLocalTimeDelta();
+
+                            rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags,
+                                                         pbData, cbData, cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
+                            if (rc == NO_ERROR)
+                            {
+                                uint32_t cRefs = ASMAtomicIncU32(&pFolder->cOpenSearches);
+                                Assert(cRefs < _4K); RT_NOREF(cRefs);
+
+                                /* We keep these on success: */
+                                if (pFilter == pStrFolderPath)
+                                    pStrFolderPath = NULL;
+                                pFilter  = NULL;
+                                pDataBuf = NULL;
+                                pFolder  = NULL;
+                            }
+                            else
+                            {
+                                vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, pFsFsd->hHostDir);
+                                AssertRC(vrc);
+                                pFsFsd->u32Magic = ~VBOXSFFS_MAGIC;
+                                pDataBuf->u32Magic = ~VBOXSFFSBUF_MAGIC;
+                                pFsFsd->pFolder  = NULL;
+                                pFsFsd->hHostDir = NULL;
+                            }
+                        }
+                        else
+                        {
+                            LogFlow(("FS32_FINDFIRST: VbglR0SfCreate returns NIL handle for '%s'\n", pStrFolderPath->String.utf8));
+                            rc = ERROR_PATH_NOT_FOUND;
+                        }
+                        break;
+
+                    case SHFL_PATH_NOT_FOUND:
+                        rc = ERROR_PATH_NOT_FOUND;
+                        break;
+
+                    default:
+                    case SHFL_FILE_NOT_FOUND:
+                        rc = ERROR_FILE_NOT_FOUND;
+                        break;
+                }
+            }
+            else
+                rc = vboxSfOs2ConvertStatusToOs2(vrc, ERROR_GEN_FAILURE);
+        }
+
+        RTMemFree(pDataBuf);
+        if (pFilter != pStrFolderPath)
+            vboxSfOs2StrFree(pFilter);
+        vboxSfOs2ReleasePathAndFolder(pStrFolderPath, pFolder);
+    }
+    LogFlow(("FS32_FINDFIRST: returns %u\n", rc));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_FINDFROMNAME(PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches,
+                  ULONG uLevel, ULONG uPosition, PCSZ pszName, ULONG fFlags)
+{
+    LogFlow(("FS32_FINDFROMNAME: pFsFsi=%p pFsFsd=%p pbData=%p cbData=%#x pcMatches=%p:{%#x} uLevel=%#x uPosition=%#x pszName=%p:{%s} fFlags=%#x\n",
+             pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, uPosition, pszName, pszName, fFlags));
+
+    /*
+     * Input validation.
+     */
+    USHORT const cMaxMatches = *pcMatches;
+    *pcMatches = 0;
+    AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenSearches > 0);
+    PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
+    AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
+    Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
+
+    switch (uLevel)
+    {
+        case FI_LVL_STANDARD:
+        case FI_LVL_STANDARD_64:
+        case FI_LVL_STANDARD_EASIZE:
+        case FI_LVL_STANDARD_EASIZE_64:
+            break;
+
+        case FI_LVL_EAS_FROM_LIST:
+        case FI_LVL_EAS_FROM_LIST_64:
+            Log(("FS32_FINDFIRST: FI_LVL_EAS_FROM_LIST[_64] -> ERROR_EAS_NOT_SUPPORTED\n"));
+            return ERROR_EAS_NOT_SUPPORTED;
+
+        default:
+            LogRel(("FS32_FINDFIRST: Unsupported info level %u!\n", uLevel));
+            return ERROR_INVALID_LEVEL;
+    }
+
+    /*
+     * Check if we're just continuing.  This is usually the case.
+     */
+    APIRET rc;
+    if (uPosition == pFsFsd->offLastFile)
+        rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
+                                     cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
+    else
+    {
+        Log(("TODO: uPosition differs: %#x, expected %#x (%s)\n", uPosition, pFsFsd->offLastFile, pszName));
+        rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
+                                     cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
+    }
+
+    RT_NOREF(pFsFsi, pszName);
+    LogFlow(("FS32_FINDFROMNAME: returns %u (*pcMatches=%#x)\n", rc, *pcMatches));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_FINDNEXT(PFSFSI pFsFsi, PVBOXSFFS pFsFsd, PBYTE pbData, ULONG cbData, PUSHORT pcMatches, ULONG uLevel, ULONG fFlags)
+{
+    LogFlow(("FS32_FINDNEXT: pFsFsi=%p pFsFsd=%p pbData=%p cbData=%#x pcMatches=%p:{%#x} uLevel=%#x fFlags=%#x\n",
+             pFsFsi, pFsFsd, pbData, cbData, pcMatches, *pcMatches, uLevel, fFlags));
+
+    /*
+     * Input validation.
+     */
+    USHORT const cMaxMatches = *pcMatches;
+    *pcMatches = 0;
+    AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenSearches > 0);
+    PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
+    AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
+    Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
+
+    switch (uLevel)
+    {
+        case FI_LVL_STANDARD:
+        case FI_LVL_STANDARD_64:
+        case FI_LVL_STANDARD_EASIZE:
+        case FI_LVL_STANDARD_EASIZE_64:
+            break;
+
+        case FI_LVL_EAS_FROM_LIST:
+        case FI_LVL_EAS_FROM_LIST_64:
+            Log(("FS32_FINDFIRST: FI_LVL_EAS_FROM_LIST[_64] -> ERROR_EAS_NOT_SUPPORTED\n"));
+            return ERROR_EAS_NOT_SUPPORTED;
+
+        default:
+            LogRel(("FS32_FINDFIRST: Unsupported info level %u!\n", uLevel));
+            return ERROR_INVALID_LEVEL;
+    }
+
+    /*
+     * Read more.
+     */
+    APIRET rc = vboxSfOs2ReadDirEntries(pFolder, pFsFsd, pDataBuf, uLevel, fFlags, pbData, cbData,
+                                        cMaxMatches ? cMaxMatches : UINT16_MAX, pcMatches);
+
+    NOREF(pFsFsi);
+    LogFlow(("FS32_FINDNEXT: returns %u (*pcMatches=%#x)\n", rc, *pcMatches));
+    return rc;
+}
+
+
+DECLASM(APIRET)
+FS32_FINDCLOSE(PFSFSI pFsFsi, PVBOXSFFS pFsFsd)
+{
+    /*
+     * Input validation.
+     */
+    AssertReturn(pFsFsd->u32Magic == VBOXSFFS_MAGIC, ERROR_SYS_INTERNAL);
+    PVBOXSFFOLDER pFolder = pFsFsd->pFolder;
+    AssertReturn(pFolder != NULL, ERROR_SYS_INTERNAL);
+    Assert(pFolder->u32Magic == VBOXSFFOLDER_MAGIC);
+    Assert(pFolder->cOpenSearches > 0);
+    PVBOXSFFSBUF pDataBuf = pFsFsd->pBuf;
+    AssertReturn(pDataBuf, ERROR_SYS_INTERNAL);
+    Assert(pDataBuf->u32Magic == VBOXSFFSBUF_MAGIC);
+
+    /*
+     * Close it.
+     */
+    if (pFsFsd->hHostDir != SHFL_HANDLE_NIL)
+    {
+        int vrc = VbglR0SfClose(&g_SfClient, &pFolder->hHostFolder, pFsFsd->hHostDir);
+        AssertRC(vrc);
+    }
+
+    pFsFsd->u32Magic   = ~VBOXSFFS_MAGIC;
+    pFsFsd->hHostDir   = SHFL_HANDLE_NIL;
+    pFsFsd->pFolder    = NULL;
+    pFsFsd->pBuf       = NULL;
+    vboxSfOs2StrFree(pDataBuf->pFilter);
+    pDataBuf->pFilter  = NULL;
+    pDataBuf->u32Magic = ~VBOXSFFSBUF_MAGIC;
+    pDataBuf->cbBuf    = 0;
+    RTMemFree(pDataBuf);
+
+    uint32_t cRefs = ASMAtomicDecU32(&pFolder->cOpenSearches);
+    Assert(cRefs < _4K); RT_NOREF(cRefs);
+    vboxSfOs2ReleaseFolder(pFolder);
+
+    RT_NOREF(pFsFsi);
+    LogFlow(("FS32_FINDCLOSE: returns NO_ERROR\n"));
+    return NO_ERROR;
+}
+
+
+
+
+
+DECLASM(APIRET)
+FS32_FINDNOTIFYFIRST(PCDFSI pCdFsi, PVBOXSFCD pCdFsd, PCSZ pszPath, LONG offCurDirEnd, ULONG fAttribs,
+                     PUSHORT phHandle, PBYTE pbData, ULONG cbData, PUSHORT pcMatches,
+                     ULONG uLevel, ULONG fFlags)
+{
+    RT_NOREF(pCdFsi, pCdFsd, pszPath, offCurDirEnd, fAttribs, phHandle, pbData, cbData, pcMatches, uLevel, fFlags);
     return ERROR_NOT_SUPPORTED;
 }
 
 
-DECLASM(int)
-FS32_FINDFROMNAME(PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
-                  USHORT uLevel, ULONG position, PCSZ pszName, USHORT fFlags)
-{
-    NOREF(pfsfsi); NOREF(pfsfsd); NOREF(pbData); NOREF(cbData); NOREF(pcMatch); NOREF(uLevel); NOREF(position); NOREF(pszName);
-    NOREF(fFlags);
+DECLASM(APIRET)
+FS32_FINDNOTIFYNEXT(ULONG hHandle, PBYTE pbData, ULONG cbData, PUSHORT pcMatchs, ULONG uLevel, ULONG cMsTimeout)
+{
+    RT_NOREF(hHandle, pbData, cbData, pcMatchs, uLevel, cMsTimeout);
     return ERROR_NOT_SUPPORTED;
 }
 
 
-DECLASM(int)
-FS32_FINDNEXT(PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
-              USHORT uLevel, USHORT fFlags)
-{
-    NOREF(pfsfsi); NOREF(pfsfsd); NOREF(pbData); NOREF(cbData); NOREF(pcMatch); NOREF(uLevel); NOREF(fFlags);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_FINDCLOSE(PFSFSI pfsfsi, PVBOXSFFS pfsfsd)
-{
-    NOREF(pfsfsi); NOREF(pfsfsd);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-
-
-
-DECLASM(int)
-FS32_FINDNOTIFYFIRST(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, USHORT fAtt,
-                     PUSHORT phHandle, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
-                     USHORT uLevel, USHORT fFlags)
-{
-    NOREF(pcdfsi); NOREF(pcdfsd); NOREF(pszName); NOREF(iCurDirEnd); NOREF(fAtt); NOREF(phHandle); NOREF(pbData); NOREF(cbData);
-    NOREF(pcMatch); NOREF(uLevel); NOREF(fFlags);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_FINDNOTIFYNEXT(USHORT hHandle, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
-                    USHORT uLevel, ULONG cMsTimeout)
-{
-    NOREF(hHandle); NOREF(pbData); NOREF(cbData); NOREF(pcMatch); NOREF(uLevel); NOREF(cMsTimeout);
-    return ERROR_NOT_SUPPORTED;
-}
-
-
-DECLASM(int)
-FS32_FINDNOTIFYCLOSE(USHORT hHandle)
+DECLASM(APIRET)
+FS32_FINDNOTIFYCLOSE(ULONG hHandle)
 {
     NOREF(hHandle);
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp	(revision 75336)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp	(revision 75337)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ * Copyright (c) 2007-2018 knut st. osmundsen <bird-src-spam@anduin.net>
  *
  * Permission is hereby granted, free of charge, to any person
@@ -37,4 +37,5 @@
 
 #include <VBox/VBoxGuestLib.h>
+#include <VBox/VBoxGuest.h>
 #include <VBox/log.h>
 #include <iprt/assert.h>
@@ -70,6 +71,13 @@
 DECLASM(void) VBoxSFR0Init(void)
 {
-    Log(("VBoxSFR0Init: g_fpfnDevHlp=%lx u32Version=%RX32 u32Session=%RX32 pfnServiceEP=%p g_u32Info=%u (%#x)\n",
-         g_fpfnDevHlp, g_VBoxGuestIDC.u32Version, g_VBoxGuestIDC.u32Session, g_VBoxGuestIDC.pfnServiceEP, g_u32Info, g_u32Info));
+    RTLogBackdoorPrintf("VBoxSFR0Init: g_fpfnDevHlp=%lx u32Version=%RX32 u32Session=%RX32 pfnServiceEP=%p g_u32Info=%u (%#x)\n",
+                        g_fpfnDevHlp, g_VBoxGuestIDC.u32Version, g_VBoxGuestIDC.u32Session, g_VBoxGuestIDC.pfnServiceEP, g_u32Info, g_u32Info);
+    RTLogBackdoorPrintf("&KernSISData=%p\n",        &KernSISData);
+    RTLogBackdoorPrintf("&KernLISData=%p\n",        &KernLISData);
+    RTLogBackdoorPrintf("KernInterruptLevel=%#x\n", KernInterruptLevel);
+    RTLogBackdoorPrintf("KernTKSSBase=%p\n",        KernTKSSBase);
+
+    KernAllocMutexLock(&g_MtxFolders);
+    RTListInit(&g_FolderHead);
 
     /*
@@ -101,13 +109,13 @@
 #endif
 
-                Log(("VBoxSFR0Init: completed successfully\n"));
+                RTLogBackdoorPrintf("VBoxSFR0Init: completed successfully\n");
                 return;
             }
         }
 
-        LogRel(("VBoxSF: RTR0Init failed, rc=%Rrc\n", rc));
+        RTLogBackdoorPrintf("VBoxSF: RTR0Init failed, rc=%Rrc\n", rc);
     }
     else
-        LogRel(("VBoxSF: Failed to connect to VBoxGuest.sys.\n"));
+        RTLogBackdoorPrintf("VBoxSF: Failed to connect to VBoxGuest.sys.\n");
 }
 
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInternal.h
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInternal.h	(revision 75336)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInternal.h	(revision 75337)
@@ -36,5 +36,5 @@
 #define INCL_ERROR
 #define INCL_LONGLONG
-#include <os2.h>
+#define OS2EMX_PLAIN_CHAR
 #include <os2ddk/bsekee.h>
 #include <os2ddk/devhlp.h>
@@ -45,4 +45,51 @@
 #include <iprt/types.h>
 #include <iprt/assert.h>
+#include <iprt/list.h>
+#include <VBox/VBoxGuestLibSharedFolders.h>
+
+
+/** Allocation header used by RTMemAlloc.
+ * This should be subtracted from round numbers. */
+#define ALLOC_HDR_SIZE  (0x10 + 4)
+
+
+/**
+ * A shared folder
+ */
+typedef struct VBOXSFFOLDER
+{
+    /** For the shared folder list. */
+    RTLISTNODE          ListEntry;
+    /** Magic number (VBOXSFFOLDER_MAGIC). */
+    uint32_t            u32Magic;
+    /** Number of active references to this folder. */
+    uint32_t volatile   cRefs;
+    /** Number of open files referencing this folder.   */
+    uint32_t volatile   cOpenFiles;
+    /** Number of open searches referencing this folder.   */
+    uint32_t volatile   cOpenSearches;
+    /** Number of drives this is attached to. */
+    uint8_t volatile    cDrives;
+
+    /** The host folder handle. */
+    VBGLSFMAP           hHostFolder;
+
+    /** OS/2 volume handle. */
+    USHORT              hVpb;
+
+    /** The length of the folder name. */
+    uint8_t             cchName;
+    /** The shared folder name. */
+    char                szName[RT_FLEXIBLE_ARRAY];
+} VBOXSFFOLDER;
+/** Pointer to a shared folder. */
+typedef VBOXSFFOLDER *PVBOXSFFOLDER;
+/** Magic value for VBOXSFVP (Neal Town Stephenson). */
+#define VBOXSFFOLDER_MAGIC      UINT32_C(0x19591031)
+
+/** The shared mutex protecting folders list, drives and the connection. */
+extern MutexLock_t      g_MtxFolders;
+/** List of active folder (PVBOXSFFOLDER). */
+extern RTLISTANCHOR     g_FolderHead;
 
 
@@ -50,13 +97,19 @@
  * VBoxSF Volume Parameter Structure.
  *
- * @remark  Overlays the 36 byte VPFSD structure (fsd.h).
+ * @remarks Overlays the 36 byte VPFSD structure (fsd.h).
+ * @note    No self pointer as the kernel may reallocate these.
  */
 typedef struct VBOXSFVP
 {
-    uint32_t u32Dummy;
+    /** Magic value (VBOXSFVP_MAGIC). */
+    uint32_t         u32Magic;
+    /** The folder. */
+    PVBOXSFFOLDER    pFolder;
 } VBOXSFVP;
 AssertCompile(sizeof(VBOXSFVP) <= sizeof(VPFSD));
 /** Pointer to a VBOXSFVP struct. */
 typedef VBOXSFVP *PVBOXSFVP;
+/** Magic value for VBOXSFVP (Laurence van Cott Niven). */
+#define VBOXSFVP_MAGIC          UINT32_C(0x19380430)
 
 
@@ -80,12 +133,63 @@
  * @remark  Overlays the 30 byte SFFSD structure (fsd.h).
  */
-typedef struct VBOXSFFSD
-{
+typedef struct VBOXSFSYFI
+{
+    /** Magic value (VBOXSFSYFI_MAGIC). */
+    uint32_t            u32Magic;
     /** Self pointer for quick 16:16 to flat translation. */
-    struct VBOXSFFSD *pSelf;
-} VBOXSFFSD;
-AssertCompile(sizeof(VBOXSFFSD) <= sizeof(SFFSD));
-/** Pointer to a VBOXSFFSD struct. */
-typedef VBOXSFFSD *PVBOXSFFSD;
+    struct VBOXSFSYFI  *pSelf;
+    /** The host file handle. */
+    SHFLHANDLE          hHostFile;
+    /** The shared folder (referenced). */
+    PVBOXSFFOLDER       pFolder;
+} VBOXSFSYFI;
+AssertCompile(sizeof(VBOXSFSYFI) <= sizeof(SFFSD));
+/** Pointer to a VBOXSFSYFI struct. */
+typedef VBOXSFSYFI *PVBOXSFSYFI;
+/** Magic value for VBOXSFSYFI (Jon Ellis Meacham). */
+#define VBOXSFSYFI_MAGIC         UINT32_C(0x19690520)
+
+
+/**
+ * VBoxSF File Search Buffer (header).
+ */
+typedef struct VBOXSFFSBUF
+{
+    /** A magic number (VBOXSFFSBUF_MAGIC). */
+    uint32_t            u32Magic;
+    /** Amount of buffer space allocated after this header. */
+    uint32_t            cbBuf;
+    /** The filter string (full path), NULL if all files are request. */
+    PSHFLSTRING         pFilter;
+    /** Must have attributes (shifted down DOS attributes).  */
+    uint8_t             fMustHaveAttribs;
+    /** Non-matching attributes (shifted down DOS attributes).  */
+    uint8_t             fExcludedAttribs;
+    /** Set if FF_ATTR_LONG_FILENAME. */
+    bool                fLongFilenames : 1;
+    uint8_t             bPadding1;
+    /** The local time offset to use for this search. */
+    int16_t             cMinLocalTimeDelta;
+    uint8_t             abPadding2[2];
+    /** Number of valid bytes in the buffer. */
+    uint32_t            cbValid;
+    /** Number of entries left in the buffer.   */
+    uint32_t            cEntriesLeft;
+    /** The next entry. */
+    PSHFLDIRINFO        pEntry;
+    /** Staging area for staging a full FILEFINDBUF4L (+ 32 safe bytes). */
+    uint8_t             abStaging[RT_ALIGN_32(sizeof(FILEFINDBUF4L) + 32, 8)];
+    /** For temporary convertion to UTF-8 so we can use KernStrFromUcs to get
+     *  string encoded according to the process codepage. */
+    RTUTF16             wszTmp[260];
+} VBOXSFFSBUF;
+AssertCompileSizeAlignment(VBOXSFFSBUF, 8);
+/** Pointer to a file search buffer. */
+typedef VBOXSFFSBUF *PVBOXSFFSBUF;
+/** Magic number for VBOXSFFSBUF (Robert Anson Heinlein). */
+#define VBOXSFFSBUF_MAGIC       UINT32_C(0x19070707)
+/** Minimum buffer size. */
+#define VBOXSFFSBUF_MIN_SIZE (  RT_ALIGN_32(sizeof(VBOXSFFSBUF) + sizeof(SHFLDIRINFO) + CCHMAXPATHCOMP * 4 + ALLOC_HDR_SIZE, 64) \
+                              - ALLOC_HDR_SIZE)
 
 
@@ -94,14 +198,47 @@
  *
  * @remark  Overlays the 24 byte FSFSD structure (fsd.h).
+ * @note    No self pointer as the kernel may reallocate these.
  */
 typedef struct VBOXSFFS
 {
-    /** Self pointer for quick 16:16 to flat translation. */
-    struct VBOXSFFS *pSelf;
+    /** Magic value (VBOXSFFS_MAGIC). */
+    uint32_t            u32Magic;
+    /** The last file position position. */
+    uint32_t            offLastFile;
+    /** The host directory handle. */
+    SHFLHANDLE          hHostDir;
+    /** The shared folder (referenced). */
+    PVBOXSFFOLDER       pFolder;
+    /** Search data buffer. */
+    PVBOXSFFSBUF        pBuf;
 } VBOXSFFS;
 AssertCompile(sizeof(VBOXSFFS) <= sizeof(FSFSD));
 /** Pointer to a VBOXSFFS struct. */
 typedef VBOXSFFS *PVBOXSFFS;
-
+/** Magic number for VBOXSFFS (Isaak Azimov). */
+#define VBOXSFFS_MAGIC          UINT32_C(0x19200102)
+
+
+extern VBGLSFCLIENT g_SfClient;
+
+PSHFLSTRING vboxSfOs2StrAlloc(size_t cchLength);
+PSHFLSTRING vboxSfOs2StrDup(const char *pachSrc, size_t cchSrc);
+void        vboxSfOs2StrFree(PSHFLSTRING pStr);
+
+APIRET      vboxSfOs2ResolvePath(const char *pszPath, PVBOXSFCD pCdFsd, LONG offCurDirEnd,
+                                 PVBOXSFFOLDER *ppFolder, PSHFLSTRING *ppStrFolderPath);
+void        vboxSfOs2ReleasePathAndFolder(PSHFLSTRING pStrPath, PVBOXSFFOLDER pFolder);
+void        vboxSfOs2ReleaseFolder(PVBOXSFFOLDER pFolder);
+APIRET      vboxSfOs2ConvertStatusToOs2(int vrc, APIRET rcDefault);
+int16_t     vboxSfOs2GetLocalTimeDelta(void);
+void        vboxSfOs2DateTimeFromTimeSpec(FDATE *pDosDate, FTIME *pDosTime, RTTIMESPEC SrcTimeSpec, int16_t cMinLocalTimeDelta);
+PRTTIMESPEC vboxSfOs2DateTimeToTimeSpec(FDATE DosDate, FTIME DosTime, int16_t cMinLocalTimeDelta, PRTTIMESPEC pDstTimeSpec);
+APIRET      vboxSfOs2FileStatusFromObjInfo(PBYTE pbDst, ULONG cbDst, ULONG uLevel, SHFLFSOBJINFO const *pSrc);
+APIRET      vboxSfOs2SetInfoCommonWorker(PVBOXSFFOLDER pFolder, SHFLHANDLE hHostFile, ULONG fAttribs,
+                                         PFILESTATUS pTimestamps, PSHFLFSOBJINFO pObjInfoBuf);
+APIRET      vboxSfOs2MakeEmptyEaList(PEAOP pEaOp, ULONG uLevel);
+APIRET      vboxSfOs2MakeEmptyEaListEx(PEAOP pEaOp, ULONG uLevel, uint32_t *pcbWritten, ULONG *poffError);
+
+DECLASM(PVBOXSFVP) Fsh32GetVolParams(USHORT hVbp, PVPFSI *ppVpFsi /*optional*/);
 
 #endif
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFUtil.cpp
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFUtil.cpp	(revision 75337)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFUtil.cpp	(revision 75337)
@@ -0,0 +1,528 @@
+/** $Id$ */
+/** @file
+ * VBoxSF - OS/2 Shared Folders, Utility for attaching and testing.
+ */
+
+/*
+ * Copyright (c) 2016-2018 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#define INCL_BASE
+#include <os2.h>
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+extern "C" APIRET __cdecl CallDosQFileMode(const char *pszFilename, PUSHORT pfAttr, ULONG ulReserved);
+
+
+
+int vboxSfOs2UtilUse(int argc, char **argv)
+{
+    /*
+     * Continue parsing.
+     */
+    if (argc != 3)
+    {
+        fprintf(stderr, "syntax error: Expected three arguments to 'use' command\n");
+        return 2;
+    }
+
+    /* The drive letter. */
+    const char *pszDrive = argv[1];
+    if (   (   (pszDrive[0] >= 'A' && pszDrive[0] <= 'Z')
+            || (pszDrive[0] >= 'a' && pszDrive[0] <= 'z'))
+        && pszDrive[1] == ':'
+        && pszDrive[2] == '\0')
+    { /* likely */ }
+    else
+    {
+        fprintf(stderr, "syntax error: Invalid drive specification '%s', expected something like 'K:'.\n", pszDrive);
+        return 2;
+    }
+
+    /* The shared folder. */
+    const char *pszFolder = argv[2];
+    size_t      cchFolder = strlen(pszFolder);
+    if (cchFolder <= 80 && cchFolder != 0)
+    { /* likely */ }
+    else
+    {
+        fprintf(stderr, "syntax error: Shared folder name '%s' is too %s!\n", cchFolder >= 1 ? "long" : "short");
+        return 2;
+    }
+
+    /*
+     * Try attach it.
+     */
+    APIRET rc = DosFSAttach(pszDrive, "VBOXSF", (void *)pszFolder, cchFolder + 1, FS_ATTACH);
+    if (rc == NO_ERROR)
+    {
+        printf("done\n");
+        return 0;
+    }
+    fprintf(stderr, "error: DosFSAttach failed: %u\n", rc);
+    return 1;
+}
+
+
+int vboxSfOs2UtilQPathInfo(int argc, char **argv)
+{
+    for (int i = 1; i < argc; i++)
+    {
+        union
+        {
+            USHORT          fAttribs;
+            FILESTATUS      Lvl1r1;
+            FILESTATUS3     Lvl1r3;
+            FILESTATUS3L    Lvl11;
+            FILESTATUS2     Lvl2r2;
+            FILESTATUS4     Lvl2r4;
+            FILESTATUS4L    Lvl12;
+            FEA2LIST        FeaList;
+            char            szFullName[260];
+            //char            szTest1[sizeof(FILESTATUS3) == sizeof(FILESTATUS)];
+            //char            szTest2[sizeof(FILESTATUS4) == sizeof(FILESTATUS2)];
+        } u;
+
+        u.fAttribs = 0xffff;
+        int rc = CallDosQFileMode(argv[i], &u.fAttribs, 0 /* reserved */);
+        printf("%s: DosQFileMode -> %u, %#x\n", argv[i], rc, u.fAttribs);
+
+        memset(&u, 0xaa, sizeof(u));
+        rc = DosQueryPathInfo(argv[i], FIL_STANDARD, &u.Lvl1r1, sizeof(u.Lvl1r1));
+        printf("%s: FIL_STANDARD/%#x -> %u\n", argv[i], sizeof(u.Lvl1r1), rc);
+        if (rc == NO_ERROR)
+        {
+            printf("  Lvl1r1: creation=%u:%u write=%u:%u access=%u:%u\n",
+                   u.Lvl1r1.fdateCreation, u.Lvl1r1.ftimeCreation,  u.Lvl1r1.fdateLastWrite, u.Lvl1r1.ftimeLastWrite,
+                   u.Lvl1r1.fdateLastAccess, u.Lvl1r1.ftimeLastAccess);
+            printf("  Lvl1r1:  attrib=%#x size=%u alloc=%u\n", u.Lvl1r1.attrFile, u.Lvl1r1.cbFile, u.Lvl1r1.cbFileAlloc);
+
+        }
+
+        memset(&u, 0xbb, sizeof(u));
+        rc = DosQueryPathInfo(argv[i], FIL_STANDARD, &u.Lvl1r3, sizeof(u.Lvl1r3));
+        printf("%s: FIL_STANDARD/%#x-> %u\n", argv[i], sizeof(u.Lvl1r3), rc);
+        if (rc == NO_ERROR)
+        {
+            printf("  Lvl1r3: creation=%u:%u write=%u:%u access=%u:%u\n",
+                   u.Lvl1r3.fdateCreation, u.Lvl1r3.ftimeCreation,  u.Lvl1r3.fdateLastWrite, u.Lvl1r3.ftimeLastWrite,
+                   u.Lvl1r3.fdateLastAccess, u.Lvl1r3.ftimeLastAccess);
+            printf("  Lvl1r3:  attrib=%#x size=%u alloc=%u\n", u.Lvl1r3.attrFile, u.Lvl1r3.cbFile, u.Lvl1r3.cbFileAlloc);
+
+        }
+
+        memset(&u, 0xdd, sizeof(u));
+        rc = DosQueryPathInfo(argv[i], FIL_STANDARDL, &u.Lvl11, sizeof(u.Lvl11));
+        printf("%s: FIL_STANDARDL/%#x -> %u\n", argv[i], sizeof(u.Lvl11), rc);
+        if (rc == NO_ERROR)
+        {
+            printf("   Lvl11: creation=%u:%u write=%u:%u access=%u:%u\n",
+                   u.Lvl11.fdateCreation, u.Lvl11.ftimeCreation,  u.Lvl11.fdateLastWrite, u.Lvl11.ftimeLastWrite,
+                   u.Lvl11.fdateLastAccess, u.Lvl11.ftimeLastAccess);
+            printf("   Lvl11:  attrib=%#x size=%llu alloc=%llu\n", u.Lvl11.attrFile, u.Lvl11.cbFile, u.Lvl11.cbFileAlloc);
+        }
+
+        memset(&u, 0xee, sizeof(u));
+        rc = DosQueryPathInfo(argv[i], FIL_QUERYEASIZE, &u.Lvl2r2, sizeof(u.Lvl2r2));
+        printf("%s: FIL_QUERYEASIZE/%#x -> %u\n", argv[i], sizeof(u.Lvl2r2), rc);
+        if (rc == NO_ERROR)
+        {
+            printf("    Lvl2: creation=%u:%u write=%u:%u access=%u:%u\n",
+                   u.Lvl2r2.fdateCreation, u.Lvl2r2.ftimeCreation,  u.Lvl2r2.fdateLastWrite, u.Lvl2r2.ftimeLastWrite,
+                   u.Lvl2r2.fdateLastAccess, u.Lvl2r2.ftimeLastAccess);
+            printf("    Lvl2:  attrib=%#x size=%u alloc=%u cbList=%#x\n",
+                   u.Lvl2r2.attrFile, u.Lvl2r2.cbFile, u.Lvl2r2.cbFileAlloc, u.Lvl2r4.cbList);
+        }
+
+        memset(&u, 0x55, sizeof(u));
+        rc = DosQueryPathInfo(argv[i], FIL_QUERYEASIZE, &u.Lvl2r4, sizeof(u.Lvl2r4));
+        printf("%s: FIL_QUERYEASIZE/%#x -> %u\n", argv[i], sizeof(u.Lvl2r4), rc);
+        if (rc == NO_ERROR)
+        {
+            printf("    Lvl2: creation=%u:%u write=%u:%u access=%u:%u\n",
+                   u.Lvl2r4.fdateCreation, u.Lvl2r4.ftimeCreation,  u.Lvl2r4.fdateLastWrite, u.Lvl2r4.ftimeLastWrite,
+                   u.Lvl2r4.fdateLastAccess, u.Lvl2r4.ftimeLastAccess);
+            printf("    Lvl2:  attrib=%#x size=%u alloc=%u cbList=%#x\n",
+                   u.Lvl2r4.attrFile, u.Lvl2r4.cbFile, u.Lvl2r4.cbFileAlloc, u.Lvl2r4.cbList);
+        }
+
+        memset(&u, 0x99, sizeof(u));
+        rc = DosQueryPathInfo(argv[i], FIL_QUERYEASIZEL, &u.Lvl12, sizeof(u.Lvl12));
+        printf("%s: FIL_QUERYEASIZEL/%#x -> %u\n", argv[i], sizeof(u.Lvl12), rc);
+        if (rc == NO_ERROR)
+        {
+            printf("   Lvl12: creation=%u:%u write=%u:%u access=%u:%u\n",
+                   u.Lvl12.fdateCreation, u.Lvl12.ftimeCreation,  u.Lvl12.fdateLastWrite, u.Lvl12.ftimeLastWrite,
+                   u.Lvl12.fdateLastAccess, u.Lvl12.ftimeLastAccess);
+            printf("   Lvl12:  attrib=%#x size=%llu alloc=%llu cbList=%#x\n",
+                   u.Lvl12.attrFile, u.Lvl12.cbFile, u.Lvl12.cbFileAlloc, u.Lvl12.cbList);
+        }
+
+        memset(&u, 0x44, sizeof(u));
+        rc = DosQueryPathInfo(argv[i], FIL_QUERYFULLNAME, &u.szFullName[0], sizeof(u.szFullName));
+        printf("%s: FIL_QUERYFULLNAME -> %u\n", argv[i], rc);
+        if (rc == NO_ERROR)
+            printf("   Lvl5: %s<eol>\n", u.szFullName);
+
+        size_t cchInput = strlen(argv[i]);
+        if (cchInput >= sizeof(u.szFullName))
+            cchInput = sizeof(u.szFullName) - 1;
+
+        /** @todo Cannot get to level 6 thru 32-bit, need 16-bit. DOSCALL1.DLL
+         *        chickens out on us.  Sigh. */
+        memcpy(u.szFullName, argv[i], cchInput);
+        u.szFullName[0] = '\0';
+        rc = DosQueryPathInfo(argv[i], 6 /*FIL_VERIFY_SYNTAX*/, &u.szFullName[0], sizeof(u.szFullName));
+        printf("%s: FIL_VERIFY_SYNTAX -> %u\n", argv[i], rc);
+        if (rc == NO_ERROR)
+            printf("   Lvl6: %s<eol>\n", u.szFullName);
+
+        memcpy(u.szFullName, argv[i], cchInput);
+        u.szFullName[0] = '\0';
+        rc = DosQueryPathInfo(argv[i], 16 /*FIL_VERIFY_SYNTAX_L*/, &u.szFullName[0], sizeof(u.szFullName));
+        printf("%s: FIL_VERIFY_SYNTAX_L -> %u\n", argv[i], rc);
+        if (rc == NO_ERROR)
+            printf("  Lvl6L: %s<eol>\n", u.szFullName);
+
+        memcpy(u.szFullName, argv[i], cchInput);
+        u.szFullName[0] = '\0';
+        rc = DosQueryPathInfo(argv[i], 7 /*FIL_FIX_CASE*/, &u.szFullName[0], sizeof(u.szFullName));
+        printf("%s: FIL_FIX_CASE -> %u\n", argv[i], rc);
+        if (rc == NO_ERROR)
+            printf("   Lvl7: %s<eol>\n", u.szFullName);
+
+        memcpy(u.szFullName, argv[i], cchInput);
+        u.szFullName[0] = '\0';
+        rc = DosQueryPathInfo(argv[i], 17 /*FIL_FIX_CASE_L*/, &u.szFullName[0], sizeof(u.szFullName));
+        printf("%s: FIL_FIX_CASE_L -> %u\n", argv[i], rc);
+        if (rc == NO_ERROR)
+            printf("  Lvl17: %s<eol>\n", u.szFullName);
+
+        struct
+        {
+            ULONG cbList;
+            ULONG oNext;
+            BYTE  cchName;
+            char  szName[10];
+        } Gea2List, const Gea2ListOrg = { sizeof(Gea2List), 0, sizeof(".LONGNAME") - 1, ".LONGNAME" };
+        EAOP2 EaOp;
+        EaOp.fpGEA2List = (PGEA2LIST)memcpy(&Gea2List, &Gea2ListOrg, sizeof(Gea2List));
+        EaOp.fpFEA2List = &u.FeaList;
+        EaOp.oError     = 0;
+        memset(&u, '\0', sizeof(u));
+        u.FeaList.cbList = sizeof(u);
+        rc = DosQueryPathInfo(argv[i], FIL_QUERYEASFROMLIST, &EaOp, sizeof(EaOp));
+        printf("%s: FIL_QUERYEASFROMLIST -> %u\n", argv[i], rc);
+        if (rc == NO_ERROR)
+            printf("  Lvl3: FeaList.cbList=%#x oError=%#x\n", u.FeaList.cbList, EaOp.oError);
+
+        EaOp.fpGEA2List = (PGEA2LIST)memcpy(&Gea2List, &Gea2ListOrg, sizeof(Gea2List));
+        EaOp.fpFEA2List = &u.FeaList;
+        EaOp.oError     = 0;
+        memset(&u, '\0', sizeof(u));
+        u.FeaList.cbList = sizeof(u);
+        rc = DosQueryPathInfo(argv[i], FIL_QUERYEASFROMLISTL, &EaOp, sizeof(EaOp));
+        if (rc != ERROR_INVALID_LEVEL)
+            printf("%s: FIL_QUERYEASFROMLISTL -> %u\n", argv[i], rc);
+
+        EaOp.fpGEA2List = (PGEA2LIST)memcpy(&Gea2List, &Gea2ListOrg, sizeof(Gea2List));
+        EaOp.fpFEA2List = &u.FeaList;
+        EaOp.oError     = 0;
+        memset(&u, '\0', sizeof(u));
+        u.FeaList.cbList = sizeof(u);
+        rc = DosQueryPathInfo(argv[i], 4, &EaOp, sizeof(EaOp));
+        printf("%s: FIL_QUERYALLEAS/4 -> %u\n", argv[i], rc);
+        if (rc == NO_ERROR)
+            printf("  Lvl4: FeaList.cbList=%#x oError=%#x\n", u.FeaList.cbList, EaOp.oError);
+
+        EaOp.fpGEA2List = (PGEA2LIST)memcpy(&Gea2List, &Gea2ListOrg, sizeof(Gea2List));
+        EaOp.fpFEA2List = &u.FeaList;
+        EaOp.oError     = 0;
+        memset(&u, '\0', sizeof(u));
+        u.FeaList.cbList = sizeof(u);
+        rc = DosQueryPathInfo(argv[i], 14, &EaOp, sizeof(EaOp));
+        if (rc != ERROR_INVALID_LEVEL)
+            printf("%s: FIL_QUERYALLEASL/14 -> %u\n", argv[i], rc);
+
+        EaOp.fpGEA2List = (PGEA2LIST)memcpy(&Gea2List, &Gea2ListOrg, sizeof(Gea2List));
+        EaOp.fpFEA2List = &u.FeaList;
+        EaOp.oError     = 0;
+        memset(&u, '\0', sizeof(u));
+        u.FeaList.cbList = sizeof(u);
+        rc = DosQueryPathInfo(argv[i], 8, &EaOp, sizeof(EaOp));
+        printf("%s: FIL_QUERYALLEAS/8 -> %u\n", argv[i], rc);
+        if (rc == NO_ERROR)
+            printf("  Lvl8: FeaList.cbList=%#x oError=%#x\n", u.FeaList.cbList, EaOp.oError);
+
+        EaOp.fpGEA2List = (PGEA2LIST)memcpy(&Gea2List, &Gea2ListOrg, sizeof(Gea2List));
+        EaOp.fpFEA2List = &u.FeaList;
+        EaOp.oError     = 0;
+        memset(&u, '\0', sizeof(u));
+        u.FeaList.cbList = sizeof(u);
+        rc = DosQueryPathInfo(argv[i], 18, &EaOp, sizeof(EaOp));
+        if (rc != ERROR_INVALID_LEVEL)
+            printf("%s: FIL_QUERYALLEASL/18 -> %u\n", argv[i], rc);
+
+        EaOp.fpGEA2List = (PGEA2LIST)memcpy(&Gea2List, &Gea2ListOrg, sizeof(Gea2List));
+        EaOp.fpFEA2List = &u.FeaList;
+        EaOp.oError     = 0;
+        memset(&u, '\0', sizeof(u));
+        u.FeaList.cbList = sizeof(u);
+        rc = DosQueryPathInfo(argv[i], 15, &EaOp, sizeof(EaOp));
+        if (rc != ERROR_INVALID_LEVEL)
+            printf("%s: FIL_QUERYALLEASL/15 -> %u\n", argv[i], rc);
+
+        memset(&u, '\0', sizeof(u));
+        rc = DosQueryPathInfo(argv[i], 0, &u, sizeof(u));
+        if (rc != ERROR_INVALID_LEVEL)
+            printf("%s: 0 -> %u\n", argv[i], rc);
+    }
+    return 0;
+}
+
+
+int vboxSfOs2UtilFindFile(int argc, char **argv)
+{
+    unsigned    cMaxMatches = 1;
+    unsigned    cbBuf       = 1024;
+    unsigned    uLevel      = FIL_STANDARDL;
+    unsigned    fAttribs    = FILE_DIRECTORY | FILE_HIDDEN | FILE_SYSTEM;
+    bool        fOptions    = true;
+
+    uint8_t    *pbBuf = NULL;
+    for (int i = 1; i < argc; i++)
+    {
+        const char *pszArg = argv[i];
+
+        /*
+         * Deal with options.
+         */
+        if (fOptions && *pszArg == '-')
+        {
+            pszArg++;
+            if (*pszArg == '-')
+            {
+                pszArg++;
+                if (!*pszArg)
+                {
+                    fOptions = false;
+                    continue;
+                }
+                if (strcmp(pszArg, "attribs") == 0)
+                    pszArg = "a";
+                else if (strcmp(pszArg, "buffer-size") == 0)
+                    pszArg = "b";
+                else if (strcmp(pszArg, "level") == 0)
+                    pszArg = "l";
+                else if (strcmp(pszArg, "matches") == 0)
+                    pszArg = "m";
+                else if (strcmp(pszArg, "help") == 0)
+                    pszArg = "h";
+                else
+                {
+                    fprintf(stderr, "syntax error: Unknown option: %s\n", argv[i]);
+                    return 2;
+                }
+            }
+            do
+            {
+                const char *pszValue = NULL;
+                char chOpt = *pszArg++;
+                if (strchr("ablm", chOpt) != NULL)
+                {
+                    if (*pszArg != '\0')
+                        pszValue = *pszArg == ':' || *pszArg == '=' ? ++pszArg : pszArg;
+                    else if (i + 1 < argc)
+                        pszValue = argv[++i];
+                    else
+                    {
+                        fprintf(stderr, "syntax error: -%c takes a value\n", chOpt);
+                        return 2;
+                    }
+                    pszArg = "";
+                }
+                switch (chOpt)
+                {
+                    case 'a':
+                        fAttribs = atoi(pszValue);
+                        break;
+                    case 'b':
+                        cbBuf = atoi(pszValue);
+                        free(pbBuf);
+                        pbBuf = NULL;
+                        break;
+                    case 'l':
+                        uLevel = atoi(pszValue);
+                        break;
+                    case 'm':
+                        cMaxMatches = atoi(pszValue);
+                        break;
+                    case 'h':
+                        printf("usage: findfile [-a|--attribs <mask>] [-b|--buffer-size <bytes>]\n"
+                               "           [-l|--level <num>] [-m|--matches <num>] [--] <dir1> [dir2..N]\n");
+                        return 0;
+                    default:
+                        fprintf(stderr, "syntax error: Unknown option '%c' (%s)\n", chOpt, argv[i - (pszValue != NULL)]);
+                        return 2;
+                }
+
+            } while (pszArg && *pszArg != '\0');
+        }
+        else
+        {
+            /*
+             * Search the specified directory/whatever.
+             */
+            if (!pbBuf)
+            {
+                pbBuf = (uint8_t *)malloc(cbBuf);
+                if (!pbBuf)
+                {
+                    fprintf(stderr, "error: out of memory (cbBuf=%#x)\n", cbBuf);
+                    return 1;
+                }
+            }
+
+            HDIR  hDir     = HDIR_CREATE;
+            ULONG cMatches = cMaxMatches;
+            memset(pbBuf, 0xf6, cbBuf);
+            APIRET rc = DosFindFirst(pszArg, &hDir, fAttribs, pbBuf, cbBuf, &cMatches, uLevel);
+            printf("DosFindFirst -> %u hDir=%p cMatches=%#x\n", rc, hDir, cMatches);
+            if (rc == NO_ERROR)
+            {
+                do
+                {
+                    uint8_t *pbTmp = pbBuf;
+                    for (uint32_t iMatch = 0; iMatch < cMatches; iMatch++)
+                    {
+                        uint32_t offNext = *(uint32_t *)pbTmp;
+                        switch (uLevel)
+                        {
+                            case FIL_STANDARD:
+                            {
+                                PFILEFINDBUF3 pBuf = (PFILEFINDBUF3)pbTmp;
+                                printf("#%u: nx=%#x sz=%#x at=%#x nm=%#x:%s\n",
+                                       iMatch, pBuf->oNextEntryOffset, pBuf->cbFile, pBuf->attrFile, pBuf->cchName, pBuf->achName);
+                                if (strlen(pBuf->achName) != pBuf->cchName)
+                                    printf("Bad name length!\n");
+                                break;
+                            }
+                            case FIL_STANDARDL:
+                            {
+                                PFILEFINDBUF3L pBuf = (PFILEFINDBUF3L)pbTmp;
+                                printf("#%u: nx=%#x sz=%#llx at=%#x nm=%#x:%s\n",
+                                       iMatch, pBuf->oNextEntryOffset, pBuf->cbFile, pBuf->attrFile, pBuf->cchName, pBuf->achName);
+                                if (strlen(pBuf->achName) != pBuf->cchName)
+                                    printf("Bad name length!\n");
+                                break;
+                            }
+                            case FIL_QUERYEASIZE:
+                            {
+                                PFILEFINDBUF4 pBuf = (PFILEFINDBUF4)pbTmp;
+                                printf("#%u: nx=%#x sz=%#x at=%#x nm=%#x:%s\n",
+                                       iMatch, pBuf->oNextEntryOffset, pBuf->cbFile, pBuf->attrFile, pBuf->cchName, pBuf->achName);
+                                if (strlen(pBuf->achName) != pBuf->cchName)
+                                    printf("Bad name length!\n");
+                                break;
+                            }
+                            case FIL_QUERYEASIZEL:
+                            {
+                                PFILEFINDBUF4L pBuf = (PFILEFINDBUF4L)pbTmp;
+                                printf("#%u: nx=%#x sz=%#llx at=%#x nm=%#x:%s\n",
+                                       iMatch, pBuf->oNextEntryOffset, pBuf->cbFile, pBuf->attrFile, pBuf->cchName, pBuf->achName);
+                                if (strlen(pBuf->achName) != pBuf->cchName)
+                                    printf("Bad name length!\n");
+                                break;
+                            }
+
+                        }
+
+                        pbTmp += offNext;
+                    }
+
+                    /* Next bunch. */
+                    memset(pbBuf, 0xf6, cbBuf);
+                    cMatches = cMaxMatches;
+                    rc = DosFindNext(hDir, pbBuf, cbBuf, &cMatches);
+                    printf("DosFindNext -> %u hDir=%p cMatches=%#x\n", rc, hDir, cMatches);
+                } while (rc == NO_ERROR);
+
+                rc = DosFindClose(hDir);
+                printf("DosFindClose -> %u\n", rc);
+            }
+        }
+    }
+    return 0;
+}
+
+
+static int vboxSfOs2UtilMkDir(int argc, char **argv)
+{
+    for (int i = 1; i < argc; i++)
+    {
+        APIRET rc = DosCreateDir(argv[i], NULL);
+        printf("DosCreateDir -> %u for '%s'\n", rc, argv[i]);
+    }
+    return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+    /*
+     * Parse input.
+     */
+    for (int i = 1; i < argc; i++)
+    {
+        const char *pszArg = argv[i];
+        if (strcmp(pszArg, "use") == 0)
+            return vboxSfOs2UtilUse(argc - i, argv + i);
+        if (strcmp(pszArg, "qpathinfo") == 0)
+            return vboxSfOs2UtilQPathInfo(argc - i, argv + i);
+        if (strcmp(pszArg, "findfile") == 0)
+            return vboxSfOs2UtilFindFile(argc - i, argv + i);
+        if (strcmp(pszArg, "mkdir") == 0)
+            return vboxSfOs2UtilMkDir(argc - i, argv + i);
+
+        fprintf(stderr,  "Unknown command/option: %u\n", pszArg);
+        return 2;
+    }
+    fprintf(stderr,
+            "usage: VBoxSFUtil.exe use [drive] [shared-folder]\n"
+            "    or VBoxSFUtil.exe unuse [drive|shared-folder] [..]\n"
+            "    or VBoxSFUtil.exe list\n");
+    return 2;
+}
+
Index: /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFUtilA.asm
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFUtilA.asm	(revision 75337)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFUtilA.asm	(revision 75337)
@@ -0,0 +1,115 @@
+; $Id$
+;; @file
+; VBoxSF - OS/2 Shared Folders Utility, Assembly code for calling 16-bit APIs.
+;
+
+;
+; Copyright (c) 2007-2017 knut st. osmundsen <bird-src-spam@anduin.net>
+;
+; Permission is hereby granted, free of charge, to any person
+; obtaining a copy of this software and associated documentation
+; files (the "Software"), to deal in the Software without
+; restriction, including without limitation the rights to use,
+; copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the
+; Software is furnished to do so, subject to the following
+; conditions:
+;
+; The above copyright notice and this permission notice shall be
+; included in all copies or substantial portions of the Software.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+; OTHER DEALINGS IN THE SOFTWARE.
+;
+
+
+
+;*********************************************************************************************************************************
+;*  Header Files                                                                                                                 *
+;*********************************************************************************************************************************
+%define RT_INCL_16BIT_SEGMENTS
+%include "iprt/asmdefs.mac"
+
+
+;*********************************************************************************************************************************
+;*  External symbols                                                                                                             *
+;*********************************************************************************************************************************
+BEGINCODE16
+extern DOSQFILEMODE
+IMPORT DOSQFILEMODE DOSCALLS 75
+
+
+BEGINCODE
+BEGINPROC CallDosQFileMode
+        push    ebp
+        mov     ebp, esp
+
+        ;
+        ; Make a LSS frame to ease switching back the stack.
+        ;
+        push    ss
+        push    esp
+
+        ;
+        ; Create the PASCAL stackframe.
+        ;
+
+        ; Use tile algorithm to convert pointers.
+        mov     eax, [ebp + 08h]        ; PCSZ    pszPath
+        ror     eax, 16
+        shl     ax, 3
+        or      eax, 0007h
+        rol     eax, 16
+        push    eax
+
+        ; Use the tiled algorithm to convert flat to 16:16 pointer.
+        mov     ecx, [ebp + 0ch]        ; PUSHORT pfAttrib
+        ror     ecx, 16
+        shl     cx, 3
+        or      ecx, 0007h
+        rol     ecx, 16
+        push    ecx
+
+        mov     eax, [ebp + 10h]        ; ULONG   ulReserved
+        push    eax
+
+        ;
+        ; Convert the stack in the same manner.
+        ;
+        movzx   edx, sp
+        mov     eax, esp
+        shr     eax, 16
+        shl     eax, 3
+        or      eax, 0007h
+        push    eax
+        push    edx
+        lss     esp, [esp]
+
+        ;jmp far dword .thunked_to_16bit wrt CODE16
+        db      066h
+        db      0eah
+        dw      .thunked_to_16bit wrt CODE16
+        dw      CODE16
+BEGINCODE16
+.thunked_to_16bit:
+        call far DOSQFILEMODE
+
+        ;jmp far dword NAME(%i %+ _32) wrt FLAT
+        db      066h
+        db      0eah
+        dd      .thunked_back_to_32bit ;wrt FLAT
+        dw      TEXT32 wrt FLAT
+BEGINCODE
+.thunked_back_to_32bit:
+        lss     esp, [ds:ebp - 8]
+        movzx   eax, ax
+        leave
+        ret
+ENDPROC CallDosQFileMode
+
Index: /trunk/src/VBox/Additions/os2/VBoxSF/dotseg.cpp
===================================================================
--- /trunk/src/VBox/Additions/os2/VBoxSF/dotseg.cpp	(revision 75337)
+++ /trunk/src/VBox/Additions/os2/VBoxSF/dotseg.cpp	(revision 75337)
@@ -0,0 +1,148 @@
+/** $Id$ */
+/** @file
+ * VBoxSF - OS/2 Shared Folders, NASM Object File Editor for DWARF segments.
+ */
+
+/*
+ * Copyright (c) 2018 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#include <iprt/types.h>
+#include <iprt/formats/omf.h>
+
+#include <stdio.h>
+
+
+
+int main(int argc, char **argv)
+{
+    if (argc != 2)
+    {
+        fprintf(stderr, "syntax error! Expected exactly one argument, found %d!\n", argc - 1);
+        return 2;
+    }
+    const char *pszFilename = argv[1];
+
+    /*
+     * Open the file.
+     */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+    FILE *pFile = fopen(pszFilename, "r+b");
+#else
+    FILE *pFile = fopen(pszFilename, "r+b");
+#endif
+    if (!pFile)
+    {
+        fprintf(stderr,  "error opening '%s' for updating!\n", pszFilename);
+        return 1;
+    }
+
+    /*
+     * Parse the file.
+     */
+    uint32_t offRec = 0;
+    while (!feof(pFile))
+    {
+        OMFRECHDR Hdr;
+        if (fread(&Hdr, sizeof(Hdr), 1, pFile) != 1)
+            break;
+        /*fprintf(stderr, "dbg: %#07x: %02x %04x\n", offRec, Hdr.bType, Hdr.cbLen);*/
+        uint8_t abData[OMF_MAX_RECORD_LENGTH];
+        if (Hdr.cbLen > sizeof(abData))
+        {
+            fprintf(stderr, "%#07x: bad record: cbLen=%#x\n", offRec, Hdr.cbLen);
+            return 1;
+        }
+
+        /* Is it interesting? */
+        if (Hdr.bType == OMF_LNAMES)
+        {
+            /* Read the whole record. */
+            if (fread(abData, Hdr.cbLen, 1, pFile) != 1)
+                break;
+
+            /* Scan it and make updates. */
+            bool fUpdated = false;
+            for (unsigned offData = 0; offData + 1 < Hdr.cbLen; )
+            {
+                uint8_t cchName = abData[offData++];
+                if (offData + cchName + 1 > Hdr.cbLen)
+                {
+                    fprintf(stderr, "%#07x: bad LNAMES record (offData=3 + %#x)\n", offRec, offData);
+                    return 1;
+                }
+                if (   cchName > 5
+                    && abData[offData + 0] == '_'
+                    && abData[offData + 1] == 'd'
+                    && abData[offData + 2] == 'e'
+                    && abData[offData + 3] == 'b'
+                    && abData[offData + 4] == 'u'
+                    && abData[offData + 5] == 'g')
+                {
+                    abData[offData] = '.';
+                    fUpdated = true;
+                }
+                offData += cchName;
+            }
+
+            /* Write out updates. */
+            if (fUpdated)
+            {
+                abData[Hdr.cbLen - 1] = 0; /* squash crc */
+                if (   fseek(pFile, offRec + 3, SEEK_SET) != 0
+                    || fwrite(abData, Hdr.cbLen, 1, pFile) != 1
+                    || fseek(pFile, offRec + 3 + Hdr.cbLen, SEEK_SET) != 0)
+                {
+                    fprintf(stderr, "%#07x: error writing %#x bytes\n", offRec, Hdr.cbLen);
+                    return 1;
+                }
+            }
+        }
+        /* Not interesting, so skip it and the CRC. */
+        else if (fseek(pFile, Hdr.cbLen, SEEK_CUR) != 0)
+        {
+            fprintf(stderr, "%#07x: error skipping %#x bytes\n", offRec, Hdr.cbLen);
+            return 1;
+        }
+        offRec += 3 + Hdr.cbLen;
+    }
+
+    if (ferror(pFile))
+    {
+        fprintf(stderr,  "read error\n");
+        return 1;
+    }
+    if (fclose(pFile) != 0)
+    {
+        fprintf(stderr,  "error flush/closing file\n");
+        return 1;
+    }
+    return 0;
+}
+
