Index: trunk/src/VBox/Additions/os2/VBoxSF/Makefile.kmk
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/Makefile.kmk	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxSF/Makefile.kmk	(revision 3655)
@@ -0,0 +1,36 @@
+# $Id$
+## @file
+# Makefile for the OS/2 Guest Additions Shared Folder FSD.
+#
+
+DEPTH = ../../../../..
+include	$(PATH_KBUILD)/header.kmk
+
+#?DLLS += VBoxSF
+SYSMODS += VBoxSF
+VBoxSF_TEMPLATE      = VBOXOS2GUESTR0
+VBoxSF_SYSSUFF       = .ifs
+VBoxSF_DLLSUFF       = .ifs
+VBoxSF_DEFS          = VBOX_HGCM
+VBoxSF_INCS         := \
+	$(PATH_SUB_CURRENT) \
+	$(PATH_ROOT)/src/VBox/Runtime/include
+VBoxSF_LDFLAGS      := $(addprefix -Zlinker , \
+	SEG TYPE CODE PRELOAD DYNAMIC SHARED NONPERMANENT, \
+	    TYPE DATA PRELOAD DYNAMIC)
+#VBoxSF_LDFLAGS      += -s -t -v
+VBoxSF_SOURCES       = \
+	VBoxSFA.asm \
+	VBoxSF.cpp \
+	VBoxSFFile.cpp \
+	VBoxSFFind.cpp \
+	VBoxSFInit.cpp \
+	VBoxSF.def
+VBoxSF_LIBS          = \
+	$(VBOX_LIB_VBGL_OS2_R0) \
+	$(VBOX_LIB_IPRT_OS2_GUEST_R0) \
+	$(VBOX_GCC_LIBGCC) \
+	end
+
+include	$(PATH_KBUILD)/footer.kmk
+
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp	(revision 3655)
@@ -0,0 +1,158 @@
+/** $Id$ */
+/** @file
+ * VBoxSF - OS/2 Shared Folders, the FS and FSD level IFS EPs
+ */
+
+/*
+ * Copyright (c) 2007 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 LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxSFInternal.h"
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+
+
+
+DECLASM(void)
+FS32_EXIT(ULONG uid, ULONG pid, ULONG pdb)
+{
+}
+
+
+DECLASM(int)
+FS32_SHUTDOWN(ULONG type, ULONG reserved)
+{
+    return NO_ERROR;
+}
+
+
+DECLASM(int)
+FS32_ATTACH(ULONG flag, PCSZ pszDev, PVBOXSFVP pvpfsd, PVBOXSFCD pcdfsd, PBYTE pszParm, PUSHORT pcbParm)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FLUSHBUF(USHORT hVPB, ULONG flag)
+{
+    return NO_ERROR;
+}
+
+
+DECLASM(int)
+FS32_FSINFO(ULONG flag, USHORT hVPB, PBYTE pbData, USHORT cbData, ULONG level)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FSCTL(union argdat *pArgdat, ULONG iArgType, ULONG func,
+           PVOID pParm, USHORT lenParm, PUSHORT plenParmIO,
+           PVOID pData, USHORT lenData, PUSHORT plenDataIO)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_PROCESSNAME(PSZ pszName)
+{
+    return NO_ERROR;
+}
+
+
+DECLASM(int)
+FS32_CHDIR(ULONG flag, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszDir, USHORT iCurDirEnd)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_MKDIR(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd,
+           PBYTE pEABuf, ULONG flag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_RMDIR(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_COPY(USHORT flag, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszSrc, USHORT iSrcCurDirEnd,
+          PCSZ pszDst, USHORT iDstCurDirEnd, USHORT nameType)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_MOVE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszSrc, USHORT iSrcCurDirEnd,
+          PCSZ pszDst, USHORT iDstCurDirEnd, USHORT type)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_DELETE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszFile, USHORT iCurDirEnd)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FILEATTRIBUTE(ULONG flag, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, PUSHORT pAttr)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_PATHINFO(USHORT flag, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnt,
+              USHORT level, PBYTE pData, USHORT cbData)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_MOUNT(USHORT flag, PVPFSI pvpfsi, PVBOXSFVP pvpfsd, USHORT hVPB, PCSZ pszBoot)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.def
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.def	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.def	(revision 3655)
@@ -0,0 +1,69 @@
+;; @file
+; VBoxSF - OS/2 definition file.
+;
+
+LIBRARY VBoxSF
+DESCRIPTION 'VirtualBox.org Guest Additions Shared Folder FSD for OS/2.'
+DATA PRELOAD SINGLE SHARED MOVABLE
+CODE PRELOAD MOVABLE
+EXPORTS
+    ; Data exports
+    FS_ATTRIBUTE
+    FS_NAME
+    FS_MPSAFEFLAGS2
+    FS32_ATTRIBUTE
+    
+    ; 16-bit entry points.
+    FS_ALLOCATEPAGESPACE
+    FS_ATTACH
+    FS_CANCELLOCKREQUEST
+    FS_CANCELLOCKREQUESTL
+    FS_CHDIR
+    FS_CHGFILEPTR
+    FS_CLOSE
+    FS_COMMIT
+    FS_COPY
+    FS_DELETE
+    FS_DOPAGEIO
+    FS_EXIT
+    FS_FILEATTRIBUTE
+    FS_FILEINFO
+    FS_FILEIO
+    FS_FILELOCKS
+    FS_FILELOCKSL
+    FS_FINDCLOSE
+    FS_FINDFIRST
+    FS_FINDFROMNAME
+    FS_FINDNEXT
+    FS_FINDNOTIFYCLOSE
+    FS_FINDNOTIFYFIRST
+    FS_FINDNOTIFYNEXT
+    FS_FLUSHBUF
+    FS_FSCTL
+    FS_FSINFO
+    FS_INIT
+    FS_IOCTL
+    FS_MKDIR
+    FS_MOUNT
+    FS_MOVE
+    FS_NEWSIZE
+    FS_NEWSIZEL
+    FS_NMPIPE
+    FS_OPENCREATE
+    FS_OPENPAGEFILE
+    FS_PATHINFO
+    FS_PROCESSNAME
+    FS_READ
+    FS_RMDIR
+    FS_SETSWAP
+    FS_SHUTDOWN
+    FS_WRITE
+
+    ; 32-bit entry points.
+    FS32_CHGFILEPTR
+    FS32_CHGFILEPTRL
+    FS32_READ
+    FS32_READFILEATCACHE
+    FS32_RETURNFILECACHE
+    FS32_WRITE
+
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFA.asm
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFA.asm	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFA.asm	(revision 3655)
@@ -0,0 +1,1577 @@
+; $Id$
+;; @file
+; VBoxSF - OS/2 Shared Folders, all assembly code (16 -> 32 thunking mostly).
+;
+
+;
+; Copyright (c) 2007 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"
+%include "iprt/err.mac"
+%include "VBox/VBoxGuest.mac"
+
+
+;*******************************************************************************
+;*  Defined Constants And Macros                                               *
+;*******************************************************************************
+%define ERROR_NOT_SUPPORTED         50
+%define ERROR_INVALID_PARAMETER     87
+%define DevHlp_AttachDD             2ah
+
+;;
+; Prints a string to the VBox log port.
+%macro DEBUG_STR16 1
+%ifdef DEBUG
+segment DATA16
+%%my_dbg_str: db %1, 0ah, 0
+segment CODE16
+    push    ax
+    mov     ax, %%my_dbg_str
+    call    NAME(dbgstr16)
+    pop     ax
+%endif
+%endmacro
+
+
+%macro VBOXSF_EP16_BEGIN 2
+global %1
+%1:
+    DEBUG_STR16 {'VBoxSF: ', %2}
+
+%endmacro
+
+%macro VBOXSF_EP16_END 1
+global %1_EndProc
+%1_EndProc:
+%endmacro
+
+;;
+; Used to taking us to 32-bit and reserving a parameter frame.
+;
+; @param    %1      The function name
+; @param    %2      The number of bytes to reserve
+;
+%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
+    ; boundrary 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
+
+;;
+; The counter part to VBOXSF_TO_32
+;
+; @param    %1      The function name
+;
+%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
+segment CODE16
+GLOBALNAME %1 %+ _16
+
+    ; Epilogue
+    lea     sp, [bp - 4h]
+    pop     es
+    pop     ds
+    mov     esp, ebp
+    pop     ebp
+%endmacro
+
+;;
+; Thunks the given 16:16 pointer to a flat pointer.
+;
+; @param    %1      The negated 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
+%endmacro
+
+;;
+; Thunks the given 16:16 struct sffsd pointer to a flat pointer.
+;
+; @param    %1      The negated 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
+%endmacro
+
+
+;;
+; Thunks the given 16:16 struct cdfsd pointer to a flat pointer.
+;
+; @param    %1      The negated 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
+%endmacro
+
+;;
+; Thunks the given 16:16 struct fsfsd pointer to a flat pointer.
+;
+; @param    %1      The negated 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
+%endmacro
+
+
+
+;*******************************************************************************
+;* External Symbols                                                            *
+;*******************************************************************************
+segment CODE32
+extern KernThunkStackTo32
+extern KernThunkStackTo16
+extern KernSelToFlat
+segment CODE16
+extern FSH_FORCENOSWAP
+extern DOS16WRITE
+
+segment CODE32
+extern NAME(FS32_ALLOCATEPAGESPACE)
+extern NAME(FS32_ATTACH)
+extern NAME(FS32_CANCELLOCKREQUEST)
+extern NAME(FS32_CANCELLOCKREQUESTL)
+extern NAME(FS32_CHDIR)
+extern FS32_CHGFILEPTRL
+extern NAME(FS32_CLOSE)
+extern NAME(FS32_COMMIT)
+extern NAME(FS32_COPY)
+extern NAME(FS32_DELETE)
+extern NAME(FS32_DOPAGEIO)
+extern NAME(FS32_EXIT)
+extern NAME(FS32_FILEATTRIBUTE)
+extern NAME(FS32_FILEINFO)
+extern NAME(FS32_FILEIO)
+extern NAME(FS32_FILELOCKS)
+extern NAME(FS32_FILELOCKSL)
+extern NAME(FS32_FINDCLOSE)
+extern NAME(FS32_FINDFIRST)
+extern NAME(FS32_FINDFROMNAME)
+extern NAME(FS32_FINDNEXT)
+extern NAME(FS32_FINDNOTIFYCLOSE)
+extern NAME(FS32_FINDNOTIFYFIRST)
+extern NAME(FS32_FINDNOTIFYNEXT)
+extern NAME(FS32_FLUSHBUF)
+extern NAME(FS32_FSCTL)
+extern NAME(FS32_FSINFO)
+extern NAME(FS32_IOCTL)
+extern NAME(FS32_MKDIR)
+extern NAME(FS32_MOUNT)
+extern NAME(FS32_MOVE)
+extern NAME(FS32_NEWSIZEL)
+extern NAME(FS32_NMPIPE)
+extern NAME(FS32_OPENCREATE)
+extern NAME(FS32_OPENPAGEFILE)
+extern NAME(FS32_PATHINFO)
+extern NAME(FS32_PROCESSNAME)
+extern FS32_READ
+extern NAME(FS32_RMDIR)
+extern NAME(FS32_SETSWAP)
+extern NAME(FS32_SHUTDOWN)
+extern FS32_WRITE
+
+extern NAME(VBoxSFR0Init)
+
+
+
+;*******************************************************************************
+;*  Global Variables                                                           *
+;*******************************************************************************
+segment DATA16
+
+;;
+; The file system name.
+global FS_NAME
+FS_NAME:
+    db 'VBOXSF',0
+
+;;
+; File system attributes
+; The 32-bit version is only used to indicate that this is a 32-bit file system.
+;
+%define FSA_REMOTE      0001h           ; remote file system.
+%define FSA_UNC         0002h           ; implements UNC.
+%define FSA_LOCK        0004h           ; needs lock notification.
+%define FSA_LVL7        0008h           ; accept level 7 (case perserving path request).
+%define FSA_PSVR        0010h           ; (named) pipe server.
+%define FSA_LARGEFILE   0020h           ; large file support.
+align 16
+global FS_ATTRIBUTE
+global FS32_ATTRIBUTE
+FS_ATTRIBUTE:
+FS32_ATTRIBUTE:
+    dd FSA_REMOTE + FSA_LARGEFILE ;+ FSA_LVL7 + FSA_LOCK
+
+;; 64-bit mask.
+; bit 0 - don't get the ring-0 spinlock.
+; bit 6 - don't get the subsystem ring-0 spinlock.
+global FS_MPSAFEFLAGS2
+FS_MPSAFEFLAGS2:
+    dd  0
+    dd  0
+
+;;
+; Set after VBoxSFR0Init16Bit has been called.
+GLOBALNAME g_fDoneRing0
+    db 0
+
+align 4
+;;
+; The device helper (IPRT expects this name).
+; (This is set by FS_INIT.)
+GLOBALNAME g_fpfnDevHlp
+    dd 0
+
+;;
+; Whether initialization should be verbose or quiet.
+GLOBALNAME g_fVerbose
+    db 1
+
+;; DEBUGGING DEBUGGING
+GLOBALNAME g_u32Info
+    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
+
+;;
+; The attach dd data.
+GLOBALNAME g_VBoxGuestAttachDD
+    dd 0
+    dw 0
+    dd 0
+    dw 0
+;;
+; The AttachDD name of the VBoxGuest.sys driver.
+GLOBALNAME g_szVBoxGuestName
+    db VBOXGUEST_DEVICE_NAME, 0
+;;
+; The VBoxGuest IDC connection data.
+GLOBALNAME g_VBoxGuestIDC
+    times VBGOS2IDC_size db 0
+
+;;
+; This must be present, we've got fixups against it.
+segment DATA32
+g_pfnDos16Write:
+    dd  DOS16WRITE  ; flat
+
+
+
+
+
+
+
+;
+;
+;  16-bit entry point thunking.
+;  16-bit entry point thunking.
+;  16-bit entry point thunking.
+;
+;
+segment CODE16
+
+
+;;
+; @cproto int FS_ALLOCATEPAGESPACE(PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG cb, USHORT cbWantContig)
+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)
+VBOXSF_TO_16        FS_ALLOCATEPAGESPACE
+    retf    0eh
+VBOXSF_EP16_END     FS_ALLOCATEPAGESPACE
+
+;;
+; @cproto int FS_ATTACH(USHORT flag, PCSZ pszDev, PVPFSD pvpfsd, PCDFSD pcdfsd, PBYTE pszParm, PUSHORT pcbParm)
+;
+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)
+.DoneRing0:
+    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_TO_16        FS_ATTACH
+    retf    16h
+VBOXSF_EP16_END     FS_ATTACH
+
+
+;;
+; @cproto int FS_CANCELLOCKREQUEST(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelock far *pLockRange)
+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_TO_16        FS_CANCELLOCKREQUEST
+    retf    0ch
+VBOXSF_EP16_END     FS_CANCELLOCKREQUEST
+
+
+;;
+; @cproto int FS_CANCELLOCKREQUESTL(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelockl far *pLockRange)
+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_TO_16        FS_CANCELLOCKREQUESTL
+    retf    0ch
+VBOXSF_EP16_END     FS_CANCELLOCKREQUESTL
+
+
+;;
+; @cproto int FS_CHDIR(USHORT flag, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszDir, USHORT iCurDirEnd)
+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)
+VBOXSF_TO_16        FS_CHDIR
+    retf    10h
+VBOXSF_EP16_END     FS_CHDIR
+
+
+; @cproto int FS_CHGFILEPTR(PSFFSI psffsi, PVBOXSFFSD psffsd, LONG off, USHORT usMethod, USHORT IOflag)
+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
+VBOXSF_TO_16        FS_CHGFILEPTR
+    retf    10h
+VBOXSF_EP16_END     FS_CHGFILEPTR
+
+
+;;
+; @cproto int FS_CLOSE(USHORT type, USHORT IOflag, PSFFSI psffsi, PVBOXSFFSD psffsd)
+;
+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_TO_16        FS_CLOSE
+    retf    0ch
+VBOXSF_EP16_END     FS_CLOSE
+
+
+;;
+; @cproto int FS_COMMIT(USHORT type, USHORT IOflag, PSFFSI psffsi, PVBOXSFFSD psffsd)
+;
+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_TO_16        FS_COMMIT
+    retf    0ch
+VBOXSF_EP16_END     FS_COMMIT
+
+;;
+; @cproto int FS_COPY(USHORT flag, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszSrc, USHORT iSrcCurDirEnd
+;                     PCSZ pszDst, USHORT iDstCurDirEnd, USHORT nameType);
+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)
+VBOXSF_TO_16        FS_COPY
+    retf    18h
+VBOXSF_EP16_END     FS_COPY
+
+
+;;
+; @cproto int FS_DELETE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszFile, USHORT iCurDirEnd);
+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)
+VBOXSF_TO_16        FS_DELETE
+    retf    0eh
+VBOXSF_EP16_END FS_DELETE
+
+
+;;
+; @cproto int FS_DOPAGEIO(PSFFSI psffsi, PVBOXSFFSD psffsd, struct PageCmdHeader far *pList)
+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_TO_16        FS_DOPAGEIO
+    retf    0ch
+VBOXSF_EP16_END     FS_DOPAGEIO
+
+;;
+; @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)
+.DoneRing0:
+    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)
+VBOXSF_TO_16        FS_EXIT
+    retf    6h
+VBOXSF_EP16_END     FS_EXIT
+
+
+;;
+; @cproto int FS_FILEATTRIBUTE(USHORT flag, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, PUSHORT pAttr);
+;
+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_TO_16        FS_FILEATTRIBUTE
+    retf    14h
+VBOXSF_EP16_END     FS_FILEATTRIBUTE
+
+
+;;
+; @cproto  int FS_FILEINFO(USHORT flag, PSFFSI psffsi, PVBOXSFFSD psffsd, USHORT level,
+;                          PBYTE pData, USHORT cbData, USHORT IOflag);
+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)
+VBOXSF_TO_16        FS_FILEINFO
+    retf    14h
+VBOXSF_EP16_END     FS_FILEINFO
+
+
+;;
+; @cproto  int FS_FILEIO(PSFFSI psffsi, PVBOXSFFSD psffsd, PBYTE pCmdList, USHORT cbCmdList,
+;                        PUSHORT poError, USHORT IOflag);
+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)
+VBOXSF_TO_16        FS_FILEIO
+    retf    14h
+VBOXSF_EP16_END     FS_FILEIO
+
+
+;;
+; @cproto  int FS_FILELOCKS(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelock far *pUnLockRange
+;                           struct filelock far *pLockRange, ULONG timeout, ULONG flags)
+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)
+VBOXSF_TO_16        FS_FILELOCKS
+    retf    18h
+VBOXSF_EP16_END     FS_FILELOCKS
+
+
+;;
+; @cproto  int FS_FILELOCKSL(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelockl far *pUnLockRange
+;                            struct filelockl far *pLockRange, ULONG timeout, ULONG flags)
+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)
+VBOXSF_TO_16        FS_FILELOCKSL
+    retf    18h
+VBOXSF_EP16_END     FS_FILELOCKSL
+
+
+;;
+; @cproto int FS_FINDCLOSE(PFSFSI pfsfsi, PVBOXSFFS pfsfsd);
+;
+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_TO_16        FS_FINDCLOSE
+    retf    8h
+VBOXSF_EP16_END     FS_FINDCLOSE
+
+
+;;
+; @cproto int FS_FINDFIRST(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, USHORT attr,
+;                          PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+;                          USHORT level, USHORT flags);
+;
+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)
+VBOXSF_TO_16        FS_FINDFIRST
+    retf    26h
+VBOXSF_EP16_END FS_FINDFIRST
+
+
+;;
+; @cproto int FS_FINDFROMNAME(PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+;                             USHORT level, ULONG position, PCSZ pszName, USHORT flag)
+;
+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)
+VBOXSF_TO_16        FS_FINDFROMNAME
+    retf    1eh
+VBOXSF_EP16_END     FS_FINDFROMNAME
+
+
+;;
+; @cproto int FS_FINDNEXT(PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+;                         USHORT level, USHORT flag)
+;
+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)
+VBOXSF_TO_16        FS_FINDNEXT
+    retf    16h
+VBOXSF_EP16_END     FS_FINDNEXT
+
+
+;;
+; @cproto int FS_FINDNOTIFYCLOSE(USHORT handle);
+;
+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)
+VBOXSF_TO_16        FS_FINDNOTIFYCLOSE
+    retf    2h
+VBOXSF_EP16_END     FS_FINDNOTIFYCLOSE
+
+
+;;
+; @cproto int FS_FINDNOTIFYFIRST(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, USHORT attr,
+;                                PUSHORT pHandle, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+;                                USHORT level, USHORT flags);
+;
+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)
+VBOXSF_TO_16        FS_FINDNOTIFYFIRST
+    retf    22h
+VBOXSF_EP16_END     FS_FINDNOTIFYFIRST
+
+
+;;
+; @cproto int FS_FINDNOTIFYNEXT(USHORT handle, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+;                               USHORT level, ULONG timeout)
+;
+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)
+VBOXSF_TO_16        FS_FINDNOTIFYNEXT
+    retf    12h
+VBOXSF_EP16_END     FS_FINDNOTIFYNEXT
+
+
+;; @cproto int FS_FLUSHBUF(USHORT hVPB, USHORT flag);
+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)
+VBOXSF_TO_16        FS_FLUSHBUF
+    retf    4h
+VBOXSF_EP16_END FS_FLUSHBUF
+
+
+;; @cproto int FS_FSCTL(union argdat far *pArgdat, USHORT iArgType, USHORT func,
+;                       PVOID pParm, USHORT lenParm, PUSHORT plenParmIO,
+;                       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)
+.DoneRing0:
+    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_TO_16        FS_FSCTL
+    retf    1ch
+VBOXSF_EP16_END FS_FSCTL
+
+
+;; @cproto int FS_FSINFO(USHORT flag, USHORT hVPB, PBYTE pbData, USHORT cbData, USHORT level)
+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)
+VBOXSF_TO_16        FS_FSINFO
+    retf    14h
+VBOXSF_EP16_END     FS_FSINFO
+
+
+;;
+; @cproto int FS_IOCTL(PSFFSI psffsi, PVBOXSFFSD psffsd, USHORT cat, USHORT func,
+;                      PVOID pParm, USHORT lenParm, PUSHORT plenParmIO,
+;                      PVOID pData, USHORT lenData, PUSHORT plenDataIO);
+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_TO_16        FS_IOCTL
+    retf    20h
+VBOXSF_EP16_END     FS_IOCTL
+
+
+;;
+; @cproto int FS_MKDIR(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd,
+;                      PBYTE pEABuf, USHORT flag);
+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)
+VBOXSF_TO_16        FS_MKDIR
+    retf    14h
+VBOXSF_EP16_END     FS_MKDIR
+
+
+;;
+; @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)
+.DoneRing0:
+    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_TO_16        FS_MOUNT
+    retf    10h
+VBOXSF_EP16_END     FS_MOUNT
+
+
+;;
+; @cproto int FS_MOVE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszSrc, USHORT iSrcCurDirEnd
+;                     PCSZ pszDst, USHORT iDstCurDirEnd, USHORT type)
+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)
+VBOXSF_TO_16        FS_MOVE
+    retf    16h
+VBOXSF_EP16_END     FS_MOVE
+
+
+;;
+; @cproto int FS_NEWSIZE(PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG cbFile, USHORT IOflag);
+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)
+VBOXSF_TO_16        FS_NEWSIZE
+    retf    0eh
+VBOXSF_EP16_END     FS_NEWSIZE
+
+
+;;
+; @cproto int FS_NEWSIZEL(PSFFSI psffsi, PVBOXSFFSD psffsd, LONGLONG cbFile, USHORT IOflag);
+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)
+VBOXSF_TO_16        FS_NEWSIZEL
+    retf    12h
+VBOXSF_EP16_END FS_NEWSIZEL
+
+
+;;
+; @cproto int FS_NMPIPE(PSFFSI psffsi, PVBOXSFFSD psffsd, USHORT OpType, union npoper far *pOpRec,
+;                       PBYTE pData, PCSZ pszName);
+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_TO_16        FS_NMPIPE
+    retf    16h
+VBOXSF_EP16_END     FS_NMPIPE
+
+
+;;
+; @cproto int FS_OPENCREATE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd,
+;                           PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG ulOpenMode, USHORT usOpenFlag,
+;                           PUSHORT pusAction, USHORT usAttr, PBYTE pcEABuf, PUSHORT pfgenflag);
+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_TO_16        FS_OPENCREATE
+    retf    42
+VBOXSF_EP16_END FS_OPENCREATE
+
+
+;;
+; @cproto int FS_OPENPAGEFILE(PULONG pFlag, PULONG pcMaxReq, PCSZ pszName, PSFFSI psffsi, PVBOXSFFSD psffsd,
+;                             USHORT ulOpenMode, USHORT usOpenFlag, USHORT usAttr, ULONG Reserved)
+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)
+VBOXSF_TO_16        FS_OPENPAGEFILE
+    retf    1eh
+VBOXSF_EP16_END     FS_OPENPAGEFILE
+
+
+;;
+; @cproto int FS_PATHINFO(USHORT flag, PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnt,
+;                         USHORT level, PBYTE pData, USHORT cbData);
+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)
+VBOXSF_TO_16        FS_PATHINFO
+    retf    18h
+VBOXSF_EP16_END     FS_PATHINFO
+
+
+;; @cproto int FS_PROCESSNAME(PSZ pszName);
+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_TO_16        FS_PROCESSNAME
+    retf    4h
+VBOXSF_EP16_END FS_PROCESSNAME
+
+
+;;
+; @cproto int FS_READ(PSFFSI psffsi, PVBOXSFFSD psffsd, PBYTE pbData, PUSHORT pcbData, USHORT IOflag)
+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
+
+VBOXSF_TO_16        FS_READ
+
+    pop     es
+    retf    12h
+VBOXSF_EP16_END     FS_READ
+
+
+;;
+; @cproto int FS_RMDIR(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd);
+;
+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)
+VBOXSF_TO_16        FS_RMDIR
+    retf    14h
+VBOXSF_EP16_END     FS_RMDIR
+
+
+;;
+; @cproto int FS_SETSWAP(PSFFSI psffsi, PVBOXSFFSD psffsd);
+;
+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_TO_16        FS_SETSWAP
+    retf    8h
+VBOXSF_EP16_END     FS_SETSWAP
+
+
+;;
+; @cproto int FS_SHUTDOWN(USHORT type, ULONG reserved);
+;
+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)
+VBOXSF_TO_16        FS_SHUTDOWN
+    retf    6h
+VBOXSF_EP16_END     FS_SHUTDOWN
+
+
+;;
+; @cproto int FS_WRITE(PSFFSI psffsi, PVBOXSFFSD psffsd, PBYTE pbData, PUSHORT pcbData, USHORT IOflag)
+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
+
+VBOXSF_TO_16        FS_WRITE
+
+    pop     es
+    retf    12h
+VBOXSF_EP16_END     FS_WRITE
+
+
+
+
+
+
+;
+;
+;  Init code starts here
+;  Init code starts here
+;  Init code starts here
+;
+;
+
+
+;;
+; Ring-3 Init (16-bit).
+;
+; @param    pMiniFS         [bp + 08h]      The mini-FSD. (NULL)
+; @param    fpfnDevHlp      [bp + 0ch]      The address of the DevHlp router.
+; @param    pszCmdLine      [bp + 10h]      The config.sys command line.
+;
+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.
+.parse_next:
+
+    ; 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.
+.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
+
+.parse_verbose:
+    mov     byte [es:NAME(g_fVerbose)], 1
+    jmp     .parse_next
+
+.parse_quiet:
+    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
+.szNewLine:
+    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
+
+.parse_done:
+    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
+segment DATA16
+.szMessage:
+    db 'VirtualBox.org Guest Additions IFS for OS/2', 0dh, 0ah, 0
+segment CODE16
+    mov     bx, .szMessage
+    call    NAME(FS_INIT_FPUTS)
+.quiet:
+
+    ; 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
+VBOXSF_EP16_END FS_INIT
+
+
+;;
+; Dos16Write wrapper.
+;
+; @param    es:bx       String to print. (zero terminated)
+; @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
+%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
+%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)]
+%endif
+
+    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
+
+
+
+;;
+; 16-bit ring-0 init routine.
+;
+; Called from various entrypoints likely to be the first to be invoked.
+;
+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]
+.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
+%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
+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
+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
+ENDPROC VBoxSFR0Init16Bit
+
+
+%ifdef DEBUG
+;;
+; print the string which offset is in AX (it's in the data segment).
+; @uses AX
+;
+GLOBALNAME dbgstr16
+    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
+
+.done:
+    pop     edx
+    pop     ebx
+    pop     ds
+    ret
+ENDPROC dbgstr16
+%endif
+
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp	(revision 3655)
@@ -0,0 +1,206 @@
+/** $Id$ */
+/** @file
+ * VBoxSF - OS/2 Shared Folders, the file level IFS EPs.
+ */
+
+/*
+ * Copyright (c) 2007 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 LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxSFInternal.h"
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+
+
+
+DECLASM(int)
+FS32_OPENCREATE(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd,
+                PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG ulOpenMode, USHORT usOpenFlag,
+                PUSHORT pusAction, USHORT usAttr, PBYTE pcEABuf, PUSHORT pfgenflag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_CLOSE(ULONG type, ULONG IOflag, PSFFSI psffsi, PVBOXSFFSD psffsd)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_COMMIT(ULONG type, ULONG IOflag, PSFFSI psffsi, PVBOXSFFSD psffsd)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+extern "C" APIRET APIENTRY
+FS32_CHGFILEPTRL(PSFFSI psffsi, PVBOXSFFSD psffsd, LONGLONG off, ULONG ulMethod, ULONG IOflag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+/** Forwards the call to FS32_CHGFILEPTRL. */
+extern "C" APIRET APIENTRY
+FS32_CHGFILEPTR(PSFFSI psffsi, PVBOXSFFSD psffsd, LONG off, ULONG ulMethod, ULONG IOflag)
+{
+    return FS32_CHGFILEPTRL(psffsi, psffsd, off, ulMethod, IOflag);
+}
+
+DECLASM(int)
+FS32_FILEINFO(ULONG flag, PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG level,
+              PBYTE pData, ULONG cbData, ULONG IOflag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+DECLASM(int)
+FS32_NEWSIZEL(PSFFSI psffsi, PVBOXSFFSD psffsd, LONGLONG cbFile, ULONG IOflag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+extern "C" APIRET APIENTRY
+FS32_READ(PSFFSI psffsi, PVBOXSFFSD psffsd, PVOID pvData, PULONG pcb, ULONG IOflag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+extern "C" APIRET APIENTRY
+FS32_WRITE(PSFFSI psffsi, PVBOXSFFSD psffsd, PVOID pvData, PULONG pcb, ULONG IOflag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+extern "C" APIRET APIENTRY
+FS32_READFILEATCACHE(PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG IOflag, LONGLONG off, ULONG pcb, KernCacheList_t **ppCacheList)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+extern "C" APIRET APIENTRY
+FS32_RETURNFILECACHE(KernCacheList_t *pCacheList)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+/* oddments */
+
+DECLASM(int)
+FS32_CANCELLOCKREQUESTL(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelockl *pLockRange)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_CANCELLOCKREQUEST(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelock *pLockRange)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FILELOCKSL(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelockl *pUnLockRange,
+                struct filelockl *pLockRange, ULONG timeout, ULONG flags)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FILELOCKS(PSFFSI psffsi, PVBOXSFFSD psffsd, struct filelock *pUnLockRange,
+               struct filelock *pLockRange, ULONG timeout, ULONG flags)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_IOCTL(PSFFSI psffsi, PVBOXSFFSD psffsd, USHORT cat, USHORT func,
+           PVOID pParm, USHORT lenParm, PUSHORT plenParmIO,
+           PVOID pData, USHORT lenData, PUSHORT plenDataIO)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FILEIO(PSFFSI psffsi, PVBOXSFFSD psffsd, PBYTE pCmdList, USHORT cbCmdList,
+            PUSHORT poError, USHORT IOflag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_NMPIPE(PSFFSI psffsi, PVBOXSFFSD psffsd, USHORT OpType, union npoper *pOpRec,
+            PBYTE pData, PCSZ pszName)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_OPENPAGEFILE(PULONG pFlag, PULONG pcMaxReq, PCSZ pszName, PSFFSI psffsi, PVBOXSFFSD psffsd,
+                  USHORT ulOpenMode, USHORT usOpenFlag, USHORT usAttr, ULONG Reserved)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_SETSWAP(PSFFSI psffsi, PVBOXSFFSD psffsd)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_ALLOCATEPAGESPACE(PSFFSI psffsi, PVBOXSFFSD psffsd, ULONG cb, USHORT cbWantContig)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_DOPAGEIO(PSFFSI psffsi, PVBOXSFFSD psffsd, struct PageCmdHeader *pList)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFind.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFind.cpp	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFind.cpp	(revision 3655)
@@ -0,0 +1,99 @@
+/** $Id$ */
+/** @file
+ * VBoxSF - OS/2 Shared Folders, Find File IFS EPs.
+ */
+
+/*
+ * Copyright (c) 2007 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 LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxSFInternal.h"
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+
+
+DECLASM(int)
+FS32_FINDFIRST(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, USHORT attr,
+               PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+               USHORT level, USHORT flags)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FINDFROMNAME(PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+                  USHORT level, ULONG position, PCSZ pszName, USHORT flag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FINDNEXT(PFSFSI pfsfsi, PVBOXSFFS pfsfsd, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+              USHORT level, USHORT flag)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FINDCLOSE(PFSFSI pfsfsi, PVBOXSFFS pfsfsd)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+
+
+
+DECLASM(int)
+FS32_FINDNOTIFYFIRST(PCDFSI pcdfsi, PVBOXSFCD pcdfsd, PCSZ pszName, USHORT iCurDirEnd, USHORT attr,
+                     PUSHORT pHandle, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+                     USHORT level, USHORT flags)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FINDNOTIFYNEXT(USHORT handle, PBYTE pbData, USHORT cbData, PUSHORT pcMatch,
+                    USHORT level, ULONG timeout)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+
+DECLASM(int)
+FS32_FINDNOTIFYCLOSE(USHORT handle)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp	(revision 3655)
@@ -0,0 +1,115 @@
+/** $Id$ */
+/** @file
+ * VBoxSF - OS/2 Shared Folders, Initialization.
+ */
+
+/*
+ * Copyright (c) 2007 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 LOG_GROUP LOG_GROUP_DEFAULT
+#include "VBoxSFInternal.h"
+
+#include <VBox/VBoxGuest.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+__BEGIN_DECLS
+/* from VBoxSFA.asm */
+extern RTFAR16 g_fpfnDevHlp;
+extern VBOXGUESTOS2IDCCONNECT g_VBoxGuestIDC;
+extern uint32_t g_u32Info;
+/* from sys0.asm and the linker/end.lib. */
+extern char _text, _etext, _data, _end;
+__END_DECLS
+
+
+
+/**
+ * 32-bit Ring-0 init routine.
+ *
+ * This is called the first time somebody tries to use the IFS.
+ * It will initialize IPRT, Vbgl and whatever else is required.
+ *
+ * The caller will do the necessary AttachDD and calling of the 16 bit
+ * IDC to initialize the g_VBoxGuestIDC global. Perhaps we should move
+ * this bit to VbglInit? It's just that it's so much simpler to do it
+ * while we're on the way here...
+ *
+ */
+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));
+
+    /*
+     * Start by initializing IPRT.
+     */
+    if (    g_VBoxGuestIDC.u32Version == VMMDEV_VERSION
+        &&  VALID_PTR(g_VBoxGuestIDC.u32Session)
+        &&  VALID_PTR(g_VBoxGuestIDC.pfnServiceEP))
+    {
+        int rc = RTR0Init(0);
+        if (RT_SUCCESS(rc))
+        {
+            rc = VbglInit();
+            if (RT_SUCCESS(rc))
+            {
+#ifndef DONT_LOCK_SEGMENTS
+                /*
+                 * Lock the 32-bit segments in memory.
+                 */
+                static KernVMLock_t s_Text32, s_Data32;
+                rc = KernVMLock(VMDHL_LONG,
+                                &_text, (uintptr_t)&_etext - (uintptr_t)&_text,
+                                &s_Text32, (KernPageList_t *)-1, NULL);
+                AssertMsg(rc == NO_ERROR, ("locking text32 failed, rc=%d\n"));
+                rc = KernVMLock(VMDHL_LONG | VMDHL_WRITE,
+                                &_data, (uintptr_t)&_end - (uintptr_t)&_data,
+                                &s_Data32, (KernPageList_t *)-1, NULL);
+                AssertMsg(rc == NO_ERROR, ("locking text32 failed, rc=%d\n"));
+#endif
+
+                Log(("VBoxSFR0Init: completed successfully\n"));
+                return;
+            }
+        }
+
+        LogRel(("VBoxSF: RTR0Init failed, rc=%Rrc\n", rc));
+    }
+    else
+        LogRel(("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 3655)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInternal.h	(revision 3655)
@@ -0,0 +1,107 @@
+/** $Id$ */
+/** @file
+ * VBoxSF - OS/2 Shared Folder IFS, Internal Header.
+ */
+
+/*
+ * Copyright (c) 2007 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.
+ */
+
+#ifndef ___VBoxSFInternal_h___
+#define ___VBoxSFInternal_h___
+
+
+#define INCL_BASE
+#define INCL_ERROR
+#include <os2.h>
+#include <os2ddk/bsekee.h>
+#include <os2ddk/devhlp.h>
+#include <os2ddk/unikern.h>
+#include <os2ddk/fsd.h>
+#undef RT_MAX
+
+#include <iprt/types.h>
+#include <iprt/assert.h>
+
+
+/**
+ * VBoxSF Volume Parameter Structure.
+ *
+ * @remark  Overlays the 36 byte VPFSD structure (fsd.h).
+ */
+typedef struct VBOXSFVP
+{
+    uint32_t u32Dummy;
+} VBOXSFVP;
+AssertCompile(sizeof(VBOXSFVP) <= sizeof(VPFSD));
+/** Pointer to a VBOXSFVP struct. */
+typedef VBOXSFVP *PVBOXSFVP;
+
+
+/**
+ * VBoxSF Current Directory Structure.
+ *
+ * @remark  Overlays the 8 byte CDFSD structure (fsd.h).
+ */
+typedef struct VBOXSFCD
+{
+    uint32_t u32Dummy;
+} VBOXSFCD;
+AssertCompile(sizeof(VBOXSFCD) <= sizeof(CDFSD));
+/** Pointer to a VBOXSFCD struct. */
+typedef VBOXSFCD *PVBOXSFCD;
+
+
+/**
+ * VBoxSF System File Structure.
+ *
+ * @remark  Overlays the 30 byte SFFSD structure (fsd.h).
+ */
+typedef struct VBOXSFFSD
+{
+    /** 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;
+
+
+/**
+ * VBoxSF File Search Structure.
+ *
+ * @remark  Overlays the 24 byte FSFSD structure (fsd.h).
+ */
+typedef struct VBOXSFFS
+{
+    /** Self pointer for quick 16:16 to flat translation. */
+    struct VBOXSFFS *pSelf;
+} VBOXSFFS;
+AssertCompile(sizeof(VBOXSFFS) <= sizeof(FSFSD));
+/** Pointer to a VBOXSFFS struct. */
+typedef VBOXSFFS *PVBOXSFFS;
+
+
+#endif
+
Index: trunk/src/VBox/Additions/os2/VBoxService/Makefile.kmk
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxService/Makefile.kmk	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxService/Makefile.kmk	(revision 3655)
@@ -0,0 +1,45 @@
+# $Id$
+## @file
+# Makefile for the OS/2 Guest Addition Services.
+#
+
+#
+# Copyright (C) 2007 innotek GmbH
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software Foundation,
+# in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+# distribution. VirtualBox OSE is distributed in the hope that it will
+# be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# If you received this file as part of a commercial VirtualBox
+# distribution, then only the terms of your commercial VirtualBox
+# license agreement apply instead of the previous paragraph.
+#
+
+DEPTH = ../../../../..
+include	$(PATH_KBUILD)/header.kmk
+
+#
+# VBoxService.exe
+#
+PROGRAMS += VBoxService
+VBoxService_TEMPLATE      = VBOXOS2GUESTR3
+VBoxService_DEFS          = VBOXSERVICE_TIMESYNC
+VBoxService_DEFS.os2      = VBOX_HGCM VBOXSERVICE_CLIPBOARD
+VBoxService_SOURCES       = \
+	VBoxService.cpp \
+	VBoxServiceTimeSync.cpp 
+VBoxService_SOURCES.os2   = \
+	VBoxService-os2.cpp \
+	VBoxService-os2.def \
+	VBoxServiceClipboard-os2.cpp 
+#VBoxService_LDFLAGS       = -t -v
+VBoxService_LIBS          = \
+	$(VBOX_LIB_VBGL_OS2_R3) \
+	$(VBOX_LIB_IPRT_OS2_GUEST_R3)
+
+include	$(PATH_KBUILD)/footer.kmk
+
Index: trunk/src/VBox/Additions/os2/VBoxService/VBoxService-os2.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxService/VBoxService-os2.cpp	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxService/VBoxService-os2.cpp	(revision 3655)
@@ -0,0 +1,102 @@
+/** $Id$ */
+/** @file
+ * VBoxService - Guest Additions Service Skeleton.
+ */
+
+/*
+ * Copyright (C) 2007 innotek GmbH
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation,
+ * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+ * distribution. VirtualBox OSE is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ */
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+#ifdef __OS2__
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+#endif 
+#include <errno.h>
+
+#include <iprt/string.h>
+#include <iprt/alloca.h>
+#include <VBox/VBoxGuest.h>
+#include "VBoxServiceInternal.h"
+
+
+/**
+ * OS/2 emulation of the BSD daemon() call.
+ */
+int daemon(int nochdir, int noclose)
+{
+    PPIB pPib;
+    PTIB pTib;
+    DosGetInfoBlocks(&pTib, &pPib);
+
+    /* Get the full path to the executable. */
+    char szExe[CCHMAXPATH];
+    APIRET rc = DosQueryModuleName(pPib->pib_hmte, sizeof(szExe), szExe);
+    if (rc)
+    {
+        errno = EDOOFUS;
+        return -1;
+    }
+
+    /* calc the length of the command line. */
+    char *pch = pPib->pib_pchcmd;
+    size_t cch0 = strlen(pch);
+    pch += cch0 + 1;
+    size_t cch1 = strlen(pch);
+    pch += cch1 + 1;
+    char *pchArgs;
+    if (cch1 && *pch)
+    {
+        do  pch = strchr(pch, '\0') + 1;
+        while (*pch);
+
+        size_t cchTotal = pch - pPib->pib_pchcmd;
+        pchArgs = (char *)alloca(cchTotal + sizeof("--daemonized\0\0"));
+        memcpy(pchArgs, pPib->pib_pchcmd, cchTotal - 1);
+        memcpy(pchArgs + cchTotal - 1, "--daemonized\0\0", sizeof("--daemonized\0\0"));
+    }
+    else
+    {
+        size_t cchTotal = pch - pPib->pib_pchcmd + 1;
+        pchArgs = (char *)alloca(cchTotal + sizeof(" --daemonized "));
+        memcpy(pchArgs, pPib->pib_pchcmd, cch0 + 1);
+        pch = pchArgs + cch0 + 1;
+        memcpy(pch, " --daemonized ", sizeof(" --daemonized ") - 1);
+        pch += sizeof(" --daemonized ") - 1;
+        if (cch1)
+            memcpy(pch, pPib->pib_pchcmd + cch0 + 1, cch1 + 2);
+        else
+            pch[0] = pch[1] = '\0';
+    }
+
+    /* spawn a detach process  */
+    char szObj[128];
+    RESULTCODES ResCodes = { 0, 0 };
+    szObj[0] = '\0';
+    rc = DosExecPgm(szObj, sizeof(szObj), EXEC_BACKGROUND, (PCSZ)pchArgs, NULL, &ResCodes, (PCSZ)szExe);
+    if (rc)
+    {
+        VBoxServiceError("DosExecPgm failed with rc=%d and szObj='%s'\n", rc, szObj);
+        errno = EDOOFUS;
+        return -1;
+    }
+    DosExit(EXIT_PROCESS, 0);
+    return -1;
+}
+
+
Index: trunk/src/VBox/Additions/os2/VBoxService/VBoxService-os2.def
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxService/VBoxService-os2.def	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxService/VBoxService-os2.def	(revision 3655)
@@ -0,0 +1,26 @@
+;; @file
+; VBoxService - OS/2 definition file.
+;
+
+;
+; Copyright (C) 2007 innotek GmbH
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License as published by the Free Software Foundation,
+; in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+; distribution. VirtualBox OSE is distributed in the hope that it will
+; be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; If you received this file as part of a commercial VirtualBox
+; distribution, then only the terms of your commercial VirtualBox
+; license agreement apply instead of the previous paragraph.
+;
+
+
+NAME VBoxSvc
+DESCRIPTION 'VirtualBox.org Guest Additions Service for OS/2.'
+CODE SHARED
+DATA MULTIPLE NONSHARED
+
Index: trunk/src/VBox/Additions/os2/VBoxService/VBoxService.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxService/VBoxService.cpp	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxService/VBoxService.cpp	(revision 3655)
@@ -0,0 +1,444 @@
+/** $Id$ */
+/** @file
+ * VBoxService - Guest Additions Service Skeleton.
+ */
+
+/*
+ * Copyright (C) 2007 innotek GmbH
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation,
+ * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+ * distribution. VirtualBox OSE is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ */
+
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <unistd.h> 
+#include <errno.h>
+
+#include <iprt/thread.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/initterm.h>
+#include <iprt/asm.h>
+#include <iprt/path.h>
+#include <VBox/VBoxGuest.h>
+#include "VBoxServiceInternal.h"
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The program name (derived from argv[0]). */
+char *g_pszProgName = "";
+/** The current verbosity level. */
+int g_cVerbosity = 0;
+/** The default service interval (the -i | --interval) option). */
+uint32_t g_DefaultInterval = 0;
+/** Shutdown the main thread. (later, for signals) */
+bool volatile g_fShutdown;
+
+/**
+ * The details of the services that has been compiled in.
+ */
+static struct 
+{
+    /** Pointer to the service descriptor. */
+    PCVBOXSERVICE   pDesc;
+    /** The worker thread. NIL_RTTHREAD if it's the main thread. */
+    RTTHREAD        Thread;
+    /** Shutdown indicator. */
+    bool volatile   fShutdown;
+    /** Indicator set by the service thread exiting. */
+    bool volatile   fStopped;
+    /** Whether the service was started or not. */
+    bool            fStarted;
+    /** Whether the service is enabled or not. */
+    bool            fEnabled;
+} g_aServices[] = 
+{
+#ifdef VBOXSERVICE_CONTROL
+    { &g_Control,   NIL_RTTHREAD, false, false, false, true },
+#endif 
+#ifdef VBOXSERVICE_TIMESYNC
+    { &g_TimeSync,  NIL_RTTHREAD, false, false, false, true },
+#endif 
+#ifdef VBOXSERVICE_CLIPBOARD
+    { &g_Clipboard, NIL_RTTHREAD, false, false, false, true },
+#endif 
+};
+
+
+/**
+ * Displays the program usage message.
+ * 
+ * @returns 1.
+ */
+static int VBoxServiceUsage(void)
+{
+    RTPrintf("usage: %s [-f|--foreground] [-v|--verbose] [-i|--interval <seconds>]\n"
+             "           [--disable-<service>] [--enable-<service>] [-h|-?|--help]\n", g_pszProgName);
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+        RTPrintf("           %s\n", g_aServices[j].pDesc->pszUsage);
+    RTPrintf("\n"
+             "Options:\n"
+             "    -f | --foreground   Don't daemonzie the program. For debugging.\n"
+             "    -v | --verbose      Increment the verbosity level. For debugging.\n"
+             "    -i | --interval     The default interval.\n"
+             "    -h | -? | --help    Show this message and exit with status 1.\n");
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+    {
+        RTPrintf("    --enable-%-10s Enables the %s service. (default)\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
+        RTPrintf("    --disable-%-9s Disables the %s service.\n", g_aServices[j].pDesc->pszName, g_aServices[j].pDesc->pszName);
+        RTPrintf("%s", g_aServices[j].pDesc->pszOptions);
+    }
+    RTPrintf("\n"
+             " Copyright (C) 2007 innotek GmbH\n");
+
+    return 1;
+}
+
+
+/**
+ * Displays a syntax error message.
+ * 
+ * @returns 1
+ * @param   pszFormat   The message text.
+ * @param   ...         Format arguments.
+ */
+int VBoxServiceSyntax(const char *pszFormat, ...)
+{
+    RTStrmPrintf(g_pStdErr, "%s: syntax error: ", g_pszProgName);
+
+    va_list va;
+    va_start(va, pszFormat);
+    RTStrmPrintfV(g_pStdErr, pszFormat, va);
+    va_end(va);
+
+    return 1;
+}
+
+
+/**
+ * Displays an error message.
+ * 
+ * @returns 1
+ * @param   pszFormat   The message text.
+ * @param   ...         Format arguments.
+ */
+int VBoxServiceError(const char *pszFormat, ...)
+{
+    RTStrmPrintf(g_pStdErr, "%s: error: ", g_pszProgName);
+
+    va_list va;
+    va_start(va, pszFormat);
+    RTStrmPrintfV(g_pStdErr, pszFormat, va);
+    va_end(va);
+
+    return 1;
+}
+
+
+/**
+ * Displays a verbose message.
+ * 
+ * @returns 1
+ * @param   pszFormat   The message text.
+ * @param   ...         Format arguments.
+ */
+void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...)
+{
+    if (iLevel <= g_cVerbosity)
+    {
+        RTStrmPrintf(g_pStdOut, "%s: ", g_pszProgName);
+        va_list va;
+        va_start(va, pszFormat);
+        RTStrmPrintfV(g_pStdOut, pszFormat, va);
+        va_end(va);
+    }
+}
+
+
+/**
+ * Gets a 32-bit value argument.
+ *
+ * @returns 0 on success, non-zero exit code on error.
+ * @param   argc    The argument count.
+ * @param   argv    The argument vector
+ * @param   psz     Where in *pi to start looking for the value argument.
+ * @param   pi      Where to find and perhaps update the argument index.
+ * @param   pu32    Where to store the 32-bit value.
+ * @param   u32Min  The minimum value.
+ * @param   u32Max  The maximum value.
+ */
+int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max)
+{
+    if (*psz == ':' || *psz == '=')
+        psz++;
+    if (!*psz)
+    {
+        if (*pi + 1 >= argc)
+            return VBoxServiceSyntax("Missing value for the '%s' argument\n", argv[*pi]);
+        psz = argv[++*pi];
+    }
+
+    char *pszNext;
+    int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
+    if (RT_FAILURE(rc) || *pszNext)
+        return VBoxServiceSyntax("Failed to convert interval '%s' to a number.\n", psz);
+    if (*pu32 < u32Min || *pu32 > u32Max)
+        return VBoxServiceSyntax("The timesync interval of %RU32 secconds is out of range [%RU32..%RU32].\n", 
+                                 *pu32, u32Min, u32Max);
+    return 0;
+}
+
+
+/**
+ * The service thread.
+ * 
+ * @returns Whatever the worker function returns.
+ * @param   ThreadSelf      My thread handle.
+ * @param   pvUser          The service index.
+ */
+static DECLCALLBACK(int) VBoxServiceThread(RTTHREAD ThreadSelf, void *pvUser)
+{
+    const unsigned i = (uintptr_t)pvUser;
+    int rc = g_aServices[i].pDesc->pfnWorker(&g_aServices[i].fShutdown);
+    ASMAtomicXchgBool(&g_aServices[i].fShutdown, true);
+    RTThreadUserSignal(ThreadSelf);
+    return rc;
+}
+
+int main(int argc, char **argv)
+{
+    int rc;
+
+    /*
+     * Init globals and such.
+     */
+    RTR3Init(false, 0);
+    g_pszProgName = RTPathFilename(argv[0]);
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+    {
+        rc = g_aServices[j].pDesc->pfnPreInit();
+        if (RT_FAILURE(rc))
+            return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName);
+    }
+
+    /*
+     * Parse the arguments.
+     */
+    bool fDaemonize = true;
+    bool fDaemonzied = false;
+    for (int i = 1; i < argc; i++)
+    {
+        const char *psz = argv[i];
+        if (*psz != '-')
+            return VBoxServiceSyntax("Unknown argument '%s'\n", psz);
+        psz++;
+
+        /* translate long argument to short */
+        if (*psz == '-')
+        {
+            psz++;
+            size_t cch = strlen(psz);
+#define MATCHES(strconst)       (   cch == sizeof(strconst) - 1 \
+                                 && !memcmp(psz, strconst, sizeof(strconst) - 1) )
+            if (MATCHES("foreground"))
+                psz = "f";
+            else if (MATCHES("verbose"))
+                psz = "v";
+            else if (MATCHES("help"))
+                psz = "h";
+            else if (MATCHES("interval"))
+                psz = "i";
+            else if (MATCHES("daemonized"))
+            {
+                fDaemonzied = true;
+                continue;
+            }
+            else
+            {
+                bool fFound = false;
+
+                if (cch > sizeof("enable-") && !memcmp(psz, "enable-", sizeof("enable-") - 1))
+                    for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
+                        if ((fFound = !stricmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName)))
+                            g_aServices[j].fEnabled = true;
+
+                if (cch > sizeof("disable-") && !memcmp(psz, "disable-", sizeof("disable-") - 1))
+                    for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
+                        if ((fFound = !stricmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName)))
+                            g_aServices[j].fEnabled = false;
+
+                if (!fFound)
+                    for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++)
+                    {
+                        rc = g_aServices[j].pDesc->pfnOption(NULL, argc, argv, &i);
+                        fFound = rc == 0;
+                        if (fFound)
+                            break;
+                        if (rc != -1)
+                            return rc;
+                    }
+                if (!fFound)
+                    return VBoxServiceSyntax("Unknown option '%s'\n", argv[i]);
+                continue;
+            }
+#undef MATCHES
+        }
+
+        /* handle the string of short options. */
+        do
+        {
+            switch (*psz)
+            {
+                case 'i':
+                    rc = VBoxServiceArgUInt32(argc, argv, psz + 1, &i, 
+                                              &g_DefaultInterval, 1, (UINT32_MAX / 1000) - 1);
+                    if (rc)
+                        return rc;
+                    psz = NULL;
+                    break;
+
+                case 'f':
+                    fDaemonize = false;
+                    break;
+
+                case 'v':
+                    g_cVerbosity++;
+                    break;
+
+                case 'h':
+                    return VBoxServiceUsage();
+
+                default:
+                {
+                    bool fFound = false;
+                    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+                    {
+                        rc = g_aServices[j].pDesc->pfnOption(&psz, argc, argv, &i);
+                        fFound = rc == 0;
+                        if (fFound)
+                            break;
+                        if (rc != -1)
+                            return rc;
+                    }
+                    if (!fFound)
+                        return VBoxServiceSyntax("Unknown option '%c' (%s)\n", *psz, argv[i]);
+                    break;
+                }
+            }
+        } while (psz && *++psz);
+    }
+
+    /*
+     * Check that at least one service is enabled.
+     */
+    unsigned iMain = ~0U;
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+        if (g_aServices[j].fEnabled)
+        {
+            iMain = j;
+            break;
+        }
+    if (iMain == ~0U)
+        return VBoxServiceSyntax("At least one service must be enabled.\n");
+
+    /*
+     * Connect to the kernel part before daemonizing so we can fail
+     * and complain if there is some kind of problem.
+     */
+    VBoxServiceVerbose(2, "Calling VbgR3Init()\n");
+    rc = VbglR3Init();
+    if (RT_FAILURE(rc))
+        return VBoxServiceError("VbglR3Init failed with rc=%Rrc.\n", rc);
+
+    /*
+     * Daemonize if requested.
+     */
+    if (fDaemonize && !fDaemonzied)
+    {
+        VBoxServiceVerbose(1, "Daemonizing...\n");
+        errno = 0;
+        if (daemon(0, 0) != 0)
+            return VBoxServiceError("daemon failed: %s\n", strerror(errno));
+        /* in-child */
+    }
+
+/** @todo Make the main thread responsive to signal so it can shutdown/restart the threads on non-SIGKILL signals. */
+
+    /*
+     * Initialize the services.
+     */
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+    {
+        rc = g_aServices[j].pDesc->pfnInit();
+        if (RT_FAILURE(rc))
+            return VBoxServiceError("Service '%s' failed pre-init: %Rrc\n", g_aServices[j].pDesc->pszName);
+    }
+
+    /*
+     * Start the service(s).
+     */
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+    {
+        if (    !g_aServices[j].fEnabled 
+            ||  j == iMain)
+            continue;
+
+        rc = RTThreadCreate(&g_aServices[j].Thread, VBoxServiceThread, (void *)(uintptr_t)j, 0, 
+                            RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, g_aServices[j].pDesc->pszName);
+        if (RT_FAILURE(rc))
+        {
+            VBoxServiceError("RTThreadCreate failed, rc=%Rrc\n", rc);
+            break;
+        }
+        g_aServices[j].fStarted = true;
+
+        /* wait for the thread to initialize */
+        RTThreadUserWait(g_aServices[j].Thread, 60 * 1000);
+        if (g_aServices[j].fShutdown)
+            rc = VERR_GENERAL_FAILURE;
+    }
+    if (RT_SUCCESS(rc))
+    {
+        /* The final service runs in the main thread. */
+        VBoxServiceVerbose(1, "starting '%s' in the main thread\n", g_aServices[iMain].pDesc->pszName);
+        rc = g_aServices[iMain].pDesc->pfnWorker(&g_fShutdown);
+        VBoxServiceError("service '%s' stopped unexpected; rc=%Rrc\n", g_aServices[iMain].pDesc->pszName, rc);
+    }
+
+    /*
+     * Stop and terminate the services.
+     */
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+        ASMAtomicXchgBool(&g_aServices[j].fShutdown, true);
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+        if (g_aServices[j].fStarted)
+            g_aServices[j].pDesc->pfnStop();
+    for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++)
+    {
+        if (g_aServices[j].Thread != NIL_RTTHREAD)
+        {
+            rc = RTThreadWait(g_aServices[j].Thread, 30*1000, NULL);
+            if (RT_FAILURE(rc))
+                VBoxServiceError("service '%s' failed to stop. (%Rrc)\n", g_aServices[j].pDesc->pszName, rc);
+        }
+        g_aServices[j].pDesc->pfnTerm();
+    }
+
+    return 0;
+}
+
Index: trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceClipboard-os2.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceClipboard-os2.cpp	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceClipboard-os2.cpp	(revision 3655)
@@ -0,0 +1,1036 @@
+/** $Id$ */
+/** @file
+ * VBoxService - Guest Additions Clipboard Service, OS/2.
+ */
+
+/*
+ * Copyright (C) 2007 innotek GmbH
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation,
+ * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+ * distribution. VirtualBox OSE is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define INCL_BASE
+#define INCL_PM
+#define INCL_ERRORS
+#include <os2.h>
+
+#include <iprt/thread.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/time.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <VBox/VBoxGuest.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include "VBoxServiceInternal.h"
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/** Header for Odin32 specific clipboard entries.
+ * (Used to get the correct size of the data.)
+ */
+typedef struct _Odin32ClipboardHeader
+{
+    /** magic number */
+    char        achMagic[8];
+    /** Size of the following data.
+     * (The interpretation depends on the type.) */
+    unsigned    cbData;
+    /** Odin32 format number. */
+    unsigned    uFormat;
+} CLIPHEADER, *PCLIPHEADER;
+
+#define CLIPHEADER_MAGIC "Odin\1\0\1"
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+
+/** The control thread (main) handle. 
+ * Only used to avoid some queue creation trouble. */
+static RTTHREAD g_ThreadCtrl = NIL_RTTHREAD;
+/** The HAB of the control thread (main). */
+static HAB g_habCtrl = NULLHANDLE;
+/** The HMQ of the control thread (main). */
+static HMQ g_hmqCtrl = NULLHANDLE;
+
+/** The Listener thread handle. */
+static RTTHREAD g_ThreadListener = NIL_RTTHREAD;
+/** The HAB of the listener thread. */
+static HAB g_habListener = NULLHANDLE;
+/** The HMQ of the listener thread. */
+static HMQ g_hmqListener = NULLHANDLE;
+/** Indicator that gets set if the listener thread is successfully initialized. */
+static bool volatile g_fListenerOkay = false;
+
+/** The HAB of the worker thread. */
+static HAB g_habWorker = NULLHANDLE;
+/** The HMQ of the worker thread. */
+static HMQ g_hmqWorker = NULLHANDLE;
+/** The object window handle. */
+static HWND g_hwndWorker = NULLHANDLE;
+/** The timer id returned by WinStartTimer. */
+static ULONG g_idWorkerTimer = ~0UL;
+/** The state of the clipboard. 
+ * @remark I'm trying out the 'k' prefix from the mac here, bear with me.  */
+static enum
+{ 
+    /** The clipboard hasn't been initialized yet. */
+    kClipboardState_Uninitialized = 0,
+    /** WinSetClipbrdViewer call in progress, ignore WM_DRAWCLIPBOARD. */
+    kClipboardState_SettingViewer,
+    /** We're monitoring the clipboard as a viewer. */
+    kClipboardState_Viewer,
+    /** We're monitoring the clipboard using polling. 
+     * This usually means something is wrong... */
+    kClipboardState_Polling,
+    /** We're destroying the clipboard content, ignore WM_DESTROYCLIPBOARD. */
+    kClipboardState_Destroying,
+    /** We're owning the clipboard (i.e. we have data on it). */
+    kClipboardState_Owner
+} g_enmState = kClipboardState_Uninitialized;
+/** Set if the clipboard was empty the last time we polled it. */
+static bool g_fEmptyClipboard = false;
+
+/** A clipboard format atom for the dummy clipboard data we insert 
+ * watching for clipboard changes. If this format is found on the 
+ * clipboard, the empty clipboard function has not been called 
+ * since we last polled it. */
+static ATOM g_atomNothingChanged = 0;
+
+/** The clipboard connection client ID. */
+static uint32_t g_u32ClientId;
+/** Odin32 CF_UNICODETEXT. See user32.cpp. */
+static ATOM g_atomOdin32UnicodeText = 0;
+/** Odin32 CF_UNICODETEXT. See user32.cpp. */
+#define SZFMT_ODIN32_UNICODETEXT    (PCSZ)"Odin32 UnicodeText"
+
+
+
+
+/** @copydoc VBOXSERVICE::pfnPreInit */
+static DECLCALLBACK(int) VBoxServiceClipboardOS2PreInit(void)
+{
+    return VINF_SUCCESS;
+}
+
+
+/** @copydoc VBOXSERVICE::pfnOption */
+static DECLCALLBACK(int) VBoxServiceClipboardOS2Option(const char **ppszShort, int argc, char **argv, int *pi)
+{
+    return -1;
+}
+
+
+/** @copydoc VBOXSERVICE::pfnInit */
+static DECLCALLBACK(int) VBoxServiceClipboardOS2Init(void)
+{
+    int rc = VERR_GENERAL_FAILURE;
+    g_ThreadCtrl = RTThreadSelf();
+
+    /*
+     * Make PM happy.
+     */
+    PPIB pPib;
+    PTIB pTib;
+    DosGetInfoBlocks(&pTib, &pPib);
+    pPib->pib_ultype = 3; /* PM session type */
+
+    /*
+     * Since we have to send shutdown messages and such from the 
+     * service controller (main) thread, create a HAB and HMQ for it.
+     */
+    g_habCtrl = WinInitialize(0);
+    if (g_habCtrl  == NULLHANDLE)
+    {
+        VBoxServiceError("WinInitialize(0) failed, lasterr=%lx\n", WinGetLastError(NULLHANDLE));
+        return VERR_GENERAL_FAILURE;
+    }
+    g_hmqCtrl = WinCreateMsgQueue(g_habCtrl, 0);
+    if (g_hmqCtrl != NULLHANDLE)
+    {
+        /*
+         * Create the 'nothing-changed' format.
+         */
+        g_atomNothingChanged = WinAddAtom(WinQuerySystemAtomTable(), (PCSZ)"VirtualBox.org Clipboard Service");
+        LONG lLastError = WinGetLastError(g_habCtrl);
+        if (g_atomNothingChanged == 0)
+            g_atomNothingChanged = WinFindAtom(WinQuerySystemAtomTable(), (PCSZ)"VirtualBox.org Clipboard Service");
+        if (g_atomNothingChanged)
+        {
+            /*
+             * Connect to the clipboard service.
+             */
+            VBoxServiceVerbose(4, "clipboard: connecting\n");
+            rc = VbglR3ClipboardConnect(&g_u32ClientId);
+            if (RT_SUCCESS(rc))
+            {
+                /*
+                 * Create any extra clipboard type atoms, like the odin unicode text.
+                 */
+                g_atomOdin32UnicodeText = WinAddAtom(WinQuerySystemAtomTable(), SZFMT_ODIN32_UNICODETEXT);
+                lLastError = WinGetLastError(g_habCtrl);
+                if (g_atomOdin32UnicodeText == 0)
+                    g_atomOdin32UnicodeText = WinFindAtom(WinQuerySystemAtomTable(), SZFMT_ODIN32_UNICODETEXT);
+                if (g_atomOdin32UnicodeText == 0)
+                    VBoxServiceError("WinAddAtom() failed, lasterr=%lx; WinFindAtom() failed, lasterror=%lx\n", 
+                                     lLastError, WinGetLastError(g_habCtrl));
+
+                VBoxServiceVerbose(2, "g_u32ClientId=%RX32 g_atomNothingChanged=%#x g_atomOdin32UnicodeText=%#x\n", 
+                                   g_u32ClientId, g_atomNothingChanged, g_atomOdin32UnicodeText);
+                return VINF_SUCCESS;
+            }
+
+            VBoxServiceError("Failed to connect to the clipboard service, rc=%Rrc!\n", rc);
+        }
+        else
+            VBoxServiceError("WinAddAtom() failed, lasterr=%lx; WinFindAtom() failed, lasterror=%lx\n", 
+                             lLastError, WinGetLastError(g_habCtrl));
+    }
+    else
+        VBoxServiceError("WinCreateMsgQueue(,0) failed, lasterr=%lx\n", WinGetLastError(g_habCtrl));
+    WinTerminate(g_habCtrl);
+    return rc;
+}
+
+
+/**
+ * Check that we're still the view / try make us the viewer.
+ */
+static void VBoxServiceClipboardOS2PollViewer(void)
+{
+    const int iOrgState = g_enmState;
+
+    HWND hwndClipboardViewer = WinQueryClipbrdViewer(g_habWorker);
+    if (hwndClipboardViewer == g_hwndWorker)
+        return;
+
+    if (hwndClipboardViewer == NULLHANDLE)
+    {
+        /* The API will send a WM_DRAWCLIPBOARD message before returning. */
+        g_enmState = kClipboardState_SettingViewer;
+        if (WinSetClipbrdViewer(g_habWorker, g_hwndWorker))
+            g_enmState = kClipboardState_Viewer;
+        else
+            g_enmState = kClipboardState_Polling;
+    }
+    else
+        g_enmState = kClipboardState_Polling;
+    if ((int)g_enmState != iOrgState)
+    { 
+        if (g_enmState == kClipboardState_Viewer)
+            VBoxServiceVerbose(3, "clipboard: viewer\n");
+        else
+            VBoxServiceVerbose(3, "clipboard: poller\n");
+    }
+}
+
+
+/**
+ * Advertise the formats available from the host.
+ */
+static void VBoxServiceClipboardOS2AdvertiseHostFormats(uint32_t fFormats)
+{
+    /*
+     * Open the clipboard and switch to 'destruction' mode.
+     * Make sure we stop being viewer. Temporarily also make sure we're 
+     * not the owner so that PM won't send us any WM_DESTROYCLIPBOARD message.
+     */
+    if (WinOpenClipbrd(g_habWorker))
+    {
+        if (g_enmState == kClipboardState_Viewer)
+            WinSetClipbrdViewer(g_habWorker, NULLHANDLE); 
+        if (g_enmState == kClipboardState_Owner)
+            WinSetClipbrdOwner(g_habWorker, NULLHANDLE); 
+
+        g_enmState = kClipboardState_Destroying;
+        if (WinEmptyClipbrd(g_habWorker))
+        {
+            /*
+             * Take clipboard ownership.
+             */
+            if (WinSetClipbrdOwner(g_habWorker, g_hwndWorker))
+            {
+                g_enmState = kClipboardState_Owner;
+
+                /*
+                 * Do the format advertising.
+                 */
+                if (fFormats & (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT/* | VBOX_SHARED_CLIPBOARD_FMT_HTML ?? */))
+                {
+                    if (!WinSetClipbrdData(g_habWorker, 0, CF_TEXT, CFI_POINTER))
+                        VBoxServiceError("WinSetClipbrdData(,,CF_TEXT,) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+                    if (    g_atomOdin32UnicodeText
+                        &&  !WinSetClipbrdData(g_habWorker, 0, g_atomOdin32UnicodeText, CFI_POINTER))
+                        VBoxServiceError("WinSetClipbrdData(,,g_atomOdin32UnicodeText,) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+                }
+                if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
+                {
+                    /** @todo bitmaps */
+                }
+            }
+            else
+            {
+                VBoxServiceError("WinSetClipbrdOwner failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+                g_enmState = kClipboardState_Polling;
+            }
+        }
+        else
+        {
+            VBoxServiceError("WinEmptyClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+            g_enmState = kClipboardState_Polling;
+        }
+
+        if (g_enmState == kClipboardState_Polling)
+        {
+            g_fEmptyClipboard = true;
+            VBoxServiceClipboardOS2PollViewer();
+        }
+
+        WinCloseClipbrd(g_habWorker);
+    }
+    else
+        VBoxServiceError("VBoxServiceClipboardOS2AdvertiseHostFormats: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+}
+
+
+static void *VBoxServiceClipboardOs2ConvertToOdin32(uint32_t fFormat, USHORT usFmt, void *pv, uint32_t cb)
+{
+    PVOID pvPM = NULL;
+    APIRET rc = DosAllocSharedMem(&pvPM, NULL, cb + sizeof(CLIPHEADER), OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
+    if (rc)
+    {
+        PCLIPHEADER pHdr = (PCLIPHEADER)pvPM;
+        memcpy(pHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pHdr->achMagic));
+        pHdr->cbData = cb;
+        if (usFmt == g_atomOdin32UnicodeText)
+            pHdr->uFormat = usFmt;
+        else
+            AssertFailed();
+        memcpy(pHdr + 1, pv, cb);
+    }
+    else
+    {
+        VBoxServiceError("DosAllocSharedMem(,,%#x,,) -> %ld\n", cb + sizeof(CLIPHEADER), rc);
+        pvPM = NULL;
+    }
+    return pvPM;
+}
+
+
+static void *VBoxServiceClipboardOs2ConvertToPM(uint32_t fFormat, USHORT usFmt, void *pv, uint32_t cb)
+{
+    void *pvPM = NULL;
+
+    /*
+     * The Odin32 stuff is simple, we just assume windows data from the host 
+     * and all we need to do is add the header.
+     */
+    if (    usFmt 
+        &&  (   usFmt == g_atomOdin32UnicodeText
+             /* || usFmt == ...*/
+            )
+       )
+        pvPM = VBoxServiceClipboardOs2ConvertToOdin32(fFormat, usFmt, pv, cb);
+    else if (usFmt == CF_TEXT)
+    {
+        /*
+         * Convert the unicode text to the current ctype locale.
+         *
+         * Note that we probably should be using the current PM or DOS codepage
+         * here instead of the LC_CTYPE one which iconv uses by default. 
+         * -lazybird
+         */
+        Assert(fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
+        char *pszUtf8;
+        int rc = RTUtf16ToUtf8((PCRTUTF16)pv, &pszUtf8);
+        if (RT_SUCCESS(rc))
+        {
+            char *pszLocale;
+            rc = RTStrUtf8ToCurrentCP(&pszLocale, pszUtf8);
+            if (RT_SUCCESS(rc))
+            {
+                size_t cbPM = strlen(pszLocale) + 1;
+                APIRET rc = DosAllocSharedMem(&pvPM, NULL, cbPM, OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
+                if (rc == NO_ERROR)
+                    memcpy(pvPM, pszLocale, cbPM);
+                else
+                {
+                    VBoxServiceError("DosAllocSharedMem(,,%#x,,) -> %ld\n", cb + sizeof(CLIPHEADER), rc);
+                    pvPM = NULL;
+                }
+                RTStrFree(pszLocale);
+            }
+            else
+                VBoxServiceError("RTStrUtf8ToCurrentCP() -> %Rrc\n", rc);
+            RTStrFree(pszUtf8);
+        }
+        else
+            VBoxServiceError("RTUtf16ToUtf8() -> %Rrc\n", rc);
+    }
+    
+    return pvPM;
+}
+
+
+/**
+ * Tries to deliver an advertised host format.
+ * 
+ * @param   usFmt       The PM format name.
+ * 
+ * @remark  We must not try open the clipboard here because WM_RENDERFMT is a 
+ *          request send synchronously by someone who has already opened the 
+ *          clipboard. We would enter a deadlock trying to open it here.
+ *          
+ */
+static void VBoxServiceClipboardOS2RenderFormat(USHORT usFmt)
+{
+    bool fSucceeded = false;
+
+    /*
+     * Determin which format.
+     */
+    uint32_t fFormat;
+    if (    usFmt == CF_TEXT
+        ||  usFmt == g_atomOdin32UnicodeText)
+        fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT; 
+    else /** @todo bitmaps */
+        fFormat = 0;
+    if (fFormat)
+    {
+        /* 
+         * Query the data from the host.
+         * This might require two iterations because of buffer guessing.
+         */
+        uint32_t cb = _4K;
+        int rc = VERR_NO_MEMORY;
+        void *pv = RTMemPageAllocZ(cb);
+        if (pv)
+        {
+            VBoxServiceVerbose(4, "clipboard: reading host data (%#x)\n", fFormat);
+            rc = VbglR3ClipboardReadData(g_u32ClientId, fFormat, pv, cb, &cb);
+            if (rc == VINF_BUFFER_OVERFLOW)
+            {
+                RTMemPageFree(pv);
+                cb = RT_ALIGN_32(cb, PAGE_SIZE);
+                pv = RTMemPageAllocZ(cb);
+                rc = VbglR3ClipboardReadData(g_u32ClientId, fFormat, pv, cb, &cb);
+            }
+            if (RT_FAILURE(rc))
+                RTMemPageFree(pv);
+        }
+        if (RT_SUCCESS(rc))
+        {
+            VBoxServiceVerbose(4, "clipboard: read %u bytes\n", cb);
+
+            /*
+             * Convert the host clipboard data to PM clipboard data and set it.
+             */
+            PVOID pvPM = VBoxServiceClipboardOs2ConvertToPM(fFormat, usFmt, pv, cb);
+            if (pvPM)
+            {
+                if (WinSetClipbrdData(g_habWorker, (ULONG)pvPM, usFmt, CFI_POINTER))
+                    fSucceeded = true;
+                else
+                {
+                    VBoxServiceError("VBoxServiceClipboardOS2RenderFormat: WinSetClipbrdData(,%p,%#x, CF_POINTER) failed, lasterror=%lx\n",
+                                     pvPM, usFmt, WinGetLastError(g_habWorker));
+                    DosFreeMem(pvPM);
+                }
+            }
+            RTMemPageFree(pv);
+        }
+        else
+            VBoxServiceError("VBoxServiceClipboardOS2RenderFormat: Failed to query / allocate data. rc=%Rrc cb=%#RX32\n", rc, cb);
+    }
+
+    /*
+     * Empty the clipboard on failure so we don't end up in any loops.
+     */
+    if (!fSucceeded)
+    {
+        WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
+        g_enmState = kClipboardState_Destroying;
+        WinEmptyClipbrd(g_habWorker);
+        g_enmState = kClipboardState_Polling;
+        g_fEmptyClipboard = true;
+        VBoxServiceClipboardOS2PollViewer();
+    }
+}
+
+
+static void VBoxServiceClipboardOS2SendDataToHost(uint32_t fFormat)
+{
+    if (WinOpenClipbrd(g_habWorker))
+    {
+        PRTUTF16 pwszFree = NULL;
+        void *pv = NULL;
+        uint32_t cb = 0;
+
+        if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+        {
+            /* Got any odin32 unicode text? */
+            PVOID pvPM;
+            PCLIPHEADER pHdr = (PCLIPHEADER)WinQueryClipbrdData(g_habWorker, g_atomOdin32UnicodeText);
+            if (    pHdr 
+                &&  !memcmp(pHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pHdr->achMagic)))
+            {
+                pv = pHdr + 1;
+                cb = pHdr->cbData;
+            }
+
+            /* Got any CF_TEXT? */
+            if (    !pv
+                &&  (pvPM = (PVOID)WinQueryClipbrdData(g_habWorker, CF_TEXT)) != NULL)
+            {
+                char *pszUtf8;
+                int rc = RTStrCurrentCPToUtf8(&pszUtf8, (const char *)pvPM);
+                if (RT_SUCCESS(rc))
+                {
+                    PRTUTF16 pwsz;
+                    rc = RTStrToUtf16(pszUtf8, &pwsz);
+                    if (RT_SUCCESS(rc))
+                    {
+                        pv = pwszFree = pwsz;
+                        cb = (RTUtf16Len(pwsz) + 1) * sizeof(RTUTF16);
+                    }
+                    RTStrFree(pszUtf8);
+                }
+            }
+        }
+        if (!pv)
+            VBoxServiceError("VBoxServiceClipboardOS2SendDataToHost: couldn't find data for %#x\n", fFormat);
+
+        /*
+         * Now, sent whatever we've got to the host (it's waiting).
+         */
+        VBoxServiceVerbose(4, "clipboard: writing %pv/%#d (fFormat=%#x)\n", pv, cb, fFormat);
+        VbglR3ClipboardWriteData(g_u32ClientId, fFormat, pv, cb);
+        RTUtf16Free(pwszFree);
+
+        WinCloseClipbrd(g_habWorker);
+    }
+    else
+    {
+        VBoxServiceError("VBoxServiceClipboardOS2SendDataToHost: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+        VBoxServiceVerbose(4, "clipboard: writing NULL/0 (fFormat=%x)\n", fFormat);
+        VbglR3ClipboardWriteData(g_u32ClientId, fFormat, NULL, 0);
+    }
+}
+
+
+/**
+ * Figure out what's on the clipboard and report it to the host.
+ */
+static void VBoxServiceClipboardOS2ReportFormats(void)
+{
+    uint32_t fFormats = 0;
+    ULONG ulFormat = 0;
+    while ((ulFormat = WinEnumClipbrdFmts(g_habWorker, ulFormat)) != 0)
+    {
+        if (    ulFormat == CF_TEXT 
+            ||  ulFormat == g_atomOdin32UnicodeText)
+            fFormats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
+        /** @todo else bitmaps and stuff. */
+    }
+    VBoxServiceVerbose(4, "clipboard: reporting fFormats=%#x\n", fFormats);
+    VbglR3ClipboardReportFormats(g_u32ClientId, fFormats);
+}
+
+
+/**
+ * Poll the clipboard for changes.
+ * 
+ * This is called both when we're the viewer and when we're 
+ * falling back to polling. If something has changed it will
+ * notify the host.
+ */
+static void VBoxServiceClipboardOS2Poll(void)
+{
+    if (WinOpenClipbrd(g_habWorker))
+    {
+        /*
+         * If our dummy is no longer there, something has actually changed,
+         * unless the clipboard is really empty.
+         */
+        ULONG fFmtInfo;
+        if (!WinQueryClipbrdFmtInfo(g_habWorker, g_atomNothingChanged, &fFmtInfo))
+        {
+            if (WinEnumClipbrdFmts(g_habWorker, 0) != 0)
+            {
+                g_fEmptyClipboard = false;
+                VBoxServiceClipboardOS2ReportFormats();
+    
+                /* inject the dummy */
+                PVOID pv;
+                APIRET rc = DosAllocSharedMem(&pv, NULL, 1, OBJ_GIVEABLE | OBJ_GETTABLE | PAG_READ | PAG_WRITE | PAG_COMMIT);
+                if (rc == NO_ERROR)
+                {
+                    if (WinSetClipbrdData(g_habWorker, (ULONG)pv, g_atomNothingChanged, CFI_POINTER))
+                        VBoxServiceVerbose(4, "clipboard: Added dummy item.\n");
+                    else
+                    {
+                        VBoxServiceError("VBoxServiceClipboardOS2Poll: WinSetClipbrdData failed, lasterr=%#lx\n", WinGetLastError(g_habWorker));
+                        DosFreeMem(pv);
+                    }
+                }
+                else
+                    VBoxServiceError("VBoxServiceClipboardOS2Poll: DosAllocSharedMem(,,1,) -> %ld\n", rc);
+            }
+            else if (!g_fEmptyClipboard)
+            {
+                g_fEmptyClipboard = true;
+                VBoxServiceVerbose(3, "Reporting empty clipboard\n");
+                VbglR3ClipboardReportFormats(g_u32ClientId, 0);
+            }
+        }
+        WinCloseClipbrd(g_habWorker);
+    }
+    else
+        VBoxServiceError("VBoxServiceClipboardOS2Poll: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+}
+
+
+/**
+ * The clipboard we owned was destroyed by someone else.
+ */
+static void VBoxServiceClipboardOS2Destroyed(void)
+{
+    /* make sure we're no longer the owner. */
+    if (WinQueryClipbrdOwner(g_habWorker) == g_hwndWorker)
+        WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
+
+    /* switch to polling state and notify the host. */
+    g_enmState = kClipboardState_Polling;
+    g_fEmptyClipboard = true;
+    VBoxServiceVerbose(3, "Reporting empty clipboard\n");
+    VbglR3ClipboardReportFormats(g_u32ClientId, 0);
+
+    VBoxServiceClipboardOS2PollViewer();
+}
+
+
+/**
+ * The window procedure for the object window.
+ * 
+ * @returns Message result.
+ * 
+ * @param   hwnd    The window handle.
+ * @param   msg     The message.
+ * @param   mp1     Message parameter 1.
+ * @param   mp2     Message parameter 2.
+ */
+static MRESULT EXPENTRY VBoxServiceClipboardOS2WinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+    if (msg != WM_TIMER)
+        VBoxServiceVerbose(6, "VBoxServiceClipboardOS2WinProc: hwnd=%#lx msg=%#lx mp1=%#lx mp2=%#lx\n", hwnd, msg, mp1, mp2);
+
+    switch (msg)
+    {
+        /*
+         * Handle the two system defined messages for object windows.
+         *
+         * We'll just use the CREATE/DESTROY message to create that timer we're
+         * using for the viewer checks and polling fallback.
+         */
+        case WM_CREATE:
+            g_idWorkerTimer = WinStartTimer(g_habWorker, hwnd, 1 /* id */, 1000 /* 1 second */);
+            g_fEmptyClipboard = true;
+            g_enmState = kClipboardState_Polling;
+            return NULL; /* FALSE(/NULL) == Continue*/
+
+        case WM_DESTROY:
+            WinStopTimer(g_habWorker, hwnd, g_idWorkerTimer);
+            g_idWorkerTimer = ~0UL;
+            g_hwndWorker = NULLHANDLE;
+            break;
+
+        /* 
+         * Clipboard viewer message - the content has been changed.
+         * This is sent *after* releasing the clipboard sem 
+         * and during the WinSetClipbrdViewer call.
+         */
+        case WM_DRAWCLIPBOARD:
+            if (g_enmState == kClipboardState_SettingViewer)
+                break;
+            AssertMsgBreak(g_enmState == kClipboardState_Viewer, ("g_enmState=%d\n", g_enmState), );
+            VBoxServiceClipboardOS2Poll();
+            break;
+
+        /* 
+         * Clipboard owner message - the content was replaced.
+         * This is sent by someone with an open clipboard, so don't try open it now.
+         */
+        case WM_DESTROYCLIPBOARD:
+            if (g_enmState == kClipboardState_Destroying)
+                break; /* it's us doing the replacing, ignore. */
+            AssertMsgBreak(g_enmState == kClipboardState_Owner, ("g_enmState=%d\n", g_enmState), );
+            VBoxServiceClipboardOS2Destroyed();
+            break;
+
+        /*
+         * Clipboard owner message - somebody is requesting us to render a format.
+         * This is called by someone which owns the clipboard, but that's fine.
+         */
+        case WM_RENDERFMT:
+            AssertMsgBreak(g_enmState == kClipboardState_Owner, ("g_enmState=%d\n", g_enmState), );
+            VBoxServiceClipboardOS2RenderFormat(SHORT1FROMMP(mp1));
+            break;
+
+        /*
+         * Clipboard owner message - we're about to quit and should render all formats. 
+         *
+         * However, because we're lazy, we'll just ASSUME that since we're quitting 
+         * we're probably about to shutdown or something and there is no point in 
+         * doing anything here except for emptying the clipboard and removing 
+         * ourselves as owner. Any failures at this point are silently ignored.
+         */
+        case WM_RENDERALLFMTS:
+            WinOpenClipbrd(g_habWorker);
+            WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
+            g_enmState = kClipboardState_Destroying;
+            WinEmptyClipbrd(g_habWorker);
+            g_enmState = kClipboardState_Polling;
+            g_fEmptyClipboard = true;
+            WinCloseClipbrd(g_habWorker);
+            break;
+
+        /*
+         * Listener message - the host has new formats to offer.
+         */
+        case WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
+            VBoxServiceClipboardOS2AdvertiseHostFormats(LONGFROMMP(mp1));
+            break;
+
+        /*
+         * Listener message - the host wish to read our clipboard data.
+         */
+        case WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
+            VBoxServiceClipboardOS2SendDataToHost(LONGFROMMP(mp1));
+            break;
+
+        /*
+         * This is just a fallback polling strategy in case some other 
+         * app is trying to view the clipboard too. We also use this
+         * to try recover from errors.
+         *
+         * Because the way the clipboard service works, we have to monitor
+         * it all the time and cannot get away with simpler solutions like
+         * synergy is employing (basically checking upon entering and leaving
+         * a desktop).
+         */
+        case WM_TIMER:
+            if (    g_enmState != kClipboardState_Viewer 
+                &&  g_enmState != kClipboardState_Polling)
+                break;
+
+            /* Lost the position as clipboard viwer?*/
+            if (g_enmState == kClipboardState_Viewer)
+            {
+                if (WinQueryClipbrdViewer(g_habWorker) == hwnd)
+                    break;
+                g_enmState = kClipboardState_Polling;
+            }
+
+            /* poll for changes */
+            VBoxServiceClipboardOS2Poll();
+            VBoxServiceClipboardOS2PollViewer();
+            break;
+
+
+        /* 
+         * Clipboard owner messages dealing with owner drawn content.
+         * We shouldn't be seeing any of these.
+         */
+        case WM_PAINTCLIPBOARD:
+        case WM_SIZECLIPBOARD:
+        case WM_HSCROLLCLIPBOARD:
+        case WM_VSCROLLCLIPBOARD:
+            AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
+            break;
+
+        /*
+         * We shouldn't be seeing any other messages according to the docs.
+         * But for whatever reason, PM sends us a WM_ADJUSTWINDOWPOS message 
+         * during WinCreateWindow. So, ignore that and assert on anything else.
+         */
+        default:
+            AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
+        case WM_ADJUSTWINDOWPOS:
+            break;
+    }
+    return NULL;
+}
+
+
+/**
+ * The listener thread.
+ * 
+ * This thread is dedicated to listening for host messages and forwarding 
+ * these to the worker thread (using PM).
+ * 
+ * The thread will set g_fListenerOkay and signal its user event when it has
+ * completed initialization. In the case of init failure g_fListenerOkay will
+ * not be set.
+ * 
+ * @returns Init error code or VINF_SUCCESS.
+ * @param   ThreadSelf  Our thread handle.
+ * @param   pvUser      Pointer to the clipboard service shutdown indicator.
+ */
+static DECLCALLBACK(int) VBoxServiceClipboardOS2Listener(RTTHREAD ThreadSelf, void *pvUser)
+{
+    bool volatile *pfShutdown = (bool volatile *)pvUser;
+    int rc = VERR_GENERAL_FAILURE;
+    VBoxServiceVerbose(3, "VBoxServiceClipboardOS2Listener: ThreadSelf=%RTthrd\n", ThreadSelf);
+
+    g_habListener = WinInitialize(0);
+    if (g_habListener != NULLHANDLE)
+    {
+        g_hmqListener = WinCreateMsgQueue(g_habListener, 0);
+        if (g_hmqListener != NULLHANDLE)
+        {
+            /*
+             * Tell the worker thread that we're good.
+             */
+            rc = VINF_SUCCESS;
+            ASMAtomicXchgBool(&g_fListenerOkay, true);
+            RTThreadUserSignal(ThreadSelf);
+            VBoxServiceVerbose(3, "VBoxServiceClipboardOS2Listener: Started successfully\n");
+
+            /*
+             * Loop until termination is requested.
+             */
+            bool fQuit = false;
+            while (!*pfShutdown && !fQuit)
+            {
+                uint32_t Msg;
+                uint32_t fFormats;
+                rc = VbglR3ClipboardGetHostMsg(g_u32ClientId, &Msg, &fFormats);
+                if (RT_SUCCESS(rc))
+                {                          
+                    VBoxServiceVerbose(3, "VBoxServiceClipboardOS2Listener: Msg=%#x  fFormats=%#x\n", Msg, fFormats);
+                    switch (Msg)
+                    {
+                        /* 
+                         * The host has announced available clipboard formats. 
+                         * Forward the information to the window, so it can later
+                         * respond do WM_RENDERFORMAT message.
+                         */
+                        case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
+                            if (!WinPostMsg(g_hwndWorker, WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 
+                                            MPFROMLONG(fFormats), 0))
+                                VBoxServiceError("WinPostMsg(%lx, FORMATS,,) failed, lasterr=%#lx\n", 
+                                                 g_hwndWorker, WinGetLastError(g_habListener));
+                            break;
+
+                        /* 
+                         * The host needs data in the specified format.
+                         */
+                        case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
+                            if (!WinPostMsg(g_hwndWorker, WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, 
+                                            MPFROMLONG(fFormats), 0))
+                                VBoxServiceError("WinPostMsg(%lx, READ_DATA,,) failed, lasterr=%#lx\n", 
+                                                 g_hwndWorker, WinGetLastError(g_habListener));
+                            break;
+
+                        /*
+                         * The host is terminating.
+                         */
+                        case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
+                            fQuit = true;
+                            break;
+
+                        default:
+                            VBoxServiceVerbose(1, "VBoxServiceClipboardOS2Listener: Unknown message %RU32\n", Msg);
+                            break;
+                    }
+                }
+                else
+                {
+                    if (*pfShutdown)
+                        break;
+                    VBoxServiceError("VbglR3ClipboardGetHostMsg failed, rc=%Rrc\n", rc);
+                    RTThreadSleep(1000);
+                }
+            } /* the loop */
+
+            WinDestroyMsgQueue(g_hmqListener);
+        }
+        WinTerminate(g_habListener);
+        g_habListener = NULLHANDLE;
+    }
+
+    /* Signal our semaphore to make the worker catch on. */
+    RTThreadUserSignal(ThreadSelf);
+    VBoxServiceVerbose(3, "VBoxServiceClipboardOS2Listener: terminating, rc=%Rrc\n", rc);
+    return rc;
+}
+
+
+/** @copydoc VBOXSERVICE::pfnWorker */
+static DECLCALLBACK(int) VBoxServiceClipboardOS2Worker(bool volatile *pfShutdown)
+{
+    int rc = VERR_GENERAL_FAILURE;
+
+    /*
+     * Standard PM init.
+     */
+    g_habWorker = RTThreadSelf() != g_ThreadCtrl ? WinInitialize(0) : g_habCtrl;
+    if (g_habWorker != NULLHANDLE)
+    {
+        g_hmqWorker = RTThreadSelf() != g_ThreadCtrl ? WinCreateMsgQueue(g_habWorker, 0) : g_hmqCtrl;
+        if (g_hmqWorker != NULLHANDLE)
+        {
+            /*
+             * Create the object window.
+             */
+            if (WinRegisterClass(g_habWorker, (PCSZ)"VBoxServiceClipboardClass", VBoxServiceClipboardOS2WinProc, 0, 0))
+            {
+                g_hwndWorker = WinCreateWindow(HWND_OBJECT,                             /* hwndParent */
+                                               (PCSZ)"VBoxServiceClipboardClass",       /* pszClass */
+                                               (PCSZ)"VirtualBox.org Clipboard Service",/* pszName */
+                                               0,                                       /* flStyle */
+                                               0, 0, 0, 0,                              /* x, y, cx, cy */
+                                               NULLHANDLE,                              /* hwndOwner */
+                                               HWND_BOTTOM,                             /* hwndInsertBehind */
+                                               42,                                      /* id */
+                                               NULL,                                    /* pCtlData */
+                                               NULL);                                   /* pPresParams */
+                if (g_hwndWorker != NULLHANDLE)
+                {
+                    VBoxServiceVerbose(3, "g_hwndWorker=%#lx g_habWorker=%#lx g_hmqWorker=%#lx\n", g_hwndWorker, g_habWorker, g_hmqWorker); 
+
+                    /*
+                     * Create the listener thread.
+                     */
+                    g_fListenerOkay = false;
+                    rc = RTThreadCreate(&g_ThreadListener, VBoxServiceClipboardOS2Listener, (void *)pfShutdown, 0,
+                                        RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CLIPLISTEN");
+                    if (RT_SUCCESS(rc))
+                    {
+                        RTThreadUserWait(g_ThreadListener, 30*1000);
+                        RTThreadUserReset(g_ThreadListener);
+                        if (!g_fListenerOkay)
+                            RTThreadWait(g_ThreadListener, 60*1000, NULL);
+                        if (g_fListenerOkay)
+                        {
+                            /*
+                             * Tell the control thread that it can continue 
+                             * spawning services.
+                             */
+                            RTThreadUserSignal(RTThreadSelf());
+
+                            /*
+                             * The PM event pump.
+                             */
+                            VBoxServiceVerbose(2, "clipboard: Entering PM message loop.\n");
+                            rc = VINF_SUCCESS;
+                            QMSG qmsg;
+                            while (WinGetMsg(g_habWorker, &qmsg, NULLHANDLE, NULLHANDLE, 0))
+                                WinDispatchMsg(g_habWorker, &qmsg);
+                            VBoxServiceVerbose(2, "clipboard: Exited PM message loop. *pfShutdown=%RTbool\n", *pfShutdown);
+
+                            RTThreadWait(g_ThreadListener, 60*1000, NULL);
+                        }
+                        g_ThreadListener = NIL_RTTHREAD;
+                    }
+
+                    /*
+                     * Got a WM_QUIT, clean up.
+                     */
+                    if (g_hwndWorker != NULLHANDLE)
+                    {
+                        WinDestroyWindow(g_hwndWorker);
+                        g_hwndWorker = NULLHANDLE;
+                    }
+                }
+                else
+                    VBoxServiceError("WinCreateWindow() failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+                /* no class deregistration in PM.  */
+            }
+            else
+                VBoxServiceError("WinRegisterClass() failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+
+            if (g_hmqCtrl != g_hmqWorker)
+                WinDestroyMsgQueue(g_hmqWorker);
+            g_hmqWorker = NULLHANDLE;
+        }
+        else
+            VBoxServiceError("WinCreateMsgQueue(,0) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
+
+        if (g_habCtrl != g_habWorker)
+            WinTerminate(g_habWorker);
+        g_habWorker = NULLHANDLE;
+    }
+    else
+        VBoxServiceError("WinInitialize(0) failed, lasterr=%lx\n", WinGetLastError(NULLHANDLE));
+
+    return rc;
+}
+
+
+/** @copydoc VBOXSERVICE::pfnStop */
+static DECLCALLBACK(void) VBoxServiceClipboardOS2Stop(void)
+{
+    if (    g_hmqWorker != NULLHANDLE
+        &&  !WinPostQueueMsg(g_hmqWorker, WM_QUIT, NULL, NULL))
+        VBoxServiceError("WinPostQueueMsg(g_hmqWorker, WM_QUIT, 0,0) failed, lasterr=%lx\n", WinGetLastError(g_habCtrl));
+}
+
+
+/** @copydoc VBOXSERVICE::pfnTerm */
+static DECLCALLBACK(void) VBoxServiceClipboardOS2Term(void)
+{
+    VBoxServiceVerbose(4, "clipboard: disconnecting %#x\n", g_u32ClientId);
+    VbglR3ClipboardDisconnect(g_u32ClientId);
+    g_u32ClientId = 0;
+    WinDestroyMsgQueue(g_hmqCtrl);
+    g_hmqCtrl = NULLHANDLE;
+    WinTerminate(g_habCtrl);
+    g_habCtrl = NULLHANDLE;
+}
+
+
+/**
+ * The OS/2 'clipboard' service description.
+ */
+VBOXSERVICE g_Clipboard = 
+{
+    /* pszName. */
+    "clipboard",
+    /* pszDescription. */
+    "Shared Clipboard",
+    /* pszUsage. */
+    ""
+    ,
+    /* pszOptions. */
+    ""
+    ,
+    /* methods */
+    VBoxServiceClipboardOS2PreInit,
+    VBoxServiceClipboardOS2Option,
+    VBoxServiceClipboardOS2Init,
+    VBoxServiceClipboardOS2Worker,
+    VBoxServiceClipboardOS2Stop,
+    VBoxServiceClipboardOS2Term
+};
+
Index: trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceInternal.h
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceInternal.h	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceInternal.h	(revision 3655)
@@ -0,0 +1,109 @@
+/** @file
+ * VBoxService - Guest Additions Services.
+ */
+
+/*
+ * Copyright (C) 2007 innotek GmbH
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation,
+ * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+ * distribution. VirtualBox OSE is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ */
+
+#ifndef ___VBoxServiceInternal_h___
+#define ___VBoxServiceInternal_h___
+
+/**
+ * A service descriptor.
+ */
+typedef struct 
+{
+    /** The short service name. */
+    const char *pszName;
+    /** The longer service name. */
+    const char *pszDescription;
+    /** The usage options stuff for the --help screen. */
+    const char *pszUsage;
+    /** The option descriptions for the --help screen. */
+    const char *pszOptions;
+
+    /**
+     * Called before parsing arguments.
+     * @returns VBox status code.
+     */
+    DECLCALLBACKMEMBER(int, pfnPreInit)(void);
+    
+    /**
+     * Tries to parse the given command line option.
+     *
+     * @returns 0 if we parsed, -1 if it didn't and anything else means exit.
+     * @param   ppszShort   If not NULL it points to the short option iterator. a short argument. 
+     *                      If NULL examine argv[*pi].
+     * @param   argc        The argument count.
+     * @param   argv        The argument vector.
+     * @param   pi          The argument vector index. Update if any value(s) are eaten.
+     */
+    DECLCALLBACKMEMBER(int, pfnOption)(const char **ppszShort, int argc, char **argv, int *pi);
+    
+    /**
+     * Called before parsing arguments.
+     * @returns VBox status code.
+     */
+    DECLCALLBACKMEMBER(int, pfnInit)(void);
+
+    /** Called from the worker thread. 
+     * 
+     * @returns VBox status code.
+     * @retval  VINF_SUCCESS if exitting because *pfTerminate was set.
+     * @param   pfTerminate     Pointer to a per service termination flag to check 
+     *                          before and after blocking.
+     */
+    DECLCALLBACKMEMBER(int, pfnWorker)(bool volatile *pfTerminate);
+    
+    /**
+     * Stop an service.
+     */
+    DECLCALLBACKMEMBER(void, pfnStop)(void);
+
+    /**
+     * Does termination cleanups.
+     */
+    DECLCALLBACKMEMBER(void, pfnTerm)(void);
+} VBOXSERVICE;
+/** Pointer to a VBOXSERVICE. */
+typedef VBOXSERVICE *PVBOXSERVICE;
+/** Pointer to a const VBOXSERVICE. */
+typedef VBOXSERVICE const *PCVBOXSERVICE;
+          
+          
+__BEGIN_DECLS
+
+extern char *g_pszProgName;
+extern int g_cVerbosity;
+extern uint32_t g_DefaultInterval;
+
+extern int VBoxServiceSyntax(const char *pszFormat, ...);
+extern int VBoxServiceError(const char *pszFormat, ...);
+extern void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...);
+extern int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max);
+
+#ifdef __OS2__
+extern int daemon(int, int);
+#endif 
+
+extern VBOXSERVICE g_TimeSync;
+extern VBOXSERVICE g_Clipboard;
+extern VBOXSERVICE g_Control;
+
+__END_DECLS
+
+#endif
+
Index: trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceTimeSync.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceTimeSync.cpp	(revision 3655)
+++ trunk/src/VBox/Additions/os2/VBoxService/VBoxServiceTimeSync.cpp	(revision 3655)
@@ -0,0 +1,350 @@
+/** $Id$ */
+/** @file
+ * VBoxService - Guest Additions TimeSync Service.
+ */
+
+/*
+ * Copyright (C) 2007 innotek GmbH
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation,
+ * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+ * distribution. VirtualBox OSE is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ */
+
+
+/** @page pg_vboxservice_timesync       The Time Sync Service
+ *
+ * The time sync service plays along with the Time Manager (TM) in the VMM 
+ * to keep the guest time accurate using the host machine as reference. 
+ * TM will try its best to make sure all timer ticks gets delivered so that
+ * there isn't normally any need to adjust the guest time.
+ * 
+ * There are three normal (= acceptable) cases:
+ *      -# When the service starts up. This is because ticks and such might
+ *         be lost during VM and OS startup. (Need to figure out exactly why!)
+ *      -# When the TM is unable to deliver all the ticks and swallows a 
+ *         backlog of ticks. The threshold for this is configurable with
+ *         a default of 60 seconds.
+ *      -# The time is adjusted on the host. This can be caused manually by 
+ *         the user or by some time sync daemon (NTP, LAN server, etc.).
+ * 
+ * There are a number of very odd case where adjusting is needed. Here 
+ * are some of them:
+ *      -# Timer device emulation inaccurancies (like rounding).
+ *      -# Inaccurancies in time source VirtualBox uses.
+ *      -# The Guest and/or Host OS doesn't perform proper time keeping. This
+ *         come about as a result of OS and/or hardware issues.
+ * 
+ * The TM is our source for the host time and will make adjustments for 
+ * current timer delivery lag. The simplistic approach taken by TM is to 
+ * adjust the host time by the current guest timer delivery lag, meaning that
+ * if the guest is behind 1 second with PIT/RTC/++ ticks this should be reflected
+ * in the guest wall time as well.
+ * 
+ * Now, there is any amount of trouble we can cause by changing the time.
+ * Most applications probably uses the wall time when they need to measure 
+ * things. A walltime that is being juggled about every so often, even if just
+ * a little bit, could occationally upset these measurements by for instance
+ * yielding negative results.
+ * 
+ * This bottom line here is that the time sync service isn't really supposed 
+ * to do anything and will try avoid having to do anything when possible.
+ * 
+ * The implementation uses the latency it takes to query host time as the
+ * absolute maximum precision to avoid messing up under timer tick catchup 
+ * and/or heavy host/guest load. (Rational is that a *lot* of stuff may happen
+ * on our way back from ring-3 and TM/VMMDev since we're taking the route
+ * thru the inner EM loop with it's force action processing.)
+ * 
+ * But this latency has to be measured from our perspective, which means it 
+ * could just as easily come out as 0. (OS/2 and Windows guest only updates 
+ * the current time when the timer ticks for instance.) The good thing is
+ * that this isn't really a problem since we won't ever do anything unless 
+ * the drift is noticable.
+ * 
+ * It now boils down to these three (configuration) factors: 
+ *  -# g_TimesyncMinAdjust - The minimum drift we will ever bother with.
+ *  -# g_TimesyncLatencyFactor - The factor we multiply the latency by to
+ *     calculate the dynamic minimum adjust factor.
+ *  -# g_TimesyncMaxLatency - When to start discarding the data as utterly 
+ *     useless and take a rest (someone is too busy to give us good data).
+ */
+
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <unistd.h> 
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <iprt/thread.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/time.h>
+#include <iprt/assert.h>
+#include <VBox/VBoxGuest.h>
+#include "VBoxServiceInternal.h"
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The timesync interval (millseconds). */
+uint32_t g_TimeSyncInterval = 0;
+/**
+ * @see pg_vboxservice_timesync
+ * 
+ * @remark  OS/2: There is either a 1 second resolution on the DosSetDateTime 
+ *                API or a but in the settimeofday implementation. Thus, don't
+ *                bother unless there is at least a 1 second drift.
+ */
+#ifdef __OS2__
+static uint32_t g_TimeSyncMinAdjust = 1000;
+#else
+static uint32_t g_TimeSyncMinAdjust = 100;
+#endif 
+/** @see pg_vboxservice_timesync */
+static uint32_t g_TimeSyncLatencyFactor = 8;
+/** @see pg_vboxservice_timesync */
+static uint32_t g_TimeSyncMaxLatency = 250;
+
+/** The semaphore we're blocking on. */
+static RTSEMEVENTMULTI g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
+
+
+/** @copydoc VBOXSERVICE::pfnPreInit */
+static DECLCALLBACK(int) VBoxServiceTimeSyncPreInit(void)
+{
+    return VINF_SUCCESS;
+}
+
+
+/** @copydoc VBOXSERVICE::pfnOption */
+static DECLCALLBACK(int) VBoxServiceTimeSyncOption(const char **ppszShort, int argc, char **argv, int *pi)
+{
+    int rc = -1;
+    if (ppszShort)
+        /* no short options */;
+    else if (!strcmp(argv[*pi], "--timesync-interval"))
+        rc = VBoxServiceArgUInt32(argc, argv, "", pi, 
+                                  &g_TimeSyncInterval, 1, UINT32_MAX - 1);
+    else if (!strcmp(argv[*pi], "--timesync-min-adjust"))
+        rc = VBoxServiceArgUInt32(argc, argv, "", pi, 
+                                  &g_TimeSyncMinAdjust, 0, 3600000);
+    else if (!strcmp(argv[*pi], "--timesync-latency-factor"))
+        rc = VBoxServiceArgUInt32(argc, argv, "", pi, 
+                                  &g_TimeSyncLatencyFactor, 1, 1024);
+    else if (!strcmp(argv[*pi], "--timesync-max-latency"))
+        rc = VBoxServiceArgUInt32(argc, argv, "", pi, 
+                                  &g_TimeSyncMaxLatency, 1, 3600000);
+    return rc;
+}
+
+
+/** @copydoc VBOXSERVICE::pfnInit */
+static DECLCALLBACK(int) VBoxServiceTimeSyncInit(void)
+{
+    /* 
+     * If not specified, find the right interval default.
+     * Then create the event sem to block on.
+     */
+    if (!g_TimeSyncInterval)
+        g_TimeSyncInterval = g_DefaultInterval * 1000;
+    if (!g_TimeSyncInterval)
+        g_TimeSyncInterval = 10 * 1000;
+
+    int rc = RTSemEventMultiCreate(&g_TimeSyncEvent);
+    AssertRC(rc);
+    return rc;
+}
+
+
+/** @copydoc VBOXSERVICE::pfnWorker */
+DECLCALLBACK(int) VBoxServiceTimeSyncWorker(bool volatile *pfShutdown)
+{
+    RTTIME Time;
+    char sz[64];
+    int rc;
+
+    unsigned cErrors = 0;
+    for (;;)
+    {
+        /*
+         * Try get a reliable time reading.
+         */
+        int cTries = 3;
+        do
+        {
+            /* query it. */
+            RTTIMESPEC GuestNow0, GuestNow, HostNow;
+            RTTimeNow(&GuestNow0);
+            int rc2 = VbglR3GetHostTime(&HostNow);
+            if (RT_FAILURE(rc2))
+            {
+                if (cErrors++ < 10)
+                    VBoxServiceError("VbglR3GetHostTime failed; rc2=%Rrc\n", rc2);
+                break;
+            }
+            RTTimeNow(&GuestNow);
+
+            /* calc latency and check if it's ok. */
+            RTTIMESPEC GuestElapsed = GuestNow;
+            RTTimeSpecSub(&GuestElapsed, &GuestNow0);
+            if ((uint32_t)RTTimeSpecGetMilli(&GuestElapsed) < g_TimeSyncMaxLatency)
+            {
+                /*
+                 * Calculate the adjustment threshold and the current drift.
+                 */
+                uint32_t MinAdjust = RTTimeSpecGetMilli(&GuestElapsed) * g_TimeSyncLatencyFactor;
+                if (MinAdjust < g_TimeSyncMinAdjust)
+                    MinAdjust = g_TimeSyncMinAdjust;
+
+                RTTIMESPEC Drift = HostNow;
+                RTTimeSpecSub(&Drift, &GuestNow);
+                if (RTTimeSpecGetMilli(&Drift) < 0)
+                    MinAdjust += g_TimeSyncMinAdjust; /* extra buffer against moving time backwards. */
+
+                RTTIMESPEC AbsDrift = Drift;
+                RTTimeSpecAbsolute(&AbsDrift);
+                if (g_cVerbosity >= 3)
+                {
+                    VBoxServiceVerbose(3, "Host:    %s    (MinAdjust: %RU32 ms)\n", 
+                                       RTTimeToString(RTTimeExplode(&Time, &HostNow), sz, sizeof(sz)), MinAdjust);
+                    VBoxServiceVerbose(3, "Guest: - %s => %RDtimespec drift\n", 
+                                       RTTimeToString(RTTimeExplode(&Time, &GuestNow), sz, sizeof(sz)),
+                                       &Drift);
+                }
+                if (RTTimeSpecGetMilli(&AbsDrift) > MinAdjust)
+                {
+                    /*
+                     * The drift is to big, we have to make adjustments. :-/
+                     * If we've got adjtime around, try that first - most 
+                     * *NIX systems have it. Fall back on settimeofday.
+                     */
+                    struct timeval tv;
+#if !defined(__OS2__) /* PORTME */
+                    RTTimeSpecGetTimeval(Drift, &tv);
+                    if (adjtime(&tv, NULL) == 0)
+                    {
+                        if (g_cVerbosity >= 1)
+                            VBoxServiceVerbose(1, "adjtime by %RDtimespec\n", &Drift);
+                        cErrors = 0;
+                    }
+                    else
+#endif              
+                    {
+                        errno = 0;
+                        if (!gettimeofday(&tv, NULL))
+                        {
+                            RTTIMESPEC Tmp;
+                            RTTimeSpecAdd(RTTimeSpecSetTimeval(&Tmp, &tv), &Drift);
+                            if (!settimeofday(RTTimeSpecGetTimeval(&Tmp, &tv), NULL))
+                            {
+                                if (g_cVerbosity >= 1)
+                                    VBoxServiceVerbose(1, "settimeofday to %s\n", 
+                                                       RTTimeToString(RTTimeExplode(&Time, &Tmp), sz, sizeof(sz)));
+#ifdef DEBUG
+                                if (g_cVerbosity >= 3)
+                                    VBoxServiceVerbose(2, "       new time %s\n", 
+                                                       RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&Tmp)), sz, sizeof(sz)));
+#endif
+                                cErrors = 0;
+                            }
+                            else if (cErrors++ < 10)
+                                VBoxServiceError("settimeofday failed; errno=%d: %s\n", errno, strerror(errno));
+                        }
+                        else if (cErrors++ < 10)
+                            VBoxServiceError("gettimeofday failed; errno=%d: %s\n", errno, strerror(errno));
+                    }
+                }
+                break;
+            }
+            VBoxServiceVerbose(3, "%RDtimespec: latency too high (%RDtimespec) sleeping 1s\n", GuestElapsed);
+            RTThreadSleep(1000);
+        } while (--cTries > 0);
+
+        /*
+         * Block for a while.
+         *
+         * The event semaphore takes care of ignoring interruptions and it
+         * allows us to implement service wakeup later.
+         */
+        if (*pfShutdown)
+            break;
+        int rc2 = RTSemEventMultiWait(g_TimeSyncEvent, g_TimeSyncInterval);
+        if (*pfShutdown)
+            break;
+        if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
+        {
+            VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
+            rc = rc2;
+            break;
+        }
+    }
+
+    RTSemEventMultiDestroy(g_TimeSyncEvent);
+    g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
+    return rc;
+}
+
+
+/** @copydoc VBOXSERVICE::pfnStop */
+static DECLCALLBACK(void) VBoxServiceTimeSyncStop(void)
+{
+    RTSemEventMultiSignal(g_TimeSyncEvent);
+}
+
+
+/** @copydoc VBOXSERVICE::pfnTerm */
+static DECLCALLBACK(void) VBoxServiceTimeSyncTerm(void)
+{
+    RTSemEventMultiDestroy(g_TimeSyncEvent);
+    g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
+}
+
+
+/**
+ * The 'timesync' service description.
+ */
+VBOXSERVICE g_TimeSync = 
+{
+    /* pszName. */
+    "timesync",
+    /* pszDescription. */
+    "Time synchronization",
+    /* pszUsage. */
+    "[--timesync-interval <ms>] [--timesync-min-adjust <ms>] "
+    "[--timesync-latency-factor <x>] [--time-sync-max-latency <ms>]"
+    ,
+    /* pszOptions. */
+    "    --timesync-interval Specifies the interval at which to synchronize the\n"
+    "                        time with the host. The default is 10000 ms.\n"
+    "    --timesync-min-adjust      The minimum absolute drift drift value measured\n"
+    "                        in milliseconds to make adjustments for.\n"
+    "                        The default is 1000 ms on OS/2 and 100 ms elsewhere.\n"
+    "    --timesync-latency-factor  The factor to multiply the time query latency\n"
+    "                        with to calculate the dynamic minimum adjust time.\n"
+    "                        The default is 8 times.\n"
+    "    --timesync-max-latency     The max host timer query latency to accpet.\n"
+    "                        The default is 250 ms.\n"
+    ,
+    /* methods */
+    VBoxServiceTimeSyncPreInit,
+    VBoxServiceTimeSyncOption,
+    VBoxServiceTimeSyncInit,
+    VBoxServiceTimeSyncWorker,
+    VBoxServiceTimeSyncStop,
+    VBoxServiceTimeSyncTerm
+};
+
