diff options
author | Javier <dev.git@javispedro.com> | 2022-04-11 04:36:49 +0200 |
---|---|---|
committer | Javier <dev.git@javispedro.com> | 2022-04-11 04:36:49 +0200 |
commit | 19231982b2f374aed286f4697aebd3fb9fda05d8 (patch) | |
tree | 1922eaf34af3d904ce0f75c0e812e8da8e0d38d4 | |
parent | 970f90228a6978712c28529437721caffec76202 (diff) | |
download | vbados-19231982b2f374aed286f4697aebd3fb9fda05d8.tar.gz vbados-19231982b2f374aed286f4697aebd3fb9fda05d8.zip |
add new virtualbox shared folders client
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | dlog.h | 47 | ||||
-rw-r--r-- | dostsr.h | 116 | ||||
-rw-r--r-- | int21dos.h | 247 | ||||
-rw-r--r-- | makefile | 23 | ||||
-rw-r--r-- | mousetsr.c | 12 | ||||
-rw-r--r-- | mousetsr.h | 6 | ||||
-rw-r--r-- | mousew16.lnk | 2 | ||||
-rw-r--r-- | mousmain.c | 106 | ||||
-rw-r--r-- | sfmain.c | 531 | ||||
-rw-r--r-- | sftsr.c | 891 | ||||
-rw-r--r-- | sftsr.h | 54 | ||||
-rw-r--r-- | utils.h | 10 | ||||
-rw-r--r-- | vbmouse.lnk | 2 | ||||
-rw-r--r-- | vbox.c | 5 | ||||
-rw-r--r-- | vbox.h | 58 | ||||
-rw-r--r-- | vboxdev.h | 838 | ||||
-rw-r--r-- | vboxhgcm.h | 86 | ||||
-rw-r--r-- | vboxshfl.h | 423 | ||||
-rw-r--r-- | vbsf.lnk | 9 | ||||
-rw-r--r-- | vds.h | 8 |
21 files changed, 3304 insertions, 171 deletions
@@ -1,6 +1,7 @@ vbmouse.drv vbmouse.exe vbmouse.flp +vbsf.exe *.o *.obj *.map @@ -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 @@ -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 @@ -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 :: @@ -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)); } } @@ -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' @@ -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]); + } +} @@ -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; @@ -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 @@ -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 @@ -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; @@ -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 @@ -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 @@ -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" \ |