aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-04-11 04:36:49 +0200
committerJavier <dev.git@javispedro.com>2022-04-11 04:36:49 +0200
commit19231982b2f374aed286f4697aebd3fb9fda05d8 (patch)
tree1922eaf34af3d904ce0f75c0e812e8da8e0d38d4
parent970f90228a6978712c28529437721caffec76202 (diff)
downloadvbados-19231982b2f374aed286f4697aebd3fb9fda05d8.tar.gz
vbados-19231982b2f374aed286f4697aebd3fb9fda05d8.zip
add new virtualbox shared folders client
-rw-r--r--.gitignore1
-rw-r--r--dlog.h47
-rw-r--r--dostsr.h116
-rw-r--r--int21dos.h247
-rw-r--r--makefile23
-rw-r--r--mousetsr.c12
-rw-r--r--mousetsr.h6
-rw-r--r--mousew16.lnk2
-rw-r--r--mousmain.c106
-rw-r--r--sfmain.c531
-rw-r--r--sftsr.c891
-rw-r--r--sftsr.h54
-rw-r--r--utils.h10
-rw-r--r--vbmouse.lnk2
-rw-r--r--vbox.c5
-rw-r--r--vbox.h58
-rw-r--r--vboxdev.h838
-rw-r--r--vboxhgcm.h86
-rw-r--r--vboxshfl.h423
-rw-r--r--vbsf.lnk9
-rw-r--r--vds.h8
21 files changed, 3304 insertions, 171 deletions
diff --git a/.gitignore b/.gitignore
index b6b7233..2efb744 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
vbmouse.drv
vbmouse.exe
vbmouse.flp
+vbsf.exe
*.o
*.obj
*.map
diff --git a/dlog.h b/dlog.h
index 3f853e7..54bdc61 100644
--- a/dlog.h
+++ b/dlog.h
@@ -24,13 +24,13 @@
// Customizable defines
/** If 0, these routines become nops */
-#define ENABLE_DLOG 0
+#define ENABLE_DLOG 1
/** 1 means target serial port, 0 means target IO port. */
-#define DLOG_TARGET_SERIAL 1
+#define DLOG_TARGET_SERIAL 0
/** IO port to target.
* VirtualBox uses 0x504, Bochs, DOSBox and descendants use 0xE9.
* When using DLOG_TARGET_SERIAL, use desired UART IO base port. (e.g. COM1 = 0x3F8). */
-#define DLOG_TARGET_PORT 0x3f8
+#define DLOG_TARGET_PORT 0x504
// End of customizable defines
@@ -91,6 +91,24 @@ static void dlog_print(const char *s)
}
}
+/** Print (far) string to log */
+static void dlog_fprint(const char __far *s)
+{
+ char c;
+ while (c = *s++) {
+ dlog_putc(c);
+ }
+}
+
+/** Print (far) string of fixed length to log */
+static void dlog_fnprint(const char __far *s, unsigned l)
+{
+ while (l > 0) {
+ dlog_putc(*s++);
+ l--;
+ }
+}
+
/** Print + newline */
static void dlog_puts(const char *s)
{
@@ -158,16 +176,19 @@ static void dlog_printd(int num)
#else /* ENABLE_DLOG */
-#define dlog_init()
-#define dlog_putc(c)
-#define dlog_endline()
-#define dlog_print(s)
-#define dlog_puts(s)
-#define dlog_printub(n,b)
-#define dlog_printdb(n,b)
-#define dlog_printx(n)
-#define dlog_printu(n)
-#define dlog_printd(n)
+#define dlog_nop() do { } while(0)
+#define dlog_init() dlog_nop()
+#define dlog_putc(c) dlog_nop()
+#define dlog_endline() dlog_nop()
+#define dlog_print(s) dlog_nop()
+#define dlog_fprint(s) dlog_nop()
+#define dlog_fnprint(s,n) dlog_nop()
+#define dlog_puts(s) dlog_nop()
+#define dlog_printub(n,b) dlog_nop()
+#define dlog_printdb(n,b) dlog_nop()
+#define dlog_printx(n) dlog_nop()
+#define dlog_printu(n) dlog_nop()
+#define dlog_printd(n) dlog_nop()
#endif /* ENABLE_DLOG */
diff --git a/dostsr.h b/dostsr.h
new file mode 100644
index 0000000..726ef3a
--- /dev/null
+++ b/dostsr.h
@@ -0,0 +1,116 @@
+/*
+ * VBMouse - utility functions for DOS TSRs
+ * Copyright (C) 2022 Javier S. Pedro
+ *
+ * This program 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; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef DOSTSR_H
+#define DOSTSR_H
+
+#include <stdint.h>
+
+#include "int21dos.h"
+
+/** Deallocates the environment block from the passed PSP segment. */
+static void deallocate_environment(segment_t psp)
+{
+ // TODO : Too lazy to make PSP struct;
+ // 0x2C is offsetof the environment block field on the PSP
+ uint16_t __far *envblockP = (uint16_t __far *) MK_FP(psp, 0x2C);
+ dos_free(*envblockP);
+ *envblockP = 0;
+}
+
+/** Copies a program to another location.
+ * @param new_seg PSP segment for the new location
+ * @param old_seg PSP segment for the old location
+ * @param size size of the program to copy including PSP size. */
+static void copy_program(segment_t new_seg, segment_t old_seg, unsigned size)
+{
+ // The MCB is always 1 segment before.
+ uint8_t __far *new_mcb = MK_FP(new_seg - 1, 0);
+ uint8_t __far *old_mcb = MK_FP(old_seg - 1, 0);
+ uint16_t __far *new_mcb_owner = (uint16_t __far *) &new_mcb[1];
+ char __far *new_mcb_owner_name = &new_mcb[8];
+ char __far *old_mcb_owner_name = &old_mcb[8];
+
+ // Copy entire resident segment including PSP
+ _fmemcpy(MK_FP(new_seg, 0), MK_FP(old_seg, 0), size);
+
+ // Make the new MCB point to itself as owner
+ *new_mcb_owner = new_seg;
+
+ // Copy the program name, too.
+ _fmemcpy(new_mcb_owner_name, old_mcb_owner_name, 8);
+}
+
+/** Allocates a UMB of the given size.
+ * If no UMBs are available, this may still return a block in conventional memory. */
+static __segment allocate_umb(unsigned size)
+{
+ bool old_umb_link = dos_query_umb_link_state();
+ unsigned int old_strategy = dos_query_allocation_strategy();
+ segment_t new_segment;
+
+ dos_set_umb_link_state(true);
+ dos_set_allocation_strategy(DOS_FIT_BEST | DOS_FIT_HIGHONLY);
+
+ new_segment = dos_alloc(get_paragraphs(size));
+
+ dos_set_umb_link_state(old_umb_link);
+ dos_set_allocation_strategy(old_strategy);
+
+ return new_segment;
+}
+
+static __segment reallocate_to_umb(segment_t __far * cur_seg, unsigned segment_size)
+{
+ segment_t old_psp_segment = *cur_seg - (DOS_PSP_SIZE/16);
+ segment_t new_psp_segment;
+
+ deallocate_environment(_psp);
+
+ // If we are already in UMA, don't bother
+ if (old_psp_segment >= 0xA000) {
+ return 0;
+ }
+
+ new_psp_segment = allocate_umb(segment_size);
+
+ if (new_psp_segment && new_psp_segment >= 0xA000) {
+ segment_t new_segment = new_psp_segment + (DOS_PSP_SIZE/16);
+ printf("Moving to upper memory\n");
+
+ // Create a new program instance including PSP at the new_segment
+ copy_program(new_psp_segment, old_psp_segment, segment_size);
+
+ // Tell DOS to "switch" to the new program
+ dos_set_psp(new_psp_segment);
+
+ // Return the new segment
+ return new_segment;
+ } else {
+ printf("No upper memory available\n");
+ if (new_psp_segment) {
+ // In case we got another low-memory segment...
+ dos_free(new_psp_segment);
+ }
+
+ return 0;
+ }
+}
+
+#endif // DOSTSR_H
diff --git a/int21dos.h b/int21dos.h
index 3990793..c41ce44 100644
--- a/int21dos.h
+++ b/int21dos.h
@@ -20,11 +20,50 @@
#ifndef INT21DOS_H
#define INT21DOS_H
+#include <stddef.h>
#include <stdbool.h>
+#include <stdint.h>
#include <dos.h>
+#include "utils.h"
#define DOS_PSP_SIZE 256
+enum dos_error {
+ DOS_ERROR_SUCCESS = 0,
+ DOS_ERROR_INVALID_FUNCTION = 1,
+ DOS_ERROR_FILE_NOT_FOUND = 2,
+ DOS_ERROR_PATH_NOT_FOUND = 3,
+ DOS_ERROR_ERROR_TOO_MANY_OPEN_FILES = 4,
+ DOS_ERROR_ACCESS_DENIED = 5,
+ DOS_ERROR_INVALID_HANDLE = 6,
+ DOS_ERROR_ARENA_TRASHED = 7,
+ DOS_ERROR_NOT_ENOUGH_MEMORY = 8,
+ DOS_ERROR_INVALID_BLOCK = 9,
+ DOS_ERROR_BAD_ENVIRONMENT = 10,
+ DOS_ERROR_BAD_FORMAT = 11,
+ DOS_ERROR_INVALID_ACCESS = 12,
+ DOS_ERROR_INVALID_DATA = 13,
+ DOS_ERROR_OUT_OF_MEMORY = 14,
+ DOS_ERROR_INVALID_DRIVE = 15,
+ DOS_ERROR_CURRENT_DIRECTORY = 16,
+ DOS_ERROR_OT_SAME_DEVICE = 17,
+ DOS_ERROR_NO_MORE_FILES = 18,
+ DOS_ERROR_WRITE_PROTECT = 19,
+ DOS_ERROR_BAD_UNIT = 20,
+ DOS_ERROR_NOT_READY = 21,
+ DOS_ERROR_BAD_COMMAND = 22,
+ DOS_ERROR_CRC = 23,
+ DOS_ERROR_BAD_LENGTH = 24,
+ DOS_ERROR_SEEK = 25,
+ DOS_ERROR_NOT_DOS_DISK = 26,
+ DOS_ERROR_SECTOR_NOT_FOUND = 27,
+
+ DOS_ERROR_GEN_FAILURE = 31,
+
+ DOS_ERROR_HANDLE_EOF = 38,
+ DOS_ERROR_HANDLE_DISK_FULL = 39,
+};
+
enum dos_allocation_strategy {
DOS_FIT_FIRST = 0,
DOS_FIT_BEST = 1,
@@ -34,6 +73,14 @@ enum dos_allocation_strategy {
DOS_FIT_HIGHONLY = 0x40,
};
+typedef __segment segment_t;
+
+/** Converts bytes to paragraphs (16 bytes), rounding up. */
+static inline unsigned get_paragraphs(unsigned bytes)
+{
+ return (bytes + 15) / 16;
+}
+
static unsigned dos_query_allocation_strategy(void);
#pragma aux dos_query_allocation_strategy = \
"mov ax, 0x5800" \
@@ -82,7 +129,7 @@ static __segment dos_alloc(unsigned paragraphs);
__value [ax]
/** Frees DOS segment. */
-static void dos_free(_segment segment);
+static void dos_free(segment_t segment);
#pragma aux dos_free = \
"mov ah, 0x49" \
"int 0x21" \
@@ -90,11 +137,207 @@ static void dos_free(_segment segment);
__modify [ax]
/** Sets a given PSP as the current active process. */
-static void dos_set_psp(_segment psp);
+static void dos_set_psp(segment_t psp);
#pragma aux dos_set_psp = \
"mov ah, 0x50" \
"int 0x21" \
__parm [bx] \
__modify [ax]
+// Internal DOS structures
+
+// For documentation of these, better grab "Undocumented DOS" or similar book
+// These are probably valid only on DOS >= 4
+
+typedef _Packed struct dos_current_directory_structure {
+ char curr_path[67];
+ uint16_t flags;
+ uint32_t disk_blk;
+ void __far * info;
+ char pad1[11];
+} DOSCDS;
+STATIC_ASSERT(sizeof(DOSCDS) == 88);
+
+enum dos_cds_flags {
+ DOS_CDS_FLAG_PHYSICAL = 0x4000,
+ DOS_CDS_FLAG_NETWORK = 0x8000
+};
+
+typedef _Packed struct dos_system_file_table_entry {
+ uint16_t num_handles;
+ uint16_t open_mode;
+ uint8_t attr;
+ uint16_t dev_info;
+ void __far *dpb;
+ uint16_t start_cluster;
+ uint16_t f_time;
+ uint16_t f_date;
+ uint32_t f_size;
+ uint32_t f_pos;
+ uint16_t last_rel_cluster;
+ uint16_t last_abs_cluster;
+ uint16_t dir_sector;
+ uint8_t dir_entry;
+ char filename[11];
+} DOSSFT;
+STATIC_ASSERT(sizeof(DOSSFT) == 43);
+
+typedef _Packed struct dos_search_data_block {
+ char drive_letter;
+ char search_templ[11];
+ uint8_t search_attr;
+ uint16_t dir_entry;
+ uint16_t par_clstr;
+ char pad1[4];
+} DOSSDB;
+STATIC_ASSERT(sizeof(DOSSDB) == 21);
+
+typedef _Packed struct dos_direntry {
+ char filename[11];
+ uint8_t attr;
+ char pad1[10];
+ uint16_t f_time;
+ uint16_t f_date;
+ uint16_t start_cluster;
+ uint32_t f_size;
+} DOSDIR;
+STATIC_ASSERT(sizeof(DOSDIR) == 32);
+
+typedef _Packed struct dos_swappable_area {
+ uint8_t criterr;
+ uint8_t indos;
+ uint8_t drive_num;
+ uint8_t lasterr_dum[9];
+ uint8_t __far *cur_dta;
+ uint16_t cur_psp;
+ uint8_t pad1[4];
+ uint8_t cur_drive;
+ uint8_t pad2[135];
+ char fn1[128];
+ char fn2[128];
+ DOSSDB sdb;
+ DOSDIR found_file;
+ DOSCDS drive_cdscopy;
+ uint8_t fcb_fn1[11];
+ uint8_t pad3[1];
+ uint8_t fcb_fn2[11];
+ uint8_t pad4[11];
+ uint8_t search_attr;
+ uint8_t open_mode;
+ uint8_t pad5[51];
+ DOSCDS __far *drive_cds;
+ uint8_t pad6[12];
+ uint16_t fn1_csoffset;
+ uint16_t fn2_csoffset;
+ uint8_t pad7[71];
+ uint16_t openex_act;
+ uint16_t openex_attr;
+ uint16_t openex_mode;
+ uint8_t pad8[29];
+ DOSSDB rename_srcfile;
+ DOSDIR rename_file;
+} DOSSDA;
+STATIC_ASSERT(offsetof(DOSSDA, cur_dta) == 0xC);
+STATIC_ASSERT(offsetof(DOSSDA, cur_drive) == 0x16);
+STATIC_ASSERT(offsetof(DOSSDA, fn1) == 0x9E);
+STATIC_ASSERT(offsetof(DOSSDA, fn2) == 0x11E);
+STATIC_ASSERT(offsetof(DOSSDA, sdb) == 0x19E);
+STATIC_ASSERT(offsetof(DOSSDA, found_file) == 0x1B3);
+STATIC_ASSERT(offsetof(DOSSDA, search_attr) == 0x24D);
+STATIC_ASSERT(offsetof(DOSSDA, open_mode) == 0x24E);
+STATIC_ASSERT(offsetof(DOSSDA, drive_cds) == 0x282);
+STATIC_ASSERT(offsetof(DOSSDA, openex_act) == 0x2DD);
+STATIC_ASSERT(offsetof(DOSSDA, openex_mode) == 0x2E1);
+
+typedef _Packed struct dos_list_of_lists {
+ char pad1[22];
+ DOSCDS __far *cds;
+ char pad2[7];
+ uint8_t last_drive;
+} DOSLOL;
+
+static inline int drive_letter_to_index(char letter)
+{
+ if (letter >= 'A' && letter <= 'Z') return letter - 'A';
+ else if (letter >= 'a' && letter <= 'z') return letter - 'a';
+ else return -1;
+}
+
+static inline char drive_index_to_letter(int index)
+{
+ return 'A' + index;
+}
+
+static inline DOSSDA __far * dos_get_swappable_dos_area(void);
+#pragma aux dos_get_swappable_dos_area = \
+ "push ds" \
+ "mov ax, 0x5D06" \
+ "int 0x21" \
+ "jnc success" \
+ "xor si, si" \
+ "mov es, si" \
+ "jmp end" \
+ "success: mov ax, ds" \
+ "mov es, ax" \
+ "end: pop ds" \
+ __value [es si] \
+ __modify [ax cx dx]
+
+static inline DOSLOL __far * dos_get_list_of_lists(void);
+#pragma aux dos_get_list_of_lists = \
+ "mov ax, 0x5200" \
+ "int 0x21" \
+ __value [es bx] \
+ __modify [ax]
+
+// Network redirector interface
+
+enum DOS_REDIR_SUBFUNCTION {
+ DOS_FN_RMDIR = 0x01,
+ DOS_FN_MKDIR = 0x03,
+ DOS_FN_CHDIR = 0x05,
+ DOS_FN_CLOSE = 0x06,
+ DOS_FN_COMMIT = 0x07,
+ DOS_FN_READ = 0x08,
+ DOS_FN_WRITE = 0x09,
+ DOS_FN_LOCK = 0x0A,
+ DOS_FN_UNLOCK = 0x0B,
+ DOS_FN_GET_DISK_FREE = 0x0C,
+ DOS_FN_SET_FILE_ATTR = 0x0E,
+ DOS_FN_GET_FILE_ATTR = 0x0F,
+ DOS_FN_RENAME = 0x11,
+ DOS_FN_DELETE = 0x13,
+ DOS_FN_OPEN = 0x16,
+ DOS_FN_CREATE = 0x17,
+ DOS_FN_FIND_FIRST = 0x1B,
+ DOS_FN_FIND_NEXT = 0x1C,
+ DOS_FN_CLOSE_ALL = 0x1D,
+ DOS_FN_DO_REDIR = 0x1E,
+ DOS_FN_PRINTSETUP = 0x1F,
+ DOS_FN_FLUSH = 0x20,
+ DOS_FN_SEEK_END = 0x21,
+ DOS_FN_QUALIFY = 0x23,
+ DOS_FN_OPEN_EX = 0x2E
+};
+
+enum OPENEX_ACTIONS {
+ OPENEX_FAIL_IF_EXISTS = 0x0000,
+ OPENEX_OPEN_IF_EXISTS = 0x0001,
+ OPENEX_REPLACE_IF_EXISTS = 0x0002,
+ OPENEX_FAIL_IF_NEW = 0x0000,
+ OPENEX_CREATE_IF_NEW = 0x0100,
+};
+
+enum OPENEX_MODE {
+ OPENEX_MODE_READ = 0,
+ OPENEX_MODE_WRITE = 1,
+ OPENEX_MODE_RDWR = 2
+};
+
+enum OPENEX_RESULT {
+ OPENEX_FILE_OPENED = 1,
+ OPENEX_FILE_CREATED = 2,
+ OPENEX_FILE_REPLACED = 3,
+};
+
#endif // INT21DOS_H
diff --git a/makefile b/makefile
index 1eb22d5..4c419c9 100644
--- a/makefile
+++ b/makefile
@@ -4,6 +4,8 @@
mousedosobjs = mousetsr.obj mousmain.obj vbox.obj
mousew16objs = mousew16.obj
+sfdosobjs = sftsr.obj sfmain.obj vbox.obj
+
doscflags = -bt=dos -ms -6 -osi -w3 -wcd=202
# -ms to use small memory model (though sometimes ss != ds...)
# -osi to optimize for size, put intrinsics inline (to avoid runtime calls)
@@ -23,7 +25,9 @@ w16cflags = -bt=windows -bd -mc -zu -s -6 -oi -w3 -wcd=202
# We need DOS and Windows headers, not host platform's
set include=$(%watcom)/h/win;$(%watcom)/h
-# Main DOS driver file
+all: vbmouse.exe vbmouse.drv vbsf.exe .SYMBOLIC
+
+# DOS mouse driver
vbmouse.exe: vbmouse.lnk $(mousedosobjs)
wlink @$[@ name $@ file { $(mousedosobjs) }
@@ -36,19 +40,30 @@ mousmain.obj: mousmain.c .AUTODEPEND
vbox.obj: vbox.c .AUTODEPEND
wcc -fo=$^@ $(doscflags) $[@
+# Windows 3.x mouse driver
vbmouse.drv: mousew16.lnk $(mousew16objs)
wlink @$[@ name $@ file { $(mousew16objs) }
mousew16.obj: mousew16.c .AUTODEPEND
wcc -fo=$^@ $(w16cflags) $[@
+# DOS shared folders
+vbsf.exe: vbsf.lnk $(sfdosobjs)
+ wlink @$[@ name $@ file { $(sfdosobjs) }
+
+sfmain.obj: sfmain.c .AUTODEPEND
+ wcc -fo=$^@ $(doscflags) $[@
+
+sftsr.obj: sftsr.c .AUTODEPEND
+ wcc -fo=$^@ $(doscflags) $(dostsrcflags) $[@
+
clean: .SYMBOLIC
- rm -f vbmouse.exe vbmouse.drv vbmouse.flp *.obj *.map
+ rm -f vbmouse.exe vbmouse.drv vbsf.exe vbmouse.flp *.obj *.map
vbmouse.flp:
mformat -C -f 1440 -v VBMOUSE -i $^@ ::
# Build a floppy image containing the driver
-flp: vbmouse.flp vbmouse.exe vbmouse.drv oemsetup.inf .SYMBOLIC
- mcopy -i vbmouse.flp -o vbmouse.exe vbmouse.drv oemsetup.inf ::
+flp: vbmouse.flp vbmouse.exe vbmouse.drv oemsetup.inf vbsf.exe .SYMBOLIC
+ mcopy -i vbmouse.flp -o vbmouse.exe vbmouse.drv oemsetup.inf vbsf.exe ::
diff --git a/mousetsr.c b/mousetsr.c
index fed2c9b..ca48f13 100644
--- a/mousetsr.c
+++ b/mousetsr.c
@@ -1448,20 +1448,14 @@ static LPTSRDATA int33_get_tsr_data(void);
__value [es di] \
__modify [ax]
-static LPTSRDATA local_get_tsr_data(void);
-#pragma aux local_get_tsr_data = \
- "mov ax, cs" \
- "mov es, ax" \
- "mov di, offset data" \
- __value [es di] \
- __modify [ax]
-
LPTSRDATA __far get_tsr_data(bool installed)
{
if (installed) {
return int33_get_tsr_data();
} else {
- return local_get_tsr_data();
+ // Get the TSR data of this instance, not the one currently installed
+ // This is as simple as getting the data from this segment
+ return MK_FP(get_cs(), FP_OFF(&data));
}
}
diff --git a/mousetsr.h b/mousetsr.h
index a025c92..5958505 100644
--- a/mousetsr.h
+++ b/mousetsr.h
@@ -17,8 +17,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef DOSTSR_H
-#define DOSTSR_H
+#ifndef MOUSETSR_H
+#define MOUSETSR_H
#include <stdbool.h>
#include <stdint.h>
@@ -214,4 +214,4 @@ static inline unsigned get_resident_size(void)
return FP_OFF(&resident_end);
}
-#endif
+#endif /* MOUSETSR_H */
diff --git a/mousew16.lnk b/mousew16.lnk
index bb80eb2..0e4166d 100644
--- a/mousew16.lnk
+++ b/mousew16.lnk
@@ -1,5 +1,5 @@
system windows_dll
-option map=w16mouse.map
+option map=mousew16.map
option modname=MOUSE # This is necessary; USER.EXE imports mouse functions using this module name
option description 'VirtualBox Mouse driver'
diff --git a/mousmain.c b/mousmain.c
index 5fe2246..8692048 100644
--- a/mousmain.c
+++ b/mousmain.c
@@ -29,6 +29,7 @@
#include "ps2.h"
#include "vbox.h"
#include "vmware.h"
+#include "dostsr.h"
#include "mousetsr.h"
#if USE_WHEEL
@@ -271,107 +272,16 @@ static int configure_driver(LPTSRDATA data)
return 0;
}
-/** Converts bytes to MS-DOS "paragraphs" (16 bytes), rounding up. */
-static inline unsigned get_paragraphs(unsigned bytes)
+static int move_driver_to_umb(LPTSRDATA __far * data)
{
- return (bytes + 15) / 16;
-}
-
-/** Gets the size of the resident part of this program, including the PSP. */
-static inline unsigned get_resident_program_size()
-{
- return get_resident_size() + DOS_PSP_SIZE;
-}
-
-/** Deallocates the environment block from the passed PSP segment. */
-static void deallocate_environment(__segment psp)
-{
- // TODO : Too lazy to make PSP struct;
- // 0x2C is offsetof the environment block field on the PSP
- uint16_t __far *envblockP = (uint16_t __far *) MK_FP(psp, 0x2C);
- dos_free(*envblockP);
- *envblockP = 0;
-}
-
-/** Copies a program to another location.
- * @param new_seg PSP segment for the new location
- * @param old_seg PSP segment for the old location
- * @param size size of the program to copy including PSP size. */
-static void copy_program(__segment new_seg, __segment old_seg, unsigned size)
-{
- // The MCB is always 1 segment before.
- uint8_t __far *new_mcb = MK_FP(new_seg - 1, 0);
- uint8_t __far *old_mcb = MK_FP(old_seg - 1, 0);
- uint16_t __far *new_mcb_owner = (uint16_t __far *) &new_mcb[1];
- char __far *new_mcb_owner_name = &new_mcb[8];
- char __far *old_mcb_owner_name = &old_mcb[8];
-
- // Copy entire resident segment including PSP
- _fmemcpy(MK_FP(new_seg, 0), MK_FP(old_seg, 0), size);
-
- // Make the new MCB point to itself as owner
- *new_mcb_owner = new_seg;
-
- // Copy the program name, too.
- _fmemcpy(new_mcb_owner_name, old_mcb_owner_name, 8);
-}
-
-/** Allocates a UMB of the given size.
- * If no UMBs are available, this may still return a block in conventional memory. */
-static __segment allocate_umb(unsigned size)
-{
- bool old_umb_link = dos_query_umb_link_state();
- unsigned int old_strategy = dos_query_allocation_strategy();
- __segment new_segment;
-
- dos_set_umb_link_state(true);
- dos_set_allocation_strategy(DOS_FIT_BEST | DOS_FIT_HIGHONLY);
-
- new_segment = dos_alloc(get_paragraphs(size));
-
- dos_set_umb_link_state(old_umb_link);
- dos_set_allocation_strategy(old_strategy);
-
- return new_segment;
-}
-
-static int reallocate_to_umb(LPTSRDATA __far * data)
-{
- const unsigned int resident_size = get_resident_program_size();
- LPTSRDATA old_data = *data;
- __segment old_psp_segment = FP_SEG(old_data) - (DOS_PSP_SIZE/16);
- __segment new_psp_segment;
-
- deallocate_environment(_psp);
-
- // If we are already in UMA, don't bother
- if (old_psp_segment >= 0xA000) {
- return -1;
- }
-
- new_psp_segment = allocate_umb(resident_size);
-
- if (new_psp_segment && new_psp_segment >= 0xA000) {
- __segment new_segment = new_psp_segment + (DOS_PSP_SIZE/16);
- printf("Moving to upper memory\n");
-
- // Create a new program instance including PSP at the new_segment
- copy_program(new_psp_segment, old_psp_segment, resident_size);
-
- // Tell DOS to "switch" to the new program
- dos_set_psp(new_psp_segment);
-
- // Now update the data pointer to the new segment
- *data = MK_FP(new_segment, FP_OFF(old_data));
+ segment_t cur_seg = FP_SEG(data);
+ segment_t umb_seg = reallocate_to_umb(&cur_seg, get_resident_size() + DOS_PSP_SIZE);
+ if (umb_seg) {
+ // Update the data pointer with the new segment
+ *data = MK_FP(umb_seg, FP_OFF(*data));
return 0;
} else {
- printf("No upper memory available\n");
- if (new_psp_segment) {
- // In case we got another low-memory segment...
- dos_free(new_psp_segment);
- }
-
return -1;
}
}
@@ -562,7 +472,7 @@ int main(int argc, const char *argv[])
data = get_tsr_data(false);
if (high) {
- err = reallocate_to_umb(&data);
+ err = move_driver_to_umb(&data);
if (err) high = false; // Not fatal
} else {
deallocate_environment(_psp);
diff --git a/sfmain.c b/sfmain.c
new file mode 100644
index 0000000..6911bcb
--- /dev/null
+++ b/sfmain.c
@@ -0,0 +1,531 @@
+/*
+ * VBMouse - Shared folders entry point
+ * Copyright (C) 2022 Javier S. Pedro
+ *
+ * This program 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; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dos.h>
+
+#include "dlog.h"
+#include "vboxshfl.h"
+#include "dostsr.h"
+#include "sftsr.h"
+
+static char get_drive_letter(const char *path) {
+ if (!path || path[0] == '\0') return '\0';
+ if (path[1] == '\0' || (path[1] == ':' && path[2] == '\0')) {
+ return path[0];
+ } else {
+ return '\0';
+ }
+}
+
+static char find_free_drive_letter() {
+ DOSLOL __far *lol = dos_get_list_of_lists();
+ DOSCDS __far *cds = lol->cds;
+ char letter;
+
+ for (letter = 'A'; letter <= 'Z'; letter++) {
+ int drive = drive_letter_to_index(letter);
+
+ if (cds[drive].flags == 0) {
+ // Looks free to me...
+ return letter;
+ }
+ }
+
+ return 0;
+}
+
+static int list_folders(LPTSRDATA data)
+{
+ int32_t err;
+ SHFLMAPPING maps[SHFL_MAX_MAPPINGS];
+ SHFLSTRING_WITH_BUF(str, SHFL_MAX_LEN);
+ unsigned num_maps = sizeof(maps) / sizeof(SHFLMAPPING);
+ unsigned i;
+
+ err = vbox_shfl_query_mappings(&data->vb, data->hgcm_client_id, 0, &num_maps, maps);
+ if (err) {
+ printf("Error on Query Mappings, err=%ld\n", err);
+ return err;
+ }
+
+ printf("Available shared folders:\n");
+
+ for (i = 0 ; i < num_maps; i++) {
+ err = vbox_shfl_query_map_name(&data->vb, data->hgcm_client_id, maps[i].root, &str.shflstr);
+ if (err) {
+ printf("Error on Query Map Name, err=%ld\n", err);
+ continue;
+ }
+
+ printf(" %s\n", str.buf);
+ }
+
+ return 0;
+}
+
+static int mount_shfl(LPTSRDATA data, int drive, const char *folder)
+{
+ int32_t err;
+ SHFLSTRING_WITH_BUF(str, SHFL_MAX_LEN);
+ SHFLROOT root = SHFL_ROOT_NIL;
+
+ printf("Mounting %s...\n", folder);
+
+ shflstring_strcpy(&str.shflstr, folder);
+
+ err = vbox_shfl_map_folder(&data->vb, data->hgcm_client_id, &str.shflstr, &root);
+ if (err) {
+ fprintf(stderr, "Cannot mount shared folder '%s', err=%d\n", folder, err);
+ return -1;
+ }
+
+ data->drives[drive].root = root;
+
+ return 0;
+}
+
+static int unmount_shfl(LPTSRDATA data, int drive)
+{
+ int32_t err;
+
+ err = vbox_shfl_unmap_folder(&data->vb, data->hgcm_client_id,
+ data->drives[drive].root);
+ if (err) {
+ fprintf(stderr, "Cannot unmount shared folder, err=%d\n", err);
+ return -1;
+ }
+
+ data->drives[drive].root = SHFL_ROOT_NIL;
+
+ return 0;
+}
+
+static int mount(LPTSRDATA data, const char *folder, char drive_letter)
+{
+ int drive = drive_letter_to_index(drive_letter);
+ DOSLOL __far *lol = dos_get_list_of_lists();
+ DOSCDS __far *cds;
+
+ if (drive < 0) {
+ fprintf(stderr, "Invalid drive %c:\n", drive_letter);
+ return EXIT_FAILURE;
+ }
+
+ if (drive >= lol->last_drive || drive >= MAX_NUM_DRIVE) {
+ fprintf(stderr, "Drive %c: is after LASTDRIVE\n", drive_letter);
+ return EXIT_FAILURE;
+ }
+
+ if (data->drives[drive].root != SHFL_ROOT_NIL) {
+ fprintf(stderr, "Drive %c already mounted\n", drive_letter);
+ return EXIT_FAILURE;
+ }
+
+ cds = &lol->cds[drive];
+
+ if (cds->flags) {
+ fprintf(stderr, "Drive %c: already exists\n", drive_letter);
+ return EXIT_FAILURE;
+ }
+
+ if (mount_shfl(data, drive, folder) != 0) {
+ fprintf(stderr, "Cannot mount drive %c:\n", drive_letter);
+ return EXIT_FAILURE;
+ }
+
+ // Ok, set the network flag.
+ // By setting the physical flag, we also let DOS know the drive is present
+ cds->flags = DOS_CDS_FLAG_NETWORK | DOS_CDS_FLAG_PHYSICAL;
+
+ printf("Shared folder '%s' mounted as drive %c:\n", folder, drive_letter);
+
+ return EXIT_SUCCESS;
+}
+
+static int unmount(LPTSRDATA data, char drive_letter)
+{
+ int drive = drive_letter_to_index(drive_letter);
+ DOSLOL __far *lol = dos_get_list_of_lists();
+ DOSCDS __far *cds;
+
+ if (drive < 0) {
+ fprintf(stderr, "Invalid drive %c:\n", drive_letter);
+ return EXIT_FAILURE;
+ }
+
+ if (drive >= lol->last_drive || drive >= MAX_NUM_DRIVE) {
+ fprintf(stderr, "Drive %c: is after LASTDRIVE\n", drive_letter);
+ return EXIT_FAILURE;
+ }
+
+ cds = &lol->cds[drive];
+
+ if (data->drives[drive].root == SHFL_ROOT_NIL) {
+ fprintf(stderr, "Drive %c not mounted\n", drive_letter);
+ return EXIT_FAILURE;
+ }
+
+ if (unmount_shfl(data, drive) != 0) {
+ return EXIT_FAILURE;
+ }
+
+ // Hide the drive from DOS
+ cds->flags = 0;
+
+ // TODO Clear current directory ?
+
+ printf("Drive %c: unmounted\n", drive_letter);
+
+ return EXIT_SUCCESS;
+}
+
+static int automount(LPTSRDATA data)
+{
+ int32_t err;
+ SHFLMAPPING maps[SHFL_MAX_MAPPINGS];
+ SHFLSTRING_WITH_BUF(name, SHFL_MAX_LEN);
+ SHFLSTRING_WITH_BUF(mountPoint, SHFL_MAX_LEN);
+ unsigned num_maps = sizeof(maps) / sizeof(SHFLMAPPING);
+ unsigned flags, version;
+ unsigned i;
+
+ err = vbox_shfl_query_mappings(&data->vb, data->hgcm_client_id,
+ SHFL_MF_AUTOMOUNT, &num_maps, maps);
+ if (err) {
+ printf("Error on Query Mappings, err=%ld\n", err);
+ return err;
+ }
+
+ for (i = 0 ; i < num_maps; i++) {
+ unsigned flags = SHFL_MIQF_DRIVE_LETTER, version = 0;
+ char drive_letter;
+
+ err = vbox_shfl_query_map_info(&data->vb, data->hgcm_client_id, maps[i].root,
+ &name.shflstr, &mountPoint.shflstr, &flags, &version);
+ if (err) {
+ printf("Error on Query Map Info, err=%ld\n", err);
+ continue;
+ }
+
+ if (!(flags & SHFL_MIF_AUTO_MOUNT)) {
+ // No automount...
+ continue;
+ }
+
+ // Can we try to use the mountpoint as a drive letter?
+ if (mountPoint.shflstr.u16Length >= 1) {
+ drive_letter = get_drive_letter(mountPoint.shflstr.ach);
+ }
+
+ // Otherwise try to find a free one...
+ if (!drive_letter) {
+ drive_letter = find_free_drive_letter();
+ }
+
+ mount(data, name.buf, drive_letter);
+ }
+
+ return 0;
+}
+
+static int unmount_all(LPTSRDATA data)
+{
+ int err = 0;
+ unsigned i;
+
+ for (i = 0 ; i < NUM_DRIVES; i++) {
+ if (data->drives[i].root != SHFL_ROOT_NIL) {
+ if (unmount(data, drive_index_to_letter(i)) != 0) {
+ err = -1;
+ }
+ }
+ }
+
+ return err;
+}
+
+static int configure_driver(LPTSRDATA data)
+{
+ unsigned i;
+ int32_t err;
+
+ // Clear up data structures
+ for (i = 0; i < NUM_DRIVES; ++i) {
+ data->drives[i].root = SHFL_ROOT_NIL;
+ }
+
+ data->dossda = dos_get_swappable_dos_area();
+ data->search_dir_handle = SHFL_HANDLE_NIL;
+
+ err = vbox_init_device(&data->vb);
+ if (err) {
+ fprintf(stderr, "Cannot find VirtualBox PCI device, err=%ld\n", err);
+ return -1;
+ }
+
+ printf("Found VirtualBox device at IO 0x%x\n", data->vb.iobase);
+
+ err = vbox_init_buffer(&data->vb);
+ if (err) {
+ fprintf(stderr, "Cannot lock buffer used for VirtualBox communication, err=%ld\n", err);
+ return -1;
+ }
+
+ err = vbox_report_guest_info(&data->vb, VBOXOSTYPE_DOS);
+ if (err) {
+ fprintf(stderr, "VirtualBox communication is not working, err=%ld\n", err);
+ return -1;
+ }
+
+ err = vbox_hgcm_connect_existing(&data->vb, "VBoxSharedFolders", &data->hgcm_client_id);
+ if (err) {
+ printf("Cannot connect to shared folder service, err=%ld\n", err);
+ return -1;
+ }
+
+ err = vbox_shfl_set_utf8(&data->vb, data->hgcm_client_id);
+ if (err) {
+ printf("Cannot configure UTF-8 on shared folder service, err=%ld\n", err);
+ return -1;
+ }
+
+ printf("Connected to VirtualBox shared folder service\n");
+
+ return 0;
+}
+
+static int move_driver_to_umb(LPTSRDATA __far * data)
+{
+ segment_t cur_seg = FP_SEG(data);
+ segment_t umb_seg = reallocate_to_umb(&cur_seg, get_resident_size() + DOS_PSP_SIZE);
+
+ if (umb_seg) {
+ // Update the data pointer with the new segment
+ *data = MK_FP(umb_seg, FP_OFF(*data));
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static __declspec(aborts) int install_driver(LPTSRDATA data, bool high)
+{
+ const unsigned int resident_size = DOS_PSP_SIZE + get_resident_size();
+
+ // No more interruptions from now on and until we TSR.
+ // Inserting ourselves in the interrupt chain should be atomic.
+ _disable();
+
+ data->prev_int2f_handler = _dos_getvect(0x2f);
+ _dos_setvect(0x2f, data:>int2f_isr);
+
+ printf("Driver installed\n");
+
+ // If we reallocated ourselves to UMB,
+ // it's time to free our initial conventional memory allocation
+ if (high) {
+ // We are about to free() our own code segment.
+ // Nothing should try to allocate memory between this and the TSR call
+ // below, since it could overwrite our code...
+ dos_free(_psp);
+ }
+
+ _dos_keep(EXIT_SUCCESS, get_paragraphs(resident_size));
+
+ // Shouldn't reach this part
+ return EXIT_FAILURE;
+}
+
+static bool check_if_driver_uninstallable(LPTSRDATA data)
+{
+ void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f);
+
+ // Compare the segment of the installed handler to see if its ours
+ // or someone else's
+ if (FP_SEG(cur_int2f_handler) != FP_SEG(data)) {
+ fprintf(stderr, "INT2F has been hooked by someone else, cannot safely remove\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int unconfigure_driver(LPTSRDATA data)
+{
+ unmount_all(data);
+
+ vbox_hgcm_disconnect(&data->vb, data->hgcm_client_id);
+ data->hgcm_client_id = 0;
+
+ return 0;
+}
+
+static int uninstall_driver(LPTSRDATA data)
+{
+ _dos_setvect(0x2f, data->prev_int2f_handler);
+
+ // Find and deallocate the PSP (including the entire program),
+ // it is always 256 bytes (16 paragraphs) before the TSR segment
+ dos_free(FP_SEG(data) - (DOS_PSP_SIZE/16));
+
+ printf("Driver uninstalled\n");
+
+ return 0;
+}
+
+static int driver_not_found(void)
+{
+ fprintf(stderr, "Driver data not found (driver not installed?)\n");
+ return EXIT_FAILURE;
+}
+
+static void print_help(void)
+{
+ printf("\n"
+ "Usage: \n"
+ " VBSF <ACTION> <ARGS..>\n\n"
+ "Supported actions:\n"
+ " install install the driver (default)\n"
+ " low install in conventional memory (otherwise UMB)\n"
+ " uninstall uninstall the driver from memory\n"
+ " list list available shared folders\n"
+ " mount <FOLD> <X:> mount a shared folder into drive X:\n"
+ " umount <X:> unmount shared folder from drive X:\n"
+ );
+}
+
+static int invalid_arg(const char *s)
+{
+ fprintf(stderr, "Invalid argument '%s'\n", s);
+ print_help();
+ return EXIT_FAILURE;
+}
+
+static int arg_required(const char *s)
+{
+ fprintf(stderr, "Argument required for '%s'\n", s);
+ print_help();
+ return EXIT_FAILURE;
+}
+
+static bool is_true(const char *s)
+{
+ return stricmp(s, "yes") == 0
+ || stricmp(s, "y") == 0
+ || stricmp(s, "on") == 0
+ || stricmp(s, "true") == 0
+ || stricmp(s, "enabled") == 0
+ || stricmp(s, "enable") == 0
+ || stricmp(s, "1") == 0;
+}
+
+static bool is_false(const char *s)
+{
+ return stricmp(s, "no") == 0
+ || stricmp(s, "n") == 0
+ || stricmp(s, "off") == 0
+ || stricmp(s, "false") == 0
+ || stricmp(s, "disabled") == 0
+ || stricmp(s, "disable") == 0
+ || stricmp(s, "0") == 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ LPTSRDATA data = get_tsr_data(true);
+ int err, argi = 1;
+
+ //printf("\nVBSF %x.%x\n", VERSION_MAJOR, VERSION_MINOR);
+
+ if (argi >= argc || stricmp(argv[argi], "install") == 0) {
+ bool high = true;
+
+ argi++;
+ for (; argi < argc; argi++) {
+ if (stricmp(argv[argi], "low") == 0) {
+ high = false;
+ } else if (stricmp(argv[argi], "high") == 0) {
+ high = true;
+ } else {
+ return invalid_arg(argv[argi]);
+ }
+ }
+
+ if (data) {
+ printf("VBSF already installed\n");
+ print_help();
+ return EXIT_SUCCESS;
+ }
+
+ data = get_tsr_data(false);
+ if (high) {
+ err = move_driver_to_umb(&data);
+ if (err) high = false; // Not fatal
+ } else {
+ deallocate_environment(_psp);
+ }
+ err = configure_driver(data);
+ if (err) return EXIT_FAILURE;
+ err = automount(data);
+ if (err) return EXIT_FAILURE;
+ return install_driver(data, high);
+ } else if (stricmp(argv[argi], "uninstall") == 0) {
+ if (!data) return driver_not_found();
+ if (!check_if_driver_uninstallable(data)) {
+ return EXIT_FAILURE;
+ }
+ err = unconfigure_driver(data);
+ if (err) {
+ return EXIT_FAILURE;
+ }
+ return uninstall_driver(data);
+ } else if (stricmp(argv[argi], "list") == 0) {
+ if (!data) return driver_not_found();
+ return list_folders(data);
+ } else if (stricmp(argv[argi], "mount") == 0) {
+ const char *folder;
+ char drive;
+ if (!data) return driver_not_found();
+
+ argi++;
+ if (argi >= argc) return arg_required("mount");
+ folder = argv[argi];
+ argi++;
+ if (argi >= argc) return arg_required("mount");
+ drive = get_drive_letter(argv[argi]);
+ if (!drive) return invalid_arg(argv[argi]);
+
+ return mount(data, folder, drive);
+ } else if (stricmp(argv[argi], "umount") == 0 || stricmp(argv[argi], "unmount") == 0) {
+ char drive;
+ if (!data) return driver_not_found();
+
+ argi++;
+ if (argi >= argc) return arg_required("umount");
+ drive = get_drive_letter(argv[argi]);
+ if (!drive) return invalid_arg(argv[argi]);
+
+ return unmount(data, drive);
+ } else {
+ return invalid_arg(argv[argi]);
+ }
+}
diff --git a/sftsr.c b/sftsr.c
new file mode 100644
index 0000000..4472caf
--- /dev/null
+++ b/sftsr.c
@@ -0,0 +1,891 @@
+#include <stdlib.h>
+#include <string.h>
+#include <i86.h>
+
+#include "int21dos.h"
+#include "vboxshfl.h"
+#include "sftsr.h"
+
+TSRDATA data;
+
+/** Private buffer for VirtualBox filenames. */
+static SHFLSTRING_WITH_BUF(shflstr, SHFL_MAX_LEN);
+
+static SHFLDIRINFO_WITH_BUF(shfldirinfo, SHFL_MAX_LEN);
+
+static SHFLCREATEPARMS createparms;
+
+union qword_to_words {
+ uint64_t q;
+ struct {
+ uint16_t w[4];
+ };
+};
+
+#if 0
+static uint32_t map_timestampns_to_timestamps(uint64_t timestampns)
+{
+ unsigned long timestampsec;
+
+ __asm {
+ push eax ; Preserve 32-bit regs
+ push edx
+ mov eax, dword ptr [timestampns]
+ mov edx, dword ptr [timestampns + 4]
+
+ mov ecx, 1000000000 /* ns to seconds */
+ div ecx
+ mov [timestampsec], eax
+ pop edx
+ pop eax
+ }
+
+ return timestampsec;
+}
+#endif
+
+static void map_timestampns_to_dostime(uint64_t timestampns,
+ uint16_t __far * time, uint16_t __far * date)
+{
+ // TODO
+ (void) timestampns;
+ *time = 0;
+ *date = 0;
+}
+
+static uint8_t map_shfl_attr_to_dosattr(const SHFLFSOBJATTR *a)
+{
+ // DOS attributes are in the higher word of fMode (see RTFS_DOS_*)
+ uint8_t attr = (a->fMode >> 16) & 0x3F;
+ if (a->fMode & 0x4000U) attr |= _A_SUBDIR;
+ return attr;
+}
+
+static void map_shfl_info_to_dossft(DOSSFT __far *sft, SHFLFSOBJINFO *i)
+{
+ sft->attr = map_shfl_attr_to_dosattr(&i->Attr);
+ sft->f_size = i->cbObject;
+ map_timestampns_to_dostime(i->ModificationTime, &sft->f_time, &sft->f_date);
+}
+
+static void map_shfl_info_to_dosdir(DOSDIR __far *dir, SHFLFSOBJINFO *i)
+{
+ dir->attr = map_shfl_attr_to_dosattr(&i->Attr);
+ dir->f_size = i->cbObject;
+ map_timestampns_to_dostime(i->ModificationTime, &dir->f_time, &dir->f_date);
+ dir->start_cluster = 0;
+}
+
+static uint64_t get_sft_vbox_handle(DOSSFT __far *sft)
+{
+ union qword_to_words u;
+ u.w[0] = sft->start_cluster;
+ u.w[1] = sft->last_rel_cluster;
+ u.w[2] = sft->last_abs_cluster;
+ u.w[3] = sft->dir_sector;
+ return u.q;
+}
+
+static void set_sft_vbox_handle(DOSSFT __far *sft, uint64_t handle)
+{
+ union qword_to_words u;
+ u.q = handle;
+ sft->start_cluster = u.w[0];
+ sft->last_rel_cluster = u.w[1];
+ sft->last_abs_cluster = u.w[2];
+ sft->dir_sector = u.w[3];
+}
+
+#if ENABLE_DLOG
+static void print_handle(uint64_t handle)
+{
+
+ union qword_to_words u;
+ u.q = handle;
+ dlog_printx(u.w[3]);
+ dlog_putc('.');
+ dlog_printx(u.w[2]);
+ dlog_putc('.');
+ dlog_printx(u.w[1]);
+ dlog_putc('.');
+ dlog_printx(u.w[0]);
+}
+#else
+#define print_handle(h) dlog_nop()
+#endif
+
+static int get_op_drive_num(union INTPACK __far *r)
+{
+ DOSSFT __far *sft;
+
+ switch (r->h.al) {
+ case DOS_FN_CLOSE:
+ case DOS_FN_COMMIT:
+ case DOS_FN_READ:
+ case DOS_FN_WRITE:
+ case DOS_FN_LOCK:
+ case DOS_FN_UNLOCK:
+ case DOS_FN_SEEK_END:
+ // Some operations use an SFT and we directly get the drive from it
+ sft = MK_FP(r->x.es, r->x.di);
+ return sft->dev_info & 0x1F;
+
+ case DOS_FN_RMDIR:
+ case DOS_FN_MKDIR:
+ case DOS_FN_CHDIR:
+ case DOS_FN_SET_FILE_ATTR:
+ case DOS_FN_GET_FILE_ATTR:
+ case DOS_FN_RENAME:
+ case DOS_FN_DELETE:
+ case DOS_FN_OPEN:
+ case DOS_FN_CREATE:
+ case DOS_FN_FIND_FIRST:
+ case DOS_FN_OPEN_EX:
+ // We can use the drive of the first filename argument for these operations
+ return drive_letter_to_index(data.dossda->fn1[0]);
+
+ case DOS_FN_GET_DISK_FREE:
+ // Otherwise just get the current drive from dos
+ return drive_letter_to_index(data.dossda->drive_cds->curr_path[0]);
+
+ case DOS_FN_FIND_NEXT:
+ return data.dossda->sdb.drive_letter & 0x1F;
+
+ default:
+ // We don't support this operation
+ return -1;
+ }
+}
+
+static bool is_call_for_mounted_drive(union INTPACK __far *r)
+{
+ int drive = get_op_drive_num(r);
+
+ if (drive < 0 || drive >= MAX_NUM_DRIVE) {
+ return false;
+ }
+
+ return data.drives[drive].root != SHFL_ROOT_NIL;
+}
+
+static void clear_dos_err(union INTPACK __far *r)
+{
+ r->x.flags &= ~INTR_CF;
+}
+
+static void set_dos_err(union INTPACK __far *r, int err)
+{
+ r->x.flags |= INTR_CF;
+ r->x.ax = err;
+}
+
+static int vbox_err_to_dos(int32_t err)
+{
+ switch (err) {
+ case VINF_SUCCESS:
+ return DOS_ERROR_SUCCESS;
+ case VERR_INVALID_PARAMETER:
+ return DOS_ERROR_INVALID_FUNCTION;
+ case VERR_INVALID_HANDLE:
+ return DOS_ERROR_INVALID_HANDLE;
+ case VERR_FILE_NOT_FOUND:
+ return DOS_ERROR_FILE_NOT_FOUND;
+ case VERR_PATH_NOT_FOUND:
+ return DOS_ERROR_PATH_NOT_FOUND;
+ case VERR_NO_MORE_FILES:
+ return DOS_ERROR_NO_MORE_FILES;
+ default:
+ return DOS_ERROR_GEN_FAILURE;
+ }
+}
+
+static void set_vbox_err(union INTPACK __far *r, int32_t err)
+{
+ dlog_print("vbox error ");
+ dlog_printx(err >> 16);
+ dlog_print(":");
+ dlog_printx(err & 0xFFFF);
+ dlog_endline();
+ r->x.flags |= INTR_CF;
+ r->x.ax = vbox_err_to_dos(err);
+}
+
+static int my_strrchr(const char __far *str, char c)
+{
+ int last = -1;
+ const char __far *s = str;
+ while (*s) {
+ if (*s == c) last = s - str;
+ s++;
+ }
+ return last;
+}
+
+static const char * get_basename(const char *path)
+{
+ int last_sep = my_strrchr(path, '\\');
+ if (last_sep >= 0) {
+ return &path[last_sep+1];
+ } else {
+ return path;
+ }
+}
+
+static void translate_filename_to_host(SHFLSTRING *str)
+{
+ // TODO This should map UTF-8 to local CP :(
+ (void) str;
+}
+
+static void translate_filename_from_host(SHFLSTRING *str)
+{
+ // TODO This should map UTF-8 to local CP :(
+ // At least do a poor man's uppercase...
+ unsigned i;
+ for (i = 0; i < str->u16Length; i++) {
+ if (str->ach[i] >= 'a' && str->ach[i] <= 'z') {
+ str->ach[i] = 'A' + (str->ach[i] - 'a');
+ }
+ }
+}
+
+static void fix_wildcards(SHFLSTRING *str)
+{
+ unsigned i;
+
+ // If this is the standard ????????.??? patterns, replace with *
+ if (str->u16Length >= 8+1+3) {
+ i = str->u16Length - (8+1+3);
+ if (memcmp(&str->ach[i], "????????.???", (8+1+3)) == 0) {
+ strcpy(&str->ach[i], "*");
+ str->u16Length = i + 1;
+ }
+ }
+
+ for (i = 1; i < str->u16Length; i++) {
+ // VirtualBox will only match N consecutive ?s to filenames longer than N
+ // Replace consecutive ???? with ?***
+ if ( str->ach[i] == '?' &&
+ (str->ach[i-1] == '*' || str->ach[i-1] == '?') ) {
+ str->ach[i] = '*';
+ }
+ }
+}
+
+
+static void copy_drive_relative_filename(SHFLSTRING *str, const char __far *path)
+{
+ // Assume X:.... path for now, i.e. drive_relative path starts at char 2
+ shflstring_strcpy(str, path + 2);
+}
+
+static void copy_drive_relative_dirname(SHFLSTRING *str, const char __far *path)
+{
+ int last_sep = my_strrchr(path + 2, '\\');
+ if (last_sep >= 0) {
+ shflstring_strncpy(str, path + 2, last_sep == 0 ? 1 : last_sep);
+ } else {
+ shflstring_strcpy(str, path + 2);
+ }
+}
+
+static bool copy_to_8_3_filename(char __far *dst, const SHFLSTRING *str)
+{
+ int last_dot = my_strrchr(str->ach, '.');
+ unsigned namelen, extlen;
+ bool valid_8_3 = true;
+
+ namelen = last_dot >= 0 ? last_dot : str->u16Length;
+ extlen = last_dot >= 0 ? str->u16Length - (last_dot + 1) : 0;
+
+ if (extlen == 0 && (namelen <= 1 && str->ach[0] == '.')) {
+ // . , .. files
+ namelen = str->u16Length;
+ extlen = 0;
+ }
+
+ if (namelen > 8) {
+ namelen = 8;
+ valid_8_3 = false;
+ }
+
+ if (extlen > 3) {
+ extlen = 3;
+ valid_8_3 = false;
+ }
+
+ _fmemcpy(&dst[0], str->ach, namelen);
+ _fmemset(&dst[namelen], ' ', 8 - namelen);
+ _fmemcpy(&dst[8], str->ach + last_dot + 1, extlen);
+ _fmemset(&dst[8+extlen], ' ', 3 - extlen);
+
+ return valid_8_3;
+}
+
+static void handle_create_open_ex(union INTPACK __far *r)
+{
+ const char __far *path = data.dossda->fn1;
+ int drive = drive_letter_to_index(path[0]);
+ SHFLROOT root = data.drives[drive].root;
+ DOSSFT __far *sft = MK_FP(r->x.es, r->x.di);
+ unsigned int action, mode;
+ bool save_result;
+ int32_t err;
+
+ switch (r->h.al) {
+ case DOS_FN_CREATE:
+ action = OPENEX_CREATE_IF_NEW | OPENEX_REPLACE_IF_EXISTS;
+ mode = OPENEX_MODE_WRITE;
+ save_result = false;
+ break;
+ case DOS_FN_OPEN:
+ action = OPENEX_FAIL_IF_NEW | OPENEX_OPEN_IF_EXISTS;
+ mode = data.dossda->open_mode & 0x7F;
+ save_result = false;
+ break;
+ case DOS_FN_OPEN_EX:
+ default:
+ action = data.dossda->openex_act;
+ mode = data.dossda->openex_mode & 0x7F;
+ save_result = true;
+ break;
+ }
+
+ dlog_print("handle_open for ");
+ dlog_fprint(path);
+ dlog_print(" act=");
+ dlog_printx(action);
+ dlog_print(" mode=");
+ dlog_printx(mode);
+ dlog_endline();
+
+ copy_drive_relative_filename(&shflstr.shflstr, path);
+ translate_filename_to_host(&shflstr.shflstr);
+
+ memset(&createparms, 0, sizeof(SHFLCREATEPARMS));
+ if (action & OPENEX_REPLACE_IF_EXISTS) {
+ createparms.CreateFlags |= SHFL_CF_ACT_REPLACE_IF_EXISTS;
+ } else if (action & OPENEX_OPEN_IF_EXISTS) {
+ createparms.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ } else {
+ createparms.CreateFlags |= SHFL_CF_ACT_FAIL_IF_EXISTS;
+ }
+ if (action & OPENEX_CREATE_IF_NEW) {
+ createparms.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW;
+ } else {
+ createparms.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
+ }
+
+ if (mode == OPENEX_MODE_RDWR) {
+ createparms.CreateFlags |= SHFL_CF_ACCESS_READWRITE;
+ } else if (mode == OPENEX_MODE_WRITE) {
+ createparms.CreateFlags |= SHFL_CF_ACCESS_WRITE;
+ } else {
+ createparms.CreateFlags |= SHFL_CF_ACCESS_READ;
+ }
+
+ dlog_print("vbox createparms flags=");
+ dlog_printx(createparms.CreateFlags);
+ dlog_endline();
+
+ err = vbox_shfl_open(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr, &createparms);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ dlog_print("vbox success result=");
+ dlog_printd(createparms.Result);
+ dlog_print(" handle=");
+ print_handle(createparms.Handle);
+ dlog_endline();
+
+ switch (createparms.Result) {
+ case SHFL_PATH_NOT_FOUND:
+ r->x.ax = DOS_ERROR_PATH_NOT_FOUND;
+ r->x.flags |= INTR_CF;
+ return;
+ case SHFL_FILE_NOT_FOUND:
+ r->x.ax = DOS_ERROR_FILE_NOT_FOUND;
+ r->x.flags |= INTR_CF;
+ return;
+ case SHFL_FILE_EXISTS:
+ if (save_result) r->x.cx = OPENEX_FILE_OPENED;
+ break;
+ case SHFL_FILE_CREATED:
+ if (save_result) r->x.cx = OPENEX_FILE_CREATED;
+ break;
+ case SHFL_FILE_REPLACED:
+ if (save_result) r->x.cx = OPENEX_FILE_REPLACED;
+ break;
+ }
+
+ // Fill in the SFT
+ map_shfl_info_to_dossft(sft, &createparms.Info);
+ sft->open_mode = mode;
+ sft->dev_info = 0x8040 | drive; // "Network drive, unwritten to"
+ sft->f_pos = 0;
+ set_sft_vbox_handle(sft, createparms.Handle);
+
+ clear_dos_err(r);
+}
+
+static void handle_close(union INTPACK __far *r)
+{
+ const char __far *path = data.dossda->fn1;
+ int drive = drive_letter_to_index(path[0]);
+ SHFLROOT root = data.drives[drive].root;
+ DOSSFT __far *sft = MK_FP(r->x.es, r->x.di);
+ uint64_t handle = get_sft_vbox_handle(sft);
+ int32_t err;
+
+ dlog_print("handle_close for ");
+ print_handle(handle);
+ dlog_endline();
+
+ err = vbox_shfl_close(&data.vb, data.hgcm_client_id, root, handle);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ clear_dos_err(r);
+}
+
+static void handle_read(union INTPACK __far *r)
+{
+ DOSSFT __far *sft = MK_FP(r->x.es, r->x.di);
+ int drive = sft->dev_info & 0x1F;
+ SHFLROOT root = data.drives[drive].root;
+ uint64_t handle = get_sft_vbox_handle(sft);
+ uint8_t __far *buffer = data.dossda->cur_dta;
+ unsigned long offset = sft->f_pos;
+ unsigned bytes = r->x.cx;
+ int32_t err;
+
+ dlog_print("handle_read handle=");
+ print_handle(handle);
+ dlog_print(" bytes=");
+ dlog_printu(bytes);
+ dlog_endline();
+
+ err = vbox_shfl_read(&data.vb, data.hgcm_client_id, root, handle,
+ offset, &bytes, buffer);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ dlog_print("handle_read bytes_read=");
+ dlog_printu(bytes);
+ dlog_endline();
+
+ // Advance the file position
+ sft->f_pos += bytes;
+
+ r->x.cx = bytes;
+ clear_dos_err(r);
+}
+
+static void handle_write(union INTPACK __far *r)
+{
+ DOSSFT __far *sft = MK_FP(r->x.es, r->x.di);
+ int drive = sft->dev_info & 0x1F;
+ SHFLROOT root = data.drives[drive].root;
+ uint64_t handle = get_sft_vbox_handle(sft);
+ uint8_t __far *buffer = data.dossda->cur_dta;
+ unsigned long offset = sft->f_pos;
+ unsigned bytes = r->x.cx;
+ int32_t err;
+
+ dlog_print("handle_write handle=");
+ print_handle(handle);
+ dlog_print(" bytes=");
+ dlog_printu(bytes);
+ dlog_endline();
+
+ err = vbox_shfl_write(&data.vb, data.hgcm_client_id, root, handle,
+ offset, &bytes, buffer);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ dlog_print("handle_write bytes_written=");
+ dlog_printu(bytes);
+ dlog_endline();
+
+ // Advance the file position
+ sft->f_pos += bytes;
+
+ // Assume the file has grown if we've written past the end
+ if (sft->f_pos > sft->f_size) sft->f_size = sft->f_pos;
+
+ r->x.cx = bytes;
+ clear_dos_err(r);
+}
+
+static int32_t open_search_dir(SHFLROOT root, const char __far *path)
+{
+ int32_t err;
+
+ dlog_puts("open_search_dir");
+
+ copy_drive_relative_dirname(&shflstr.shflstr, path);
+ translate_filename_to_host(&shflstr.shflstr);
+
+ memset(&createparms, 0, sizeof(SHFLCREATEPARMS));
+ createparms.CreateFlags = SHFL_CF_DIRECTORY
+ | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW
+ | SHFL_CF_ACCESS_READ;
+
+ err = vbox_shfl_open(&data.vb, data.hgcm_client_id, root,
+ &shflstr.shflstr, &createparms);
+ if (err) {
+ dlog_puts("open search dir failed");
+ return err;
+ }
+
+ switch (createparms.Result) {
+ case SHFL_PATH_NOT_FOUND:
+ return VERR_PATH_NOT_FOUND;
+ case SHFL_FILE_NOT_FOUND:
+ return VERR_FILE_NOT_FOUND;
+ default:
+ break;
+ }
+
+ data.search_dir_handle = createparms.Handle;
+ return 0;
+}
+
+static int close_search_dir(SHFLROOT root)
+{
+ int32_t err;
+
+ if (data.search_dir_handle == SHFL_HANDLE_NIL) return 0;
+
+ err = vbox_shfl_close(&data.vb, data.hgcm_client_id, root,
+ data.search_dir_handle);
+
+ data.search_dir_handle = SHFL_HANDLE_NIL;
+
+ if (err) {
+ return vbox_err_to_dos(err);
+ }
+
+ return 0;
+}
+
+static int32_t find_volume_label(SHFLROOT root)
+{
+ DOSDIR __far *found_file = &data.dossda->found_file;
+ int32_t err;
+
+ shflstring_clear(&shflstr.shflstr);
+
+ err = vbox_shfl_query_map_name(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr);
+ if (err) return err;
+
+ translate_filename_from_host(&shflstr.shflstr);
+
+ dlog_print("label : ");
+ dlog_fprint(shflstr.buf);
+ dlog_endline();
+
+ copy_to_8_3_filename(found_file->filename, &shflstr.shflstr);
+ found_file->attr = _A_VOLID | _A_HIDDEN;
+
+ return 0;
+}
+
+static int32_t find_next_from_vbox(SHFLROOT root, uint8_t search_attr)
+{
+ DOSDIR __far *found_file = &data.dossda->found_file;
+ int32_t err;
+
+ shfldirinfo.dirinfo.name.u16Size = sizeof(shfldirinfo.buf);
+
+ while (1) { // Loop until we have a valid file (or an error)
+ unsigned size = sizeof(shfldirinfo), resume = 0, count = 0;
+ dlog_puts("calling vbox list");
+
+ err = vbox_shfl_list(&data.vb, data.hgcm_client_id, root, data.search_dir_handle,
+ SHFL_LIST_RETURN_ONE, &size, &shflstr.shflstr, &shfldirinfo.dirinfo,
+ &resume, &count);
+ if (err) {
+ return err;
+ }
+
+ if (count != 1) {
+ return VERR_IO_BAD_LENGTH;
+ }
+
+ dlog_print("got diritem name=");
+ dlog_fprint(shfldirinfo.dirinfo.name.ach);
+ dlog_print(" sfnLen=");
+ dlog_printu(shfldirinfo.dirinfo.cucShortName);
+ dlog_endline();
+
+ // TODO Use the short filename if available from a windows host
+
+ translate_filename_from_host(&shfldirinfo.dirinfo.name);
+
+ if (!copy_to_8_3_filename(found_file->filename, &shfldirinfo.dirinfo.name)) {
+ dlog_puts("hiding file with long filename");
+ continue;
+ }
+ map_shfl_info_to_dosdir(found_file, &shfldirinfo.dirinfo.Info);
+
+ if (found_file->attr & ~search_attr) {
+ continue; // Skip this one
+ }
+
+ break;
+ };
+
+ return 0;
+}
+
+static void handle_find(union INTPACK __far *r)
+{
+ const char __far *path = data.dossda->fn1;
+ int drive = drive_letter_to_index(path[0]);
+ SHFLROOT root = data.drives[drive].root;
+ DOSDIR __far *found_file = &data.dossda->found_file;
+ uint8_t search_attr;
+ int32_t err;
+
+ if (r->h.al == DOS_FN_FIND_FIRST) {
+ search_attr = data.dossda->search_attr;
+
+ dlog_print("find_first path=");
+ dlog_fprint(path);
+ dlog_print(" attr=");
+ dlog_printx(search_attr);
+ dlog_endline();
+
+ close_search_dir(root);
+ err = open_search_dir(root, path);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ copy_drive_relative_filename(&shflstr.shflstr, path);
+ translate_filename_to_host(&shflstr.shflstr);
+ fix_wildcards(&shflstr.shflstr);
+
+ data.dossda->sdb.drive_letter = 0x80 | drive;
+ data.dossda->sdb.search_attr = search_attr;
+ _fmemset(data.dossda->sdb.search_templ, ' ', 8+3);
+ data.dossda->sdb.dir_entry = 0;
+ data.dossda->sdb.par_clstr = 0;
+
+ if (search_attr & _A_VOLID) {
+ // Simulate an initial entry: volume label
+ dlog_puts("search volid");
+ // DOS wants volume label; optimize that
+ err = find_volume_label(root);
+ if (err) {
+ dlog_puts("search volid err");
+ set_vbox_err(r, err);
+ return;
+ }
+ dlog_puts("search volid OK");
+ clear_dos_err(r);
+ return;
+ }
+ } else {
+ search_attr = data.dossda->sdb.search_attr;
+
+ dlog_print("find_next");
+ dlog_print(" attr=");
+ dlog_printx(search_attr);
+ dlog_endline();
+ }
+
+ found_file->attr = 0;
+
+ err = find_next_from_vbox(root, search_attr);
+ if (err) {
+ if (err == VERR_NO_MORE_FILES) {
+ dlog_puts("no more files");
+ }
+ close_search_dir(root);
+ set_vbox_err(r, err);
+ return;
+ }
+
+ dlog_print("accepted file name='");
+ dlog_fnprint(&found_file->filename[0], 8);
+ dlog_putc(' ');
+ dlog_fnprint(&found_file->filename[8], 3);
+ dlog_print("' attr=");
+ dlog_printx(found_file->attr);
+ dlog_endline();
+
+ clear_dos_err(r);
+}
+
+static void handle_chdir(union INTPACK __far *r)
+{
+ const char __far *path = data.dossda->fn1;
+ int drive = drive_letter_to_index(path[0]);
+ SHFLROOT root = data.drives[drive].root;
+ int32_t err;
+
+ dlog_print("handle_chdir to ");
+ dlog_fprint(path);
+ dlog_endline();
+
+ // Just have to check if the directory exists
+ copy_drive_relative_filename(&shflstr.shflstr, path);
+ translate_filename_to_host(&shflstr.shflstr);
+
+ memset(&createparms, 0, sizeof(SHFLCREATEPARMS));
+ createparms.CreateFlags = SHFL_CF_LOOKUP;
+
+ err = vbox_shfl_open(&data.vb, data.hgcm_client_id, root,
+ &shflstr.shflstr, &createparms);
+ if (err) {
+ set_vbox_err(r, err);
+ return;
+ }
+
+ switch (createparms.Result) {
+ case SHFL_PATH_NOT_FOUND:
+ case SHFL_FILE_NOT_FOUND:
+ set_dos_err(r, DOS_ERROR_PATH_NOT_FOUND);
+ return;
+ default:
+ break;
+ }
+
+ // Also check whether it is really a directory
+ if (!(map_shfl_attr_to_dosattr(&createparms.Info.Attr) & _A_SUBDIR)) {
+ set_dos_err(r, DOS_ERROR_PATH_NOT_FOUND);
+ return;
+ }
+
+ clear_dos_err(r);
+}
+
+static bool int2f_11_handler(union INTPACK r)
+#pragma aux int2f_11_handler "*" parm caller [] value [al] modify [ax bx cx dx si di es gs fs]
+{
+ if (r.h.ah != 0x11) return false; // Only interested in network redirector functions
+ if (r.h.al == 0xff && r.x.bx == 0x5742 && r.x.cx == 0x5346) {
+ // These are the magic numbers to our private "Get TSR data" function
+ dlog_puts("Get TSR data");
+ r.x.es = get_ds();
+ r.x.di = FP_OFF(&data);
+ r.x.bx = 0x5444;
+ r.x.cx = 1;
+ return true;
+ }
+
+ dlog_print("2f al=");
+ dlog_printx(r.h.al);
+ dlog_endline();
+
+ if (!is_call_for_mounted_drive(&r)) {
+ return false;
+ }
+
+ switch (r.h.al) {
+ case DOS_FN_CLOSE:
+ handle_close(&r);
+ return true;
+ case DOS_FN_CREATE:
+ case DOS_FN_OPEN:
+ case DOS_FN_OPEN_EX:
+ handle_create_open_ex(&r);
+ return true;
+ case DOS_FN_READ:
+ handle_read(&r);
+ return true;
+ case DOS_FN_WRITE:
+ handle_write(&r);
+ return true;
+ case DOS_FN_FIND_FIRST:
+ case DOS_FN_FIND_NEXT:
+ handle_find(&r);
+ return true;
+ case DOS_FN_CHDIR:
+ handle_chdir(&r);
+ return true;
+ case DOS_FN_GET_DISK_FREE:
+ // We don't support this
+ set_dos_err(&r, DOS_ERROR_INVALID_FUNCTION);
+ return true;
+ }
+
+ return false;
+}
+
+void __declspec(naked) __far int2f_isr(void)
+{
+ __asm {
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov bp, sp
+ push cs
+ pop ds
+
+ call int2f_11_handler
+ test al, al
+ jnz handled
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ ; Jump to the next handler in the chain
+ jmp dword ptr cs:[data + 0] ; wasm doesn't support structs, this is data.prev_int2f_handler
+
+ handled:
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+ iret
+ }
+}
+
+static LPTSRDATA int2f_get_tsr_data(void);
+#pragma aux int2f_get_tsr_data = \
+ "mov ax, 0x11ff" \
+ "mov bx, 0x5742" /* Add magic numbers */ \
+ "mov cx, 0x5346" \
+ "int 0x2f" \
+ "cmp bx, 0x5444" /* Test output magic number */ \
+ "jne fail" \
+ "cmp cx, 1" \
+ "jne fail" \
+ "jmp end" \
+ "fail:" \
+ "xor ax, ax" \
+ "mov es, ax" \
+ "mov di, ax" \
+ "end:" \
+ __value [es di] \
+ __modify [ax bx cx]
+
+LPTSRDATA __far get_tsr_data(bool installed)
+{
+ if (installed) {
+ return int2f_get_tsr_data();
+ } else {
+ return MK_FP(get_cs(), FP_OFF(&data));
+ }
+}
+
+int resident_end;
diff --git a/sftsr.h b/sftsr.h
new file mode 100644
index 0000000..bdd819e
--- /dev/null
+++ b/sftsr.h
@@ -0,0 +1,54 @@
+#ifndef SFTSR_H
+#define SFTSR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "vbox.h"
+#include "int21dos.h"
+
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 1
+
+#define LASTDRIVE 'Z'
+#define MAX_NUM_DRIVE (LASTDRIVE - 'A')
+#define NUM_DRIVES (MAX_NUM_DRIVE + 1)
+
+typedef struct tsrdata {
+ // TSR installation data
+ /** Previous int2f ISR, storing it for uninstall. */
+ void (__interrupt __far *prev_int2f_handler)();
+
+ /** Array of all possible DOS drives. */
+ struct {
+ /** VirtualBox "root" for this drive, or NIL if unmounted. */
+ uint32_t root;
+ } drives[NUM_DRIVES];
+
+ /** Stored pointer for the DOS SDA. */
+ DOSSDA __far *dossda;
+
+ /** Handle for the directory we are currently searching in. */
+ uint64_t search_dir_handle;
+
+ struct vboxcomm vb;
+ uint32_t hgcm_client_id;
+} TSRDATA;
+
+typedef TSRDATA * PTSRDATA;
+typedef TSRDATA __far * LPTSRDATA;
+
+extern void __declspec(naked) __far int2f_isr(void);
+
+extern LPTSRDATA __far get_tsr_data(bool installed);
+
+/** This symbol is always at the end of the TSR segment */
+extern int resident_end;
+
+/** This is not just data, but the entire segment. */
+static inline unsigned get_resident_size(void)
+{
+ return FP_OFF(&resident_end);
+}
+
+#endif // SFTSR_H
diff --git a/utils.h b/utils.h
index 05b4e36..3d99d70 100644
--- a/utils.h
+++ b/utils.h
@@ -2,6 +2,9 @@
#define UTILS_H
#include <stdint.h>
+#include <i86.h>
+
+#define STATIC_ASSERT(expr) typedef int STATIC_ASSERT_FAILED[(expr) ? 1 : -1]
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
@@ -17,6 +20,13 @@ static inline __segment get_cs(void);
static inline __segment get_ds(void);
#pragma aux get_ds = "mov ax, ds" value [ax] modify exact [];
+/** Converts a far pointer into equivalent linear address.
+ * Note that under protected mode linear != physical (for that, need VDS). */
+static inline uint32_t linear_addr(const void __far * ptr)
+{
+ return ((uint32_t)(FP_SEG(ptr)) << 4) + FP_OFF(ptr);
+}
+
/** Map x linearly from range [0, srcmax] to [0, dstmax].
* Equivalent of (x * dstmax) / srcmax but with 32-bit unsigned precision. */
static unsigned scaleu(unsigned x, unsigned srcmax, unsigned dstmax);
diff --git a/vbmouse.lnk b/vbmouse.lnk
index bc1596f..803d6a3 100644
--- a/vbmouse.lnk
+++ b/vbmouse.lnk
@@ -1,5 +1,5 @@
system dos
-option map=dosmouse.map
+option map=vbmouse.map
# Put the resident text & data first, then the rest of standard classses
order clname RES_CODE
clname FAR_DATA
diff --git a/vbox.c b/vbox.c
index 28fb02d..eb05f1b 100644
--- a/vbox.c
+++ b/vbox.c
@@ -22,9 +22,10 @@
#include <stdint.h>
#include <i86.h>
+#include "dlog.h"
+#include "utils.h"
#include "pci.h"
#include "vds.h"
-#include "dlog.h"
#include "vboxdev.h"
#include "vbox.h"
@@ -103,7 +104,7 @@ int vbox_init_buffer(LPVBOXCOMM vb)
vb->dds.offset = FP_OFF(&vb->buf);
vb->dds.bufferId = 0;
- vb->dds.physicalAddress = vds_ptr_to_linear(&vb->buf);
+ vb->dds.physicalAddress = linear_addr(&vb->buf);
}
return 0;
diff --git a/vbox.h b/vbox.h
index 42b3c39..d85263a 100644
--- a/vbox.h
+++ b/vbox.h
@@ -66,18 +66,23 @@ extern int vbox_init_buffer(LPVBOXCOMM vb);
/** Releases/unlocks buffer, no further use possible. */
extern int vbox_release_buffer(LPVBOXCOMM vb);
+static void vbox_init_req(VMMDevRequestHeader __far *hdr, VMMDevRequestType type, unsigned size)
+{
+ _fmemset(hdr, 0, size);
+
+ hdr->size = size;
+ hdr->version = VMMDEV_REQUEST_HEADER_VERSION;
+ hdr->requestType = type;
+ hdr->rc = VERR_DEV_IO_ERROR; // So that we trigger an error if VirtualBox doesn't actually reply anything
+}
+
/** Lets VirtualBox know that there are VirtualBox Guest Additions on this guest.
* @param osType os installed on this guest. */
-static int vbox_report_guest_info(LPVBOXCOMM vb, uint32_t osType)
+static int32_t vbox_report_guest_info(LPVBOXCOMM vb, uint32_t osType)
{
VMMDevReportGuestInfo __far *req = (void __far *) vb->buf;
- _fmemset(req, 0, sizeof(VMMDevReportGuestInfo));
-
- req->header.size = sizeof(VMMDevReportGuestInfo);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_ReportGuestInfo;
- req->header.rc = -1;
+ vbox_init_req(&req->header, VMMDevReq_ReportGuestInfo, sizeof(VMMDevReportGuestInfo));
req->guestInfo.interfaceVersion = VMMDEV_VERSION;
req->guestInfo.osType = osType;
@@ -87,16 +92,11 @@ static int vbox_report_guest_info(LPVBOXCOMM vb, uint32_t osType)
}
/** Tells VirtualBox whether we want absolute mouse information or not. */
-static int vbox_set_mouse(LPVBOXCOMM vb, bool absolute, bool pointer)
+static int32_t vbox_set_mouse(LPVBOXCOMM vb, bool absolute, bool pointer)
{
VMMDevReqMouseStatus __far *req = (void __far *) vb->buf;
- _fmemset(req, 0, sizeof(VMMDevReqMouseStatus));
-
- req->header.size = sizeof(VMMDevReqMouseStatus);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_SetMouseStatus;
- req->header.rc = -1;
+ vbox_init_req(&req->header, VMMDevReq_SetMouseStatus, sizeof(VMMDevReqMouseStatus));
if (absolute) req->mouseFeatures |= VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
if (pointer) req->mouseFeatures |= VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR;
@@ -108,17 +108,12 @@ static int vbox_set_mouse(LPVBOXCOMM vb, bool absolute, bool pointer)
/** Gets the current absolute mouse position from VirtualBox.
* @param abs false if user has disabled mouse integration in VirtualBox,
* in which case we should fallback to PS/2 relative events. */
-static int vbox_get_mouse(LPVBOXCOMM vb, bool __far *abs,
+static int32_t vbox_get_mouse(LPVBOXCOMM vb, bool __far *abs,
uint16_t __far *xpos, uint16_t __far *ypos)
{
VMMDevReqMouseStatus __far *req = (void __far *) vb->buf;
- _fmemset(req, 0, sizeof(VMMDevReqMouseStatus));
-
- req->header.size = sizeof(VMMDevReqMouseStatus);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_GetMouseStatus;
- req->header.rc = -1;
+ vbox_init_req(&req->header, VMMDevReq_GetMouseStatus, sizeof(VMMDevReqMouseStatus));
vbox_send_request(vb->iobase, vb->dds.physicalAddress);
@@ -130,17 +125,11 @@ static int vbox_get_mouse(LPVBOXCOMM vb, bool __far *abs,
}
/** Asks the host to render the mouse cursor for us. */
-static int vbox_set_pointer_visible(LPVBOXCOMM vb, bool visible)
+static int32_t vbox_set_pointer_visible(LPVBOXCOMM vb, bool visible)
{
VMMDevReqMousePointer __far *req = (void __far *) vb->buf;
- _fmemset(req, 0, sizeof(VMMDevReqMousePointer));
-
- req->header.size = sizeof(VMMDevReqMousePointer);
- req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req->header.requestType = VMMDevReq_SetPointerShape;
- req->header.rc = -1;
-
+ vbox_init_req(&req->header, VMMDevReq_SetPointerShape, sizeof(VMMDevReqMousePointer));
if (visible) req->fFlags |= VBOX_MOUSE_POINTER_VISIBLE;
vbox_send_request(vb->iobase, vb->dds.physicalAddress);
@@ -157,6 +146,17 @@ static inline unsigned vbox_req_mouse_pointer_size(unsigned width, unsigned heig
return MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size);
}
+static int32_t vbox_idle(LPVBOXCOMM vb)
+{
+ VMMDevReqIdle __far *req = (void __far *) vb->buf;
+
+ vbox_init_req(&req->header, VMMDevReq_Idle, sizeof(VMMDevReqIdle));
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ return req->header.rc;
+}
+
#pragma pop (unreferenced)
#endif
diff --git a/vboxdev.h b/vboxdev.h
index a9beda1..1b40f8e 100644
--- a/vboxdev.h
+++ b/vboxdev.h
@@ -31,9 +31,100 @@
/* Basic defines required for interoperability with VirtualBox's VMM device */
-#define AssertCompileSize(type, size) /**/
+#pragma pack(push,4)
+
+#define AssertCompile(expr) typedef int STATIC_ASSERT_FAILED[(expr) ? 1 : -1]
+#define AssertCompileSize(type, size) AssertCompile(sizeof(type) == size)
+
#define RT_BIT(bit) ( 1U << (bit) )
+#define VINF_SUCCESS 0
+
+/** General failure - DON'T USE THIS!!! */
+#define VERR_GENERAL_FAILURE (-1)
+/** Invalid parameter. */
+#define VERR_INVALID_PARAMETER (-2)
+/** Invalid parameter. */
+#define VWRN_INVALID_PARAMETER 2
+/** Invalid magic or cookie. */
+#define VERR_INVALID_MAGIC (-3)
+/** Invalid magic or cookie. */
+#define VWRN_INVALID_MAGIC 3
+/** Invalid loader handle. */
+#define VERR_INVALID_HANDLE (-4)
+/** Invalid loader handle. */
+#define VWRN_INVALID_HANDLE 4
+
+#define VERR_FILE_IO_ERROR (-100)
+/** File/Device open failed. */
+#define VERR_OPEN_FAILED (-101)
+/** File not found. */
+#define VERR_FILE_NOT_FOUND (-102)
+/** Path not found. */
+#define VERR_PATH_NOT_FOUND (-103)
+/** Invalid (malformed) file/path name. */
+#define VERR_INVALID_NAME (-104)
+/** The object in question already exists. */
+#define VERR_ALREADY_EXISTS (-105)
+/** The object in question already exists. */
+#define VWRN_ALREADY_EXISTS 105
+/** Too many open files. */
+#define VERR_TOO_MANY_OPEN_FILES (-106)
+/** Seek error. */
+#define VERR_SEEK (-107)
+
+/** @name Generic Directory Enumeration Status Codes
+ * @{
+ */
+/** Unresolved (unknown) search error. */
+#define VERR_SEARCH_ERROR (-200)
+/** No more files found. */
+#define VERR_NO_MORE_FILES (-201)
+/** No more search handles available. */
+#define VERR_NO_MORE_SEARCH_HANDLES (-202)
+/** RTDirReadEx() failed to retrieve the extra data which was requested. */
+#define VWRN_NO_DIRENT_INFO 203
+/** @} */
+
+/** Unresolved (unknown) device i/o error. */
+#define VERR_DEV_IO_ERROR (-250)
+/** Device i/o: Bad unit. */
+#define VERR_IO_BAD_UNIT (-251)
+/** Device i/o: Not ready. */
+#define VERR_IO_NOT_READY (-252)
+/** Device i/o: Bad command. */
+#define VERR_IO_BAD_COMMAND (-253)
+/** Device i/o: CRC error. */
+#define VERR_IO_CRC (-254)
+/** Device i/o: Bad length. */
+#define VERR_IO_BAD_LENGTH (-255)
+/** Device i/o: Sector not found. */
+#define VERR_IO_SECTOR_NOT_FOUND (-256)
+/** Device i/o: General failure. */
+#define VERR_IO_GEN_FAILURE (-257)
+
+/** Requested service does not exist. */
+#define VERR_HGCM_SERVICE_NOT_FOUND (-2900)
+/** Service rejected client connection */
+#define VINF_HGCM_CLIENT_REJECTED 2901
+/** Command address is invalid. */
+#define VERR_HGCM_INVALID_CMD_ADDRESS (-2902)
+/** Service will execute the command in background. */
+#define VINF_HGCM_ASYNC_EXECUTE 2903
+/** HGCM could not perform requested operation because of an internal error. */
+#define VERR_HGCM_INTERNAL (-2904)
+/** Invalid HGCM client id. */
+#define VERR_HGCM_INVALID_CLIENT_ID (-2905)
+/** The HGCM is saving state. */
+#define VINF_HGCM_SAVE_STATE (2906)
+/** Requested service already exists. */
+#define VERR_HGCM_SERVICE_EXISTS (-2907)
+/** Too many clients for the service. */
+#define VERR_HGCM_TOO_MANY_CLIENTS (-2908)
+/** Too many calls to the service from a client. */
+#define VERR_HGCM_TOO_MANY_CLIENT_CALLS (-2909)
+
+
#define VMMDEV_VERSION 0x00010004UL
/** Version of VMMDevRequestHeader structure. */
@@ -255,6 +346,12 @@ typedef enum VMMDevRequestType
VMMDevReq_ReportGuestInfo2 = 58, /**< @since version 3.2.0 */
VMMDevReq_ReportGuestStatus = 59, /**< @since version 3.2.8 */
VMMDevReq_ReportGuestUserState = 74, /**< @since version 4.3 */
+
+ VMMDevReq_HGCMConnect = 60,
+ VMMDevReq_HGCMDisconnect = 61,
+ VMMDevReq_HGCMCall32 = 62,
+ VMMDevReq_HGCMCall64 = 63,
+
VMMDevReq_SizeHack = 0x7fffffff
} VMMDevRequestType;
@@ -505,4 +602,743 @@ typedef struct VBoxGuestInfo2
} VBoxGuestInfo2;
AssertCompileSize(VBoxGuestInfo2, 144);
+/**
+ * Idle request structure.
+ *
+ * Used by VMMDevReq_Idle.
+ */
+typedef struct
+{
+ /** Header. */
+ VMMDevRequestHeader header;
+} VMMDevReqIdle;
+AssertCompileSize(VMMDevReqIdle, 24);
+
+// HGCM
+
+typedef uint32_t RTGCPHYS32;
+typedef uint32_t RTGCPTR32;
+typedef uint64_t RTGCPHYS64;
+
+/**
+ * HGCM service location types.
+ * @ingroup grp_vmmdev_req
+ */
+typedef enum
+{
+ VMMDevHGCMLoc_Invalid = 0,
+ VMMDevHGCMLoc_LocalHost = 1,
+ VMMDevHGCMLoc_LocalHost_Existing = 2,
+ VMMDevHGCMLoc_SizeHack = 0x7fffffff
+} HGCMServiceLocationType;
+AssertCompileSize(HGCMServiceLocationType, 4);
+
+/**
+ * HGCM host service location.
+ * @ingroup grp_vmmdev_req
+ */
+typedef struct
+{
+ char achName[128]; /**< This is really szName. */
+} HGCMServiceLocationHost;
+AssertCompileSize(HGCMServiceLocationHost, 128);
+
+/**
+ * HGCM service location.
+ * @ingroup grp_vmmdev_req
+ */
+typedef struct HGCMSERVICELOCATION
+{
+ /** Type of the location. */
+ HGCMServiceLocationType type;
+
+ union
+ {
+ HGCMServiceLocationHost host;
+ } u;
+} HGCMServiceLocation;
+AssertCompileSize(HGCMServiceLocation, 128+4);
+
+
+/**
+ * HGCM parameter type.
+ */
+typedef enum
+{
+ VMMDevHGCMParmType_Invalid = 0,
+ VMMDevHGCMParmType_32bit = 1,
+ VMMDevHGCMParmType_64bit = 2,
+ VMMDevHGCMParmType_PhysAddr = 3, /**< @deprecated Doesn't work, use PageList. */
+ VMMDevHGCMParmType_LinAddr = 4, /**< In and Out */
+ VMMDevHGCMParmType_LinAddr_In = 5, /**< In (read; host<-guest) */
+ VMMDevHGCMParmType_LinAddr_Out = 6, /**< Out (write; host->guest) */
+ VMMDevHGCMParmType_LinAddr_Locked = 7, /**< Locked In and Out - for VBoxGuest, not host. */
+ VMMDevHGCMParmType_LinAddr_Locked_In = 8, /**< Locked In (read; host<-guest) - for VBoxGuest, not host. */
+ VMMDevHGCMParmType_LinAddr_Locked_Out = 9, /**< Locked Out (write; host->guest) - for VBoxGuest, not host. */
+ VMMDevHGCMParmType_PageList = 10, /**< Physical addresses of locked pages for a buffer. */
+ VMMDevHGCMParmType_Embedded = 11, /**< Small buffer embedded in request. */
+ VMMDevHGCMParmType_ContiguousPageList = 12, /**< Like PageList but with physically contiguous memory, so only one page entry. */
+ VMMDevHGCMParmType_NoBouncePageList = 13, /**< Like PageList but host function requires no bounce buffering. */
+ VMMDevHGCMParmType_SizeHack = 0x7fffffff
+} HGCMFunctionParameterType;
+AssertCompileSize(HGCMFunctionParameterType, 4);
+
+typedef struct
+{
+ HGCMFunctionParameterType type;
+ union
+ {
+ uint32_t value32;
+ uint64_t value64;
+ struct
+ {
+ uint32_t size;
+
+ union
+ {
+ RTGCPHYS32 physAddr;
+ RTGCPTR32 linearAddr;
+ } u;
+ } Pointer;
+ struct
+ {
+ uint32_t cb;
+ RTGCPTR32 uAddr;
+ } LinAddr; /**< Shorter version of the above Pointer structure. */
+ struct
+ {
+ uint32_t size; /**< Size of the buffer described by the page list. */
+ uint32_t offset; /**< Relative to the request header, valid if size != 0. */
+ } PageList;
+ struct
+ {
+ uint32_t fFlags : 8; /**< VBOX_HGCM_F_PARM_*. */
+ uint32_t offData : 24; /**< Relative to the request header (must be a valid offset even if cbData is zero). */
+ uint32_t cbData; /**< The buffer size. */
+ } Embedded;
+ } u;
+} HGCMFunctionParameter;
+AssertCompileSize(HGCMFunctionParameter, 4+8);
+
+#define VBOX_HGCM_REQ_DONE 0x1
+#define VBOX_HGCM_REQ_CANCELLED 0x2
+
+/**
+ * HGCM request header.
+ */
+typedef struct VMMDevHGCMRequestHeader
+{
+ /** Request header. */
+ VMMDevRequestHeader header;
+
+ /** HGCM flags. */
+ uint32_t fu32Flags;
+
+ /** Result code. */
+ int32_t result;
+} VMMDevHGCMRequestHeader;
+AssertCompileSize(VMMDevHGCMRequestHeader, 24+8);
+
+/**
+ * HGCM connect request structure.
+ *
+ * Used by VMMDevReq_HGCMConnect.
+ */
+typedef struct
+{
+ /** HGCM request header. */
+ VMMDevHGCMRequestHeader header;
+
+ /** IN: Description of service to connect to. */
+ HGCMServiceLocation loc;
+
+ /** OUT: Client identifier assigned by local instance of HGCM. */
+ uint32_t u32ClientID;
+} VMMDevHGCMConnect;
+AssertCompileSize(VMMDevHGCMConnect, 32+132+4);
+
+/**
+ * HGCM disconnect request structure.
+ *
+ * Used by VMMDevReq_HGCMDisconnect.
+ */
+typedef struct
+{
+ /** HGCM request header. */
+ VMMDevHGCMRequestHeader header;
+
+ /** IN: Client identifier. */
+ uint32_t u32ClientID;
+} VMMDevHGCMDisconnect;
+AssertCompileSize(VMMDevHGCMDisconnect, 32+4);
+
+/**
+ * HGCM call request structure.
+ *
+ * Used by VMMDevReq_HGCMCall32 and VMMDevReq_HGCMCall64.
+ */
+typedef struct
+{
+ /* request header */
+ VMMDevHGCMRequestHeader header;
+
+ /** IN: Client identifier. */
+ uint32_t u32ClientID;
+ /** IN: Service function number. */
+ uint32_t u32Function;
+ /** IN: Number of parameters. */
+ uint32_t cParms;
+ /** Parameters follow in form: HGCMFunctionParameter aParms[X]; */
+ HGCMFunctionParameter aParms[];
+} VMMDevHGCMCall;
+AssertCompileSize(VMMDevHGCMCall, 32+12);
+
+/** @name Direction of data transfer (HGCMPageListInfo::flags). Bit flags.
+ * @{ */
+#define VBOX_HGCM_F_PARM_DIRECTION_NONE 0x00000000UL
+#define VBOX_HGCM_F_PARM_DIRECTION_TO_HOST 0x00000001UL
+#define VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST 0x00000002UL
+#define VBOX_HGCM_F_PARM_DIRECTION_BOTH 0x00000003UL
+#define VBOX_HGCM_F_PARM_DIRECTION_MASK 0x00000003UL
+/** Macro for validating that the specified flags are valid. */
+#define VBOX_HGCM_F_PARM_ARE_VALID(fFlags) \
+ ( ((fFlags) & VBOX_HGCM_F_PARM_DIRECTION_MASK) \
+ && !((fFlags) & ~VBOX_HGCM_F_PARM_DIRECTION_MASK) )
+/** @} */
+
+/**
+ * VMMDevHGCMParmType_PageList points to this structure to actually describe the
+ * buffer.
+ */
+typedef struct
+{
+ uint32_t flags; /**< VBOX_HGCM_F_PARM_*. */
+ uint16_t offFirstPage; /**< Offset in the first page where data begins. */
+ uint16_t cPages; /**< Number of pages. */
+ RTGCPHYS64 aPages[1]; /**< Page addresses. */
+} HGCMPageListInfo;
+AssertCompileSize(HGCMPageListInfo, 4+2+2+8);
+
+# define VBOX_HGCM_MAX_PARMS 32
+
+/**
+ * HGCM cancel request structure.
+ *
+ * The Cancel request is issued using the same physical memory address as was
+ * used for the corresponding initial HGCMCall.
+ *
+ * Used by VMMDevReq_HGCMCancel.
+ */
+typedef struct
+{
+ /** Header. */
+ VMMDevHGCMRequestHeader header;
+} VMMDevHGCMCancel;
+AssertCompileSize(VMMDevHGCMCancel, 32);
+
+
+// SHARED FOLDERS (shfl)
+
+/** @name Shared Folders service functions. (guest)
+ * @{
+ */
+/** Query mappings changes.
+ * @note Description is currently misleading, it will always return all
+ * current mappings with SHFL_MS_NEW status. Only modification is the
+ * SHFL_MF_AUTOMOUNT flag that causes filtering out non-auto mounts. */
+#define SHFL_FN_QUERY_MAPPINGS (1)
+/** Query the name of a map. */
+#define SHFL_FN_QUERY_MAP_NAME (2)
+/** Open/create object. */
+#define SHFL_FN_CREATE (3)
+/** Close object handle. */
+#define SHFL_FN_CLOSE (4)
+/** Read object content. */
+#define SHFL_FN_READ (5)
+/** Write new object content. */
+#define SHFL_FN_WRITE (6)
+/** Lock/unlock a range in the object. */
+#define SHFL_FN_LOCK (7)
+/** List object content. */
+#define SHFL_FN_LIST (8)
+/** Query/set object information. */
+#define SHFL_FN_INFORMATION (9)
+/** Remove object */
+#define SHFL_FN_REMOVE (11)
+/** Map folder (legacy) */
+#define SHFL_FN_MAP_FOLDER_OLD (12)
+/** Unmap folder */
+#define SHFL_FN_UNMAP_FOLDER (13)
+/** Rename object (possibly moving it to another directory) */
+#define SHFL_FN_RENAME (14)
+/** Flush file */
+#define SHFL_FN_FLUSH (15)
+/** @todo macl, a description, please. */
+#define SHFL_FN_SET_UTF8 (16)
+/** Map folder */
+#define SHFL_FN_MAP_FOLDER (17)
+/** Read symlink destination.
+ * @since VBox 4.0 */
+#define SHFL_FN_READLINK (18) /**< @todo rename to SHFL_FN_READ_LINK (see struct capitalization) */
+/** Create symlink.
+ * @since VBox 4.0 */
+#define SHFL_FN_SYMLINK (19)
+/** Ask host to show symlinks
+ * @since VBox 4.0 */
+#define SHFL_FN_SET_SYMLINKS (20)
+/** Query information about a map.
+ * @since VBox 6.0 */
+#define SHFL_FN_QUERY_MAP_INFO (21)
+/** Wait for changes to the mappings.
+ * @since VBox 6.0 */
+#define SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES (22)
+/** Cancel all waits for changes to the mappings for the calling client.
+ * The wait calls will return VERR_CANCELLED.
+ * @since VBox 6.0 */
+#define SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS (23)
+/** Sets the file size.
+ * @since VBox 6.0 */
+#define SHFL_FN_SET_FILE_SIZE (24)
+/** Queries supported features.
+ * @since VBox 6.0.6 */
+#define SHFL_FN_QUERY_FEATURES (25)
+/** Copies a file to another.
+ * @since VBox 6.0.6 */
+#define SHFL_FN_COPY_FILE (26)
+/** Copies part of a file to another.
+ * @since VBox 6.0.6 */
+#define SHFL_FN_COPY_FILE_PART (27)
+/** Close handle to (optional) and remove object by path.
+ * This function is tailored for Windows guests.
+ * @since VBox 6.0.8 */
+#define SHFL_FN_CLOSE_AND_REMOVE (28)
+/** Set the host error code style.
+ * This is for more efficiently getting the correct error status when the host
+ * and guest OS types differs and it won't happen naturally.
+ * @since VBox 6.0.10 */
+#define SHFL_FN_SET_ERROR_STYLE (29)
+/** The last function number. */
+#define SHFL_FN_LAST SHFL_FN_SET_ERROR_STYLE
+/** @} */
+
+
+/** @name Shared Folders service functions. (host)
+ * @{
+ */
+/** Add shared folder mapping. */
+#define SHFL_FN_ADD_MAPPING (1)
+/** Remove shared folder mapping. */
+#define SHFL_FN_REMOVE_MAPPING (2)
+/** Set the led status light address. */
+#define SHFL_FN_SET_STATUS_LED (3)
+/** Allow the guest to create symbolic links
+ * @since VBox 4.0 */
+#define SHFL_FN_ALLOW_SYMLINKS_CREATE (4)
+/** @} */
+
+/** Root handle for a mapping. Root handles are unique.
+ *
+ * @note Function parameters structures consider the root handle as 32 bit
+ * value. If the typedef will be changed, then function parameters must be
+ * changed accordingly. All those parameters are marked with SHFLROOT in
+ * comments.
+ */
+typedef uint32_t SHFLROOT;
+
+/** NIL shared folder root handle. */
+#define SHFL_ROOT_NIL ((SHFLROOT)~0)
+
+
+/** A shared folders handle for an opened object. */
+typedef uint64_t SHFLHANDLE;
+
+#define SHFL_HANDLE_NIL ((SHFLHANDLE)~0LL)
+#define SHFL_HANDLE_ROOT ((SHFLHANDLE)0LL)
+
+/** Hardcoded maximum length (in chars) of a shared folder name. */
+#define SHFL_MAX_LEN (256)
+/** Hardcoded maximum number of shared folder mapping available to the guest. */
+#define SHFL_MAX_MAPPINGS (64)
+
+/**
+ * Shared folder string buffer structure.
+ */
+typedef struct _SHFLSTRING
+{
+ /** Allocated size of the String member in bytes. */
+ uint16_t u16Size;
+
+ /** Length of string without trailing nul in bytes. */
+ uint16_t u16Length;
+
+ /** UTF-8 or UTF-16 string. Nul terminated. */
+ char ach[];
+} SHFLSTRING;
+AssertCompileSize(SHFLSTRING, 4);
+
+/**
+ * The available additional information in a SHFLFSOBJATTR object.
+ */
+typedef enum SHFLFSOBJATTRADD
+{
+ /** No additional information is available / requested. */
+ SHFLFSOBJATTRADD_NOTHING = 1,
+ /** The additional unix attributes (SHFLFSOBJATTR::u::Unix) are
+ * available / requested. */
+ SHFLFSOBJATTRADD_UNIX,
+ /** The additional extended attribute size (SHFLFSOBJATTR::u::EASize) is
+ * available / requested. */
+ SHFLFSOBJATTRADD_EASIZE,
+ /** The last valid item (inclusive).
+ * The valid range is SHFLFSOBJATTRADD_NOTHING thru
+ * SHFLFSOBJATTRADD_LAST. */
+ SHFLFSOBJATTRADD_LAST = SHFLFSOBJATTRADD_EASIZE,
+
+ /** The usual 32-bit hack. */
+ SHFLFSOBJATTRADD_32BIT_SIZE_HACK = 0x7fffffff
+} SHFLFSOBJATTRADD;
+
+
+/* Assert sizes of the IRPT types we're using below. */
+//AssertCompileSize(RTFMODE, 4);
+//AssertCompileSize(RTFOFF, 8);
+//AssertCompileSize(RTINODE, 8);
+//AssertCompileSize(RTTIMESPEC, 8);
+//AssertCompileSize(RTDEV, 4);
+//AssertCompileSize(RTUID, 4);
+
+/**
+ * Shared folder filesystem object attributes.
+ */
+#pragma pack(push, 1)
+typedef struct SHFLFSOBJATTR
+{
+ /** Mode flags (st_mode). RTFS_UNIX_*, RTFS_TYPE_*, and RTFS_DOS_*.
+ * @remarks We depend on a number of RTFS_ defines to remain unchanged.
+ * Fortuntately, these are depending on windows, dos and unix
+ * standard values, so this shouldn't be much of a pain. */
+ uint32_t fMode;
+
+ /** The additional attributes available. */
+ SHFLFSOBJATTRADD enmAdditional;
+
+ /**
+ * Additional attributes.
+ *
+ * Unless explicitly specified to an API, the API can provide additional
+ * data as it is provided by the underlying OS.
+ */
+ union SHFLFSOBJATTRUNION
+ {
+ /** Additional Unix Attributes
+ * These are available when SHFLFSOBJATTRADD is set in fUnix.
+ */
+ struct SHFLFSOBJATTRUNIX
+ {
+ /** The user owning the filesystem object (st_uid).
+ * This field is ~0U if not supported. */
+ uint32_t uid;
+
+ /** The group the filesystem object is assigned (st_gid).
+ * This field is ~0U if not supported. */
+ uint32_t gid;
+
+ /** Number of hard links to this filesystem object (st_nlink).
+ * This field is 1 if the filesystem doesn't support hardlinking or
+ * the information isn't available.
+ */
+ uint32_t cHardlinks;
+
+ /** The device number of the device which this filesystem object resides on (st_dev).
+ * This field is 0 if this information is not available. */
+ uint32_t INodeIdDevice;
+
+ /** The unique identifier (within the filesystem) of this filesystem object (st_ino).
+ * Together with INodeIdDevice, this field can be used as a OS wide unique id
+ * when both their values are not 0.
+ * This field is 0 if the information is not available. */
+ uint64_t INodeId;
+
+ /** User flags (st_flags).
+ * This field is 0 if this information is not available. */
+ uint32_t fFlags;
+
+ /** The current generation number (st_gen).
+ * This field is 0 if this information is not available. */
+ uint32_t GenerationId;
+
+ /** The device number of a character or block device type object (st_rdev).
+ * This field is 0 if the file isn't of a character or block device type and
+ * when the OS doesn't subscribe to the major+minor device idenfication scheme. */
+ uint32_t Device;
+ } Unix;
+
+ /**
+ * Extended attribute size.
+ */
+ struct SHFLFSOBJATTREASIZE
+ {
+ /** Size of EAs. */
+ uint64_t cb;
+ } EASize;
+ } u;
+} SHFLFSOBJATTR;
+#pragma pack (pop)
+AssertCompileSize(SHFLFSOBJATTR, 44);
+/** Pointer to a shared folder filesystem object attributes structure. */
+typedef SHFLFSOBJATTR *PSHFLFSOBJATTR;
+/** Pointer to a const shared folder filesystem object attributes structure. */
+typedef const SHFLFSOBJATTR *PCSHFLFSOBJATTR;
+
+
+/**
+ * Filesystem object information structure.
+ */
+#pragma pack(push, 1)
+typedef struct SHFLFSOBJINFO
+{
+ /** Logical size (st_size).
+ * For normal files this is the size of the file.
+ * For symbolic links, this is the length of the path name contained
+ * in the symbolic link.
+ * For other objects this fields needs to be specified.
+ */
+ uint64_t cbObject;
+
+ /** Disk allocation size (st_blocks * DEV_BSIZE). */
+ uint64_t cbAllocated;
+
+ /** Time of last access (st_atime).
+ * @remarks Here (and other places) we depend on the IPRT timespec to
+ * remain unchanged. */
+ uint64_t AccessTime;
+
+ /** Time of last data modification (st_mtime). */
+ uint64_t ModificationTime;
+
+ /** Time of last status change (st_ctime).
+ * If not available this is set to ModificationTime.
+ */
+ uint64_t ChangeTime;
+
+ /** Time of file birth (st_birthtime).
+ * If not available this is set to ChangeTime.
+ */
+ uint64_t BirthTime;
+
+ /** Attributes. */
+ SHFLFSOBJATTR Attr;
+
+} SHFLFSOBJINFO;
+#pragma pack (pop)
+AssertCompileSize(SHFLFSOBJINFO, 92);
+/** Pointer to a shared folder filesystem object information structure. */
+typedef SHFLFSOBJINFO *PSHFLFSOBJINFO;
+/** Pointer to a const shared folder filesystem object information
+ * structure. */
+typedef const SHFLFSOBJINFO *PCSHFLFSOBJINFO;
+
+
+/** Result of an open/create request.
+ * Along with handle value the result code
+ * identifies what has happened while
+ * trying to open the object.
+ */
+typedef enum _SHFLCREATERESULT
+{
+ SHFL_NO_RESULT,
+ /** Specified path does not exist. */
+ SHFL_PATH_NOT_FOUND,
+ /** Path to file exists, but the last component does not. */
+ SHFL_FILE_NOT_FOUND,
+ /** File already exists and either has been opened or not. */
+ SHFL_FILE_EXISTS,
+ /** New file was created. */
+ SHFL_FILE_CREATED,
+ /** Existing file was replaced or overwritten. */
+ SHFL_FILE_REPLACED,
+ /** Blow the type up to 32-bit. */
+ SHFL_32BIT_HACK = 0x7fffffff
+} SHFLCREATERESULT;
+AssertCompile(SHFL_NO_RESULT == 0);
+AssertCompileSize(SHFLCREATERESULT, 4);
+
+
+/** @name Open/create flags.
+ * @{
+ */
+
+/** No flags. Initialization value. */
+#define SHFL_CF_NONE (0x00000000)
+
+/** Lookup only the object, do not return a handle. All other flags are ignored. */
+#define SHFL_CF_LOOKUP (0x00000001)
+
+/** Open parent directory of specified object.
+ * Useful for the corresponding Windows FSD flag
+ * and for opening paths like \\dir\\*.* to search the 'dir'.
+ * @todo possibly not needed???
+ */
+#define SHFL_CF_OPEN_TARGET_DIRECTORY (0x00000002)
+
+/** Create/open a directory. */
+#define SHFL_CF_DIRECTORY (0x00000004)
+
+/** Open/create action to do if object exists
+ * and if the object does not exists.
+ * REPLACE file means atomically DELETE and CREATE.
+ * OVERWRITE file means truncating the file to 0 and
+ * setting new size.
+ * When opening an existing directory REPLACE and OVERWRITE
+ * actions are considered invalid, and cause returning
+ * FILE_EXISTS with NIL handle.
+ */
+#define SHFL_CF_ACT_MASK_IF_EXISTS (0x000000F0)
+#define SHFL_CF_ACT_MASK_IF_NEW (0x00000F00)
+
+/** What to do if object exists. */
+#define SHFL_CF_ACT_OPEN_IF_EXISTS (0x00000000)
+#define SHFL_CF_ACT_FAIL_IF_EXISTS (0x00000010)
+#define SHFL_CF_ACT_REPLACE_IF_EXISTS (0x00000020)
+#define SHFL_CF_ACT_OVERWRITE_IF_EXISTS (0x00000030)
+
+/** What to do if object does not exist. */
+#define SHFL_CF_ACT_CREATE_IF_NEW (0x00000000)
+#define SHFL_CF_ACT_FAIL_IF_NEW (0x00000100)
+
+/** Read/write requested access for the object. */
+#define SHFL_CF_ACCESS_MASK_RW (0x00003000)
+
+/** No access requested. */
+#define SHFL_CF_ACCESS_NONE (0x00000000)
+/** Read access requested. */
+#define SHFL_CF_ACCESS_READ (0x00001000)
+/** Write access requested. */
+#define SHFL_CF_ACCESS_WRITE (0x00002000)
+/** Read/Write access requested. */
+#define SHFL_CF_ACCESS_READWRITE (SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_WRITE)
+
+/** Requested share access for the object. */
+#define SHFL_CF_ACCESS_MASK_DENY (0x0000C000)
+
+/** Allow any access. */
+#define SHFL_CF_ACCESS_DENYNONE (0x00000000)
+/** Do not allow read. */
+#define SHFL_CF_ACCESS_DENYREAD (0x00004000)
+/** Do not allow write. */
+#define SHFL_CF_ACCESS_DENYWRITE (0x00008000)
+/** Do not allow access. */
+#define SHFL_CF_ACCESS_DENYALL (SHFL_CF_ACCESS_DENYREAD | SHFL_CF_ACCESS_DENYWRITE)
+
+/** Requested access to attributes of the object. */
+#define SHFL_CF_ACCESS_MASK_ATTR (0x00030000)
+
+/** No access requested. */
+#define SHFL_CF_ACCESS_ATTR_NONE (0x00000000)
+/** Read access requested. */
+#define SHFL_CF_ACCESS_ATTR_READ (0x00010000)
+/** Write access requested. */
+#define SHFL_CF_ACCESS_ATTR_WRITE (0x00020000)
+/** Read/Write access requested. */
+#define SHFL_CF_ACCESS_ATTR_READWRITE (SHFL_CF_ACCESS_ATTR_READ | SHFL_CF_ACCESS_ATTR_WRITE)
+
+/** The file is opened in append mode. Ignored if SHFL_CF_ACCESS_WRITE is not set. */
+#define SHFL_CF_ACCESS_APPEND (0x00040000)
+
+/** @} */
+
+#pragma pack(push,1)
+typedef struct _SHFLCREATEPARMS
+{
+ /* Returned handle of opened object. */
+ SHFLHANDLE Handle;
+
+ /* Returned result of the operation */
+ SHFLCREATERESULT Result;
+
+ /* SHFL_CF_* */
+ uint32_t CreateFlags;
+
+ /* Attributes of object to create and
+ * returned actual attributes of opened/created object.
+ */
+ SHFLFSOBJINFO Info;
+
+} SHFLCREATEPARMS;
+#pragma pack(pop)
+
+/** @name Shared Folders mappings.
+ * @{
+ */
+
+/** The mapping has been added since last query. */
+#define SHFL_MS_NEW (1)
+/** The mapping has been deleted since last query. */
+#define SHFL_MS_DELETED (2)
+
+/** Validation mask. Needs to be adjusted
+ * whenever a new SHFL_MF_ flag is added. */
+#define SHFL_MF_MASK (0x00000011)
+/** UTF-16 enconded strings. */
+#define SHFL_MF_UCS2 (0x00000000)
+/** Guest uses UTF8 strings, if not set then the strings are unicode (UCS2). */
+#define SHFL_MF_UTF8 (0x00000001)
+/** Just handle the auto-mounted folders. */
+#define SHFL_MF_AUTOMOUNT (0x00000010)
+
+typedef struct _SHFLMAPPING
+{
+ /** Mapping status.
+ * @note Currently always set to SHFL_MS_NEW. */
+ uint32_t u32Status;
+ /** Root handle. */
+ SHFLROOT root;
+} SHFLMAPPING;
+AssertCompileSize(SHFLMAPPING, 8);
+/** Pointer to a SHFLMAPPING structure. */
+typedef SHFLMAPPING *PSHFLMAPPING;
+
+/** @} */
+
+/** @name Shared Folder directory information
+ * @{
+ */
+
+typedef struct _SHFLDIRINFO
+{
+ /** Full information about the object. */
+ SHFLFSOBJINFO Info;
+ /** The length of the short field (number of RTUTF16 chars).
+ * It is 16-bit for reasons of alignment. */
+ uint16_t cucShortName;
+ /** The short name for 8.3 compatibility.
+ * Empty string if not available.
+ */
+ uint16_t uszShortName[14];
+
+ SHFLSTRING name;
+} SHFLDIRINFO, *PSHFLDIRINFO;
+
+#define SHFL_LIST_NONE 0
+#define SHFL_LIST_RETURN_ONE 1
+#define SHFL_LIST_RESTART 2
+
+/** Query flag: Guest prefers drive letters as mount points. */
+#define SHFL_MIQF_DRIVE_LETTER RT_BIT(0)
+/** Query flag: Guest prefers paths as mount points. */
+#define SHFL_MIQF_PATH RT_BIT(1)
+
+/** Set if writable. */
+#define SHFL_MIF_WRITABLE RT_BIT(0)
+/** Indicates that the mapping should be auto-mounted. */
+#define SHFL_MIF_AUTO_MOUNT RT_BIT(1)
+/** Set if host is case insensitive. */
+#define SHFL_MIF_HOST_ICASE RT_BIT(2)
+/** Set if guest is case insensitive. */
+#define SHFL_MIF_GUEST_ICASE RT_BIT(3)
+/** Symbolic link creation is allowed. */
+#define SHFL_MIF_SYMLINK_CREATION RT_BIT(4)
+
+#pragma pack (pop)
+
#endif
diff --git a/vboxhgcm.h b/vboxhgcm.h
new file mode 100644
index 0000000..3ccfa28
--- /dev/null
+++ b/vboxhgcm.h
@@ -0,0 +1,86 @@
+/*
+ * VBMouse - VirtualBox's HGCM protocol communication routines
+ * Copyright (C) 2022 Javier S. Pedro
+ *
+ * This program 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; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef VBOXHGCM_H
+#define VBOXHGCM_H
+
+#include "vbox.h"
+#include "vboxdev.h"
+
+typedef uint32_t hgcm_client_id_t;
+
+static void vbox_hgcm_wait(VMMDevHGCMRequestHeader __far * req)
+{
+ volatile uint32_t __far * req_flags = &req->fu32Flags;
+
+ while (!(*req_flags & VBOX_HGCM_REQ_DONE)) {
+ // TODO yield somehow?
+ }
+}
+
+static int32_t vbox_hgcm_connect_existing(LPVBOXCOMM vb, const char *service, hgcm_client_id_t __far *client_id)
+{
+ VMMDevHGCMConnect __far *req = (void __far *) vb->buf;
+
+ vbox_init_req(&req->header.header, VMMDevReq_HGCMConnect, sizeof(VMMDevHGCMConnect));
+ req->loc.type = VMMDevHGCMLoc_LocalHost_Existing;
+ _fstrcpy(req->loc.u.host.achName, service);
+
+ req->u32ClientID = 7;
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ *client_id = req->u32ClientID;
+
+ return req->header.result;
+}
+
+static int32_t vbox_hgcm_disconnect(LPVBOXCOMM vb, hgcm_client_id_t client_id)
+{
+ VMMDevHGCMDisconnect __far *req = (void __far *) vb->buf;
+
+ vbox_init_req(&req->header.header, VMMDevReq_HGCMDisconnect, sizeof(VMMDevHGCMDisconnect));
+ req->u32ClientID = client_id;
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ return req->header.result;
+}
+
+static inline void vbox_hgcm_init_call(VMMDevHGCMCall __far *req, hgcm_client_id_t client_id, uint32_t function, unsigned narg)
+{
+ vbox_init_req(&req->header.header, VMMDevReq_HGCMCall32, sizeof(VMMDevHGCMCall) + (narg * sizeof(HGCMFunctionParameter)));
+ req->u32ClientID = client_id;
+ req->u32Function = function;
+ req->cParms = narg;
+}
+
+#endif // VBOXHGCM_H
diff --git a/vboxshfl.h b/vboxshfl.h
new file mode 100644
index 0000000..292b8a3
--- /dev/null
+++ b/vboxshfl.h
@@ -0,0 +1,423 @@
+/*
+ * VBMouse - VirtualBox Shared Folders service client functions
+ * Copyright (C) 2022 Javier S. Pedro
+ *
+ * This program 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; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef VBOXSHFL_H
+#define VBOXSHFL_H
+
+#include "vboxhgcm.h"
+
+#define SHFLSTRING_WITH_BUF(name, size) \
+ struct { \
+ SHFLSTRING shflstr; \
+ char buf[size]; \
+ } name = { {size, 0} }
+
+#define SHFLDIRINFO_WITH_BUF(name, size) \
+ struct { \
+ SHFLDIRINFO dirinfo; \
+ char buf[size]; \
+ } name
+
+static inline unsigned shflstring_size_with_buf(const SHFLSTRING *str)
+{
+ return sizeof(SHFLSTRING) + str->u16Size;
+}
+
+static inline unsigned shflstring_size_optional_in(const SHFLSTRING *str)
+{
+ if (str->u16Length > 0) {
+ return sizeof(SHFLSTRING) + str->u16Size;
+ } else {
+ return 0;
+ }
+}
+
+static inline void shflstring_clear(SHFLSTRING *str)
+{
+ str->u16Length = 0;
+}
+
+static void shflstring_strcpy(SHFLSTRING *str, const char __far *src)
+{
+ str->u16Length = MIN(_fstrlen(src), str->u16Size - 1);
+ _fmemcpy(str->ach, src, str->u16Length);
+ str->ach[str->u16Length] = '\0';
+}
+
+static void shflstring_strncpy(SHFLSTRING *str, const char __far *src, unsigned len)
+{
+ str->u16Length = MIN(len, str->u16Size - 1);
+ _fmemcpy(str->ach, src, str->u16Length);
+ str->ach[str->u16Length] = '\0';
+}
+
+static int32_t vbox_shfl_query_mappings(LPVBOXCOMM vb, hgcm_client_id_t client_id, uint32_t flags, unsigned __far *num_maps, SHFLMAPPING __far *maps)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_QUERY_MAPPINGS, 3);
+
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = flags;
+
+ req->aParms[1].type = VMMDevHGCMParmType_32bit;
+ req->aParms[1].u.value32 = *num_maps;
+
+ req->aParms[2].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[2].u.Pointer.size = sizeof(SHFLMAPPING) * *num_maps;
+ req->aParms[2].u.Pointer.u.linearAddr = linear_addr(maps);
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ *num_maps = req->aParms[1].u.value32;
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_query_map_name(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root, SHFLSTRING *name)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_QUERY_MAP_NAME, 2);
+
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ req->aParms[1].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[1].u.Pointer.size = shflstring_size_with_buf(name);
+ req->aParms[1].u.Pointer.u.linearAddr = linear_addr(name);
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_map_folder(LPVBOXCOMM vb, hgcm_client_id_t client_id, const SHFLSTRING *name, SHFLROOT *root)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_MAP_FOLDER, 4);
+
+ // arg 0 in shflstring "name"
+ req->aParms[0].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[0].u.Pointer.size = shflstring_size_with_buf(name);
+ req->aParms[0].u.Pointer.u.linearAddr = linear_addr(name);
+
+ // arg 1 out uint32 "root"
+ req->aParms[1].type = VMMDevHGCMParmType_32bit;
+ req->aParms[1].u.value32 = *root;
+
+ // arg 2 in uint32 "delimiter"
+ req->aParms[2].type = VMMDevHGCMParmType_32bit;
+ req->aParms[2].u.value32 = '\\';
+
+ // arg 3 in uint32 "caseSensitive"
+ req->aParms[3].type = VMMDevHGCMParmType_32bit;
+ req->aParms[3].u.value32 = 0;
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ *root = req->aParms[1].u.value32;
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_unmap_folder(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_UNMAP_FOLDER, 1);
+
+ // arg 0 in uint32 "root"
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_open(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root, const SHFLSTRING *name, SHFLCREATEPARMS *parms)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_CREATE, 3);
+
+ // arg 0 in uint32 "root"
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ // arg 1 in shflstring "name"
+ req->aParms[1].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[1].u.Pointer.size = shflstring_size_with_buf(name);
+ req->aParms[1].u.Pointer.u.linearAddr = linear_addr(name);
+
+ // arg 2 in shflcreateparms "parms"
+ req->aParms[2].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[2].u.Pointer.size = sizeof(SHFLCREATEPARMS);
+ req->aParms[2].u.Pointer.u.linearAddr = linear_addr(parms);
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_close(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root, SHFLHANDLE handle)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_CLOSE, 2);
+
+ // arg 0 in uint32 "root"
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ // arg 1 in uint64 "handle"
+ req->aParms[1].type = VMMDevHGCMParmType_64bit;
+ req->aParms[1].u.value64 = handle;
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_read(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root, SHFLHANDLE handle,
+ unsigned long offset, unsigned __far *size, void __far *buffer)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_READ, 5);
+
+ // arg 0 in uint32 "root"
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ // arg 1 in uint64 "handle"
+ req->aParms[1].type = VMMDevHGCMParmType_64bit;
+ req->aParms[1].u.value64 = handle;
+
+ // arg 2 in uint64 "offset"
+ req->aParms[2].type = VMMDevHGCMParmType_64bit;
+ req->aParms[2].u.value64 = offset;
+
+ // arg 3 inout uint32 "size"
+ req->aParms[3].type = VMMDevHGCMParmType_32bit;
+ req->aParms[3].u.value32 = *size;
+
+ // arg 4 out void "buffer"
+ req->aParms[4].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[4].u.Pointer.size = *size;
+ req->aParms[4].u.Pointer.u.linearAddr = linear_addr(buffer);
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ *size = req->aParms[3].u.value32;
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_write(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root, SHFLHANDLE handle,
+ unsigned long offset, unsigned __far *size, void __far *buffer)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_WRITE, 5);
+
+ // arg 0 in uint32 "root"
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ // arg 1 in uint64 "handle"
+ req->aParms[1].type = VMMDevHGCMParmType_64bit;
+ req->aParms[1].u.value64 = handle;
+
+ // arg 2 in uint64 "offset"
+ req->aParms[2].type = VMMDevHGCMParmType_64bit;
+ req->aParms[2].u.value64 = offset;
+
+ // arg 3 inout uint32 "size"
+ req->aParms[3].type = VMMDevHGCMParmType_32bit;
+ req->aParms[3].u.value32 = *size;
+
+ // arg 4 in void "buffer"
+ req->aParms[4].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[4].u.Pointer.size = *size;
+ req->aParms[4].u.Pointer.u.linearAddr = linear_addr(buffer);
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ *size = req->aParms[3].u.value32;
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_list(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root, SHFLHANDLE handle,
+ unsigned flags, unsigned __far *size, const SHFLSTRING *path, SHFLDIRINFO *dirinfo, unsigned __far *resume, unsigned __far *count)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_LIST, 8);
+
+ // arg 0 in uint32 "root"
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ // arg 1 in uint64 "handle"
+ req->aParms[1].type = VMMDevHGCMParmType_64bit;
+ req->aParms[1].u.value64 = handle;
+
+ // arg 2 in uint32 "flags"
+ req->aParms[2].type = VMMDevHGCMParmType_32bit;
+ req->aParms[2].u.value32 = flags;
+
+ // arg 3 inout uint32 "size"
+ req->aParms[3].type = VMMDevHGCMParmType_32bit;
+ req->aParms[3].u.value32 = *size;
+
+ // arg 4 in shflstring "path"
+ req->aParms[4].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[4].u.Pointer.size = shflstring_size_optional_in(path);
+ req->aParms[4].u.Pointer.u.linearAddr = linear_addr(path);
+
+ // arg 5 out void "dirinfo"
+ req->aParms[5].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[5].u.Pointer.size = *size;
+ req->aParms[5].u.Pointer.u.linearAddr = linear_addr(dirinfo);
+
+ // arg 6 inout uint32 "resume_point"
+ req->aParms[6].type = VMMDevHGCMParmType_32bit;
+ req->aParms[6].u.value32 = *resume;
+
+ // arg 7 out uint32 "count"
+ req->aParms[7].type = VMMDevHGCMParmType_32bit;
+ req->aParms[7].u.value32 = 0;
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ *size = req->aParms[3].u.value32;
+ *resume = req->aParms[6].u.value32;
+ *count = req->aParms[7].u.value32;
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_set_utf8(LPVBOXCOMM vb, hgcm_client_id_t client_id)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_SET_UTF8, 0);
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ return req->header.result;
+}
+
+static int32_t vbox_shfl_query_map_info(LPVBOXCOMM vb, hgcm_client_id_t client_id, SHFLROOT root,
+ SHFLSTRING *name, SHFLSTRING *mountPoint, unsigned *flags, unsigned *version)
+{
+ VMMDevHGCMCall __far *req = (void __far *) vb->buf;
+ vbox_hgcm_init_call(req, client_id, SHFL_FN_QUERY_MAP_INFO, 5);
+
+ // arg 0 in uint32 "root"
+ req->aParms[0].type = VMMDevHGCMParmType_32bit;
+ req->aParms[0].u.value32 = root;
+
+ // arg 1 inout shflstring "name"
+ req->aParms[1].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[1].u.Pointer.size = shflstring_size_with_buf(name);
+ req->aParms[1].u.Pointer.u.linearAddr = linear_addr(name);
+
+ // arg 2 inout shflstring "mountPoint"
+ req->aParms[2].type = VMMDevHGCMParmType_LinAddr;
+ req->aParms[2].u.Pointer.size = shflstring_size_with_buf(mountPoint);
+ req->aParms[2].u.Pointer.u.linearAddr = linear_addr(mountPoint);
+
+ // arg 3 inout uint64 "flags"
+ req->aParms[3].type = VMMDevHGCMParmType_64bit;
+ req->aParms[3].u.value64 = *flags;
+
+ // arg 4 out uint32 "version"
+ req->aParms[4].type = VMMDevHGCMParmType_32bit;
+ req->aParms[4].u.value32 = *version;
+
+ vbox_send_request(vb->iobase, vb->dds.physicalAddress);
+
+ if (req->header.header.rc < 0) {
+ return req->header.header.rc;
+ } else if (req->header.header.rc == VINF_HGCM_ASYNC_EXECUTE) {
+ vbox_hgcm_wait(&req->header);
+ }
+
+ *flags = req->aParms[3].u.value64;
+ *version = req->aParms[4].u.value32;
+
+ return req->header.result;
+}
+
+#endif // VBOXSHFL_H
diff --git a/vbsf.lnk b/vbsf.lnk
new file mode 100644
index 0000000..cdbc767
--- /dev/null
+++ b/vbsf.lnk
@@ -0,0 +1,9 @@
+system dos
+option map=vbsf.map
+# Put the resident text & data first, then the rest of standard classses
+order clname RES_CODE
+ clname FAR_DATA
+ clname CODE segment BEGTEXT segment _TEXT
+ clname BEGDATA
+ clname DATA
+ clname BSS
diff --git a/vds.h b/vds.h
index 508a0ca..c8822c4 100644
--- a/vds.h
+++ b/vds.h
@@ -76,14 +76,6 @@ typedef _Packed struct VDSDDS
uint32_t physicalAddress;
} VDSDDS;
-/** Converts a far pointer into equivalent linear address.
- * Note that under protected mode linear != physical.
- * That's what VDS is for. */
-static inline uint32_t vds_ptr_to_linear(const void __far * ptr)
-{
- return ((uint32_t)(FP_SEG(ptr)) << 4) + FP_OFF(ptr);
-}
-
static bool vds_available(void);
#pragma aux vds_available = \
"mov ax, 0x40" \