diff options
-rw-r--r-- | README.md | 94 | ||||
-rw-r--r-- | lfn.h | 47 | ||||
-rw-r--r-- | mousmain.c | 31 | ||||
-rw-r--r-- | nls.h | 2 | ||||
-rw-r--r-- | nls/vbmouse.en | 24 | ||||
-rw-r--r-- | nls/vbmouse.es | 24 | ||||
-rw-r--r-- | nls/vbsf.en | 33 | ||||
-rw-r--r-- | nls/vbsf.es | 33 | ||||
-rw-r--r-- | sfmain.c | 246 | ||||
-rw-r--r-- | sftsr.c | 111 | ||||
-rw-r--r-- | sftsr.h | 38 | ||||
-rw-r--r-- | unicode.h | 2 | ||||
-rw-r--r-- | vboxhgcm.h | 3 |
13 files changed, 430 insertions, 258 deletions
@@ -3,11 +3,14 @@ VBADOS is a set of Terminate-and-Stay-Resident (TSR) utilities to be used inside MS-DOS virtual machines (and Windows 3.x), and help to provide closer integration between the guest and the host. -There are two utilities right now: +There are three utilities right now: * VBMOUSE.EXE is a DOS mouse driver for VirtualBox/VMware emulated mouse, but also real PS/2 mice. It allows for seamless host/guest mouse integration, and scroll wheel support. + * VBMOUSE.DRV is a Windows 3.x mouse driver that depends on VBMOUSE.EXE + (or alternatively the builtin mouse support in DOSemu/DOSBox) + to provide the same seamless mouse integration and wheel support within Windows. * VBSF.EXE is a Shared Folders driver for VirtualBox (not VMware yet). It allows to map directories from the host OS into drive letters in the MS-DOS guest. @@ -18,11 +21,11 @@ The VB stands for "Very Basic" :) # Downloads -The current release is _0.6_. +The current release is _0.61_. You can get a recent build from the ready-to-go floppy disk image: [💾 VBADOS.FLP](https://depot.javispedro.com/vbox/vbados/vbados.flp) -(contains VBMOUSE.EXE, VBSF.EXE, VBMOUSE.DRV) +(contains VBMOUSE.EXE, VBMOUSE.DRV, VBSF.EXE) Alternatively, here is a .zip containing all binaries: @@ -37,6 +40,10 @@ For the source code, you can check out [this git repository](..). ## Version history +* _0.61_: Localization support using [Kitten](http://wiki.freedos.org/wiki/index.php/Kitten); currently only Spanish is available (`set lang=es`). + Non-fully-uppercase but still 8.3 filenames are no longer "shortened" by default. + Introduced per-mount options. + Improved wheel compatibility under Windows 3.x; Word is now compatible with wheel scrolling. * _0.6_: Big VBSF revamp, thanks to Eduardo Casino. VBSF now supports long file names in shared folders, as well as translating host Unicode filenames into the corresponding characters from the current DOS codepage. @@ -53,10 +60,10 @@ wheel mouse compatibility. ## Acknowledgments * Thanks to Eduardo Casino ([VMSMOUNT project](https://vmsmount.sourceforge.io/), for his contributions regarding -internalization and long file name support. +internalization, Unicode and long file name support. * NattyNarwhal ([vmwmouse](https://github.com/NattyNarwhal/vmwmouse)) - and stsp ([dosemu2](https://github.com/dosemu2/dosemu2)) for ideas and feedback regarding the Windows 3.x driver. + and stsp ([dosemu2](https://github.com/dosemu2/dosemu2)) for ideas and feedback regarding the Windows 3.x mouse driver. ## VBMOUSE.EXE - DOS mouse driver @@ -165,12 +172,24 @@ Run `vbmouse <action>` for specific configuration. Here are the supported action This does not include any of the above settings, but rather the traditional int33 mouse settings (like sensitivity) that may be altered by other programs. It is equivalent to int33/ax=0. -### Windows 3.x driver - -A very simple Windows 3.x mouse driver (called _VBMOUSE.DRV_) is also included, -which _requires VBMOUSE.EXE to be loaded before starting Windows_ , -and uses its functionality to provide similar host/guest mouse integration -inside Windows 3.x itself (i.e. not just DOS boxes). +### VBMOUSE.DRV - Windows 3.x driver + +A Windows 3.x mouse driver (called _VBMOUSE.DRV_) is also included +which provides similar host/guest mouse integration inside Windows 3.x itself +(i.e. not just DOS boxes). +However, it _requires a DOS mouse driver_, +which means you need to load VBMOUSE.EXE from DOS _before starting Windows_. + +As an alternative to VBMOUSE.EXE, VBMOUSE.DRV +can also work with the builtin DOS mouse drivers inside DOSemu or DOSBox. +This gives mouse integration inside Windows without the need to install a +separate DOS mouse driver. +However, the builtin mouse driver needs to support my +[absolute int33 API extension bit](https://git.javispedro.com/cgit/vbados.git/about/#absolute-mouse-api). +This is currently natively supported by +[dosemu2](https://dosemu2.github.io/dosemu2/), +[DOSBox-X](https://dosbox-x.com/) and +[DOSBox Staging](https://dosbox-staging.github.io/). To install this driver, and assuming you have for example already inserted the VBADOS.flp floppy (or copied the files in some other way), @@ -194,12 +213,6 @@ VBMOUSE.EXE as DOS mouse driver, you may be interested in E.g, for either [VirtualBox](https://git.javispedro.com/cgit/vbmouse.git/) or [VMware](https://github.com/NattyNarwhal/vmwmouse). -You can also use VBMOUSE.DRV with any other int33 mouse driver, however -to use absolute positioning (e.g. under DOSBox-X or DOSemu), the int33 -driver needs to support my [absolute int33 API extension bit](https://git.javispedro.com/cgit/vbados.git/about/#absolute-mouse-api). -Otherwise the driver will only pass relative coordinates (i.e. without -SF_ABSOLUTE bit). - ## VBSF.EXE - Shared folders VBSF.EXE allows you to mount VirtualBox shared folders as drive letters. @@ -242,25 +255,38 @@ The driver will automatically mount all the directories marked as "Automount". The driver supports the following actions, too: * `install` installs the driver (i.e. the same as if you run `vbsf`). - This command has several suboptions (multiple may be combined): - * `low` can be used to force installation in conventional memory; - by default, it tries to use a DOS UMB block. - - * `short` uses short filenames directly from the host OS, without any - translations. - This is only useful in host OSes that support storing short - filenames separately, like Windows hosts, and usually only if you expect - to use the same filesystem with a different OS later on. - - * `hash <n>` changes the number of digits reserved for the hash portion - of a short filename to `n`. + `vbsf install low` can be used to force installation in conventional memory; +by default, it tries to use a DOS UMB block. * `uninstall` uninstalls the driver. * `list` shows currently mounted drives as well as all available shared folders. -* `mount FOLDER X:` can be used to mount a non-automatic shared folder at a specific drive, - or to mount a specific shared folder on multiple drives. +* `mount FOLDER X: [<OPTIONS...>]` can be used to mount a non-automatic shared folder at a specific drive, + or to mount a specific shared folder on multiple drives. + This command supports the following mount options: + * `/hash <n>` enables the automatic generation of short/hashed DOS filenames + for long filenames from the host, and uses `n` as the number of digits reserved + for the hash portion of short filenames. + By default, generation of short filenames is enabled using 3 digits for the hash, + so long filenames will look like `LONG~20B.TXT` in DOS. + Use `/nohash` to disable, in which case long filenames from the host will + not appear in DOS. + * `/upper` will also generate short/hashed filenames for any file in host + whose filename has any lowercase character. + This may be useful for host OSes with case sensitive filesystems, + where there are two or more files with filenames that differ only in case. + Without this option, the two files cannot be distinguished and may be + accidentally overwritten by DOS. + * `/host` uses short filenames directly from the host OS, without any + translations. + This is only useful in host OSes that support storing short + filenames separately, like Windows hosts, and usually only if you expect + to use the same filesystem with a different OS later on. + +* `remount X: [<OPTIONS...>]` changes the options of a currently mounted drive. + See the `mount` command for details on options. Any option set previously + can be disabled with a corresponding `/noopt`, e.g., `/noshort`. * `unmount X:` unmounts a specific drive. @@ -284,10 +310,10 @@ The use of hashes allow the short file names to be consistent between reboots of the guest OS, even if the host OS does not have support to store long these short file names. If your host OS does have support for storing persistent short file names and want to use those instead of the autogenerated ones, -consider using the option `short` when installing VBSF. +consider using the option `/host` when mounting a shared folder. If you have a directory with lots of similarly names files, it may help to reduce the chances of a hash collision by increase the number of letters dedicated to the -hash portion of the short file names. For that, use the `hash <n>` option. +hash portion of the short file names. For that, use the `/hash <n>` option. In addition, there is support for translation of extended characters from the host filesystem into the guest DOS' codepage. @@ -304,7 +330,7 @@ For example, if your local timezone is 8 hours earlier than UTC (e.g. PST), run # Building the source This requires [OpenWatcom 2.0](http://open-watcom.github.io/) to build, -and I have only tried with the latest (March 2022) snapshot, +and I have only tried with the latest (November 2022) snapshot, albeit it may likely work with older versions, starting from 1.9. I have tested building it on a Linux host as well as building on MS-DOS itself @@ -27,12 +27,6 @@ #define REVERSE_HASH 1 -#define DEF_HASH_CHARS 3 -#define MIN_HASH_CHARS 2 -#define MAX_HASH_CHARS 6 - -#ifdef IN_TSR - static inline bool translate_filename_from_host(SHFLSTRING *, bool, bool); static bool matches_8_3_wildcard(const char __far *, const char __far *); static int my_strrchr(const char __far *, char); @@ -64,6 +58,7 @@ static inline char __far *_fstrrchr_local(const char __far *str, char c) return (char __far *)&str[i]; } +/** Like strcpy, but returns a pointer to the new last char in dst (a NULL). */ static inline char *_fstrcpy_local(char *dst, const char __far *src) { while (*dst++ = *src++) @@ -86,17 +81,17 @@ static inline char hex_digit(uint8_t val) * included in gawk, which uses bit rotations instead of * multiplications) */ -// Claculates and returns hash_len bits length hash -// #define FOLD_MASK(x) (((uint32_t)1 << (x)) - 1) -uint32_t lfn_name_hash( - uint8_t *name, // in : File name in UTF-8 - uint16_t len // in : File name length +/** Calculates and returns hash_len bits length hash */ +static uint32_t lfn_name_hash( + uint8_t *name, // in : File name in UTF-8 + uint16_t len, // in : File name length + uint8_t hash_chars // in: Number of characters for hash part ) { uint8_t *be = name + len; /* beyond end of buffer */ uint32_t hval = 0; - uint8_t hash_len = data.hash_chars << 2; + uint8_t hash_len = hash_chars << 2; while (name < be) { @@ -120,15 +115,16 @@ uint32_t lfn_name_hash( // Generates a valid (and hopefully unique) 8.3 DOS path from an LFN // This function assumes that fname is already in DOS character set // -void mangle_to_8_3_filename( - uint32_t hash, // in : Pre-calculated hash of the filename in UTF-8 +static void mangle_to_8_3_filename( + uint32_t hash, // in : Pre-calculated hash of the filename in UTF-8 + uint8_t hash_chars, // in: Number of characters for hash part char __far *fcb_name, // out : File name in FCB format. Must be 11 bytes long - SHFLSTRING *str // in : File name in DOS codepage + SHFLSTRING *str // in : File name in DOS codepage ) { char *d, *p, __far *s, *fname = str->ach; int i; - uint8_t mangle = data.hash_chars; + uint8_t mangle = hash_chars; static int ror[] = {0, 4, 8, 12, 16, 20}; *(uint32_t __far *)(&fcb_name[0]) = 0x20202020; @@ -256,8 +252,8 @@ static inline bool match_to_8_3_filename(const char __far *dos_filename, const c } static inline char *find_real_name( - SHFLROOT root, TSRDATAPTR data, + int drive, char *dest, // out : destination buffer char __far *path, // in : search path char __far *filename, // in : DOS file name @@ -265,6 +261,8 @@ static inline char *find_real_name( uint16_t bufsiz // in : buffer size ) { + SHFLROOT root = data->drives[drive].root; + uint8_t hash_chars = data->drives[drive].opt.hash_chars; vboxerr err; dprintf("find_real_name path=%Fs filename=%Fs\n", path, filename); @@ -314,7 +312,7 @@ static inline char *find_real_name( } // Calculate hash using host file name - hash = lfn_name_hash(shfldirinfo.dirinfo.name.ach, shfldirinfo.dirinfo.name.u16Length); + hash = lfn_name_hash(shfldirinfo.dirinfo.name.ach, shfldirinfo.dirinfo.name.u16Length, hash_chars); // Copy now, because translate_filename_from_host() converts fName to DOS codepage // @@ -326,7 +324,7 @@ static inline char *find_real_name( d = _fstrcpy_local(dest, &shfldirinfo.dirinfo.name.ach); translate_filename_from_host(&shfldirinfo.dirinfo.name, false, true); - mangle_to_8_3_filename(hash, fcb_name, &shfldirinfo.dirinfo.name); + mangle_to_8_3_filename(hash, hash_chars, fcb_name, &shfldirinfo.dirinfo.name); if (match_to_8_3_filename(filename, fcb_name)) { @@ -346,13 +344,13 @@ not_found: } } -static uint16_t get_true_host_name_n(SHFLROOT root, TSRDATAPTR data, uint8_t *dst, char __far *src, uint16_t buflen, uint16_t count) +static uint16_t get_true_host_name_n(TSRDATAPTR data, int drive, uint8_t *dst, char __far *src, uint16_t buflen, uint16_t count) { char __far *s, *d; char __far *tl, __far *ps, __far *ns, save_ns, save_end; uint16_t ret, len = 0; - if (data->short_fnames) + if (!data->drives[drive].opt.generate_sfn) { return local_to_utf8_n(data, dst, src, buflen, count); } @@ -402,7 +400,7 @@ static uint16_t get_true_host_name_n(SHFLROOT root, TSRDATAPTR data, uint8_t *ds *d = '\0'; ++len; - d = find_real_name(root, data, d, dst, ps + 1, (uint16_t)(ns - ps + 1), buflen - len); + d = find_real_name(data, drive, d, dst, ps + 1, (uint16_t)(ns - ps + 1), buflen - len); if (d == (char *)0) { @@ -433,10 +431,9 @@ return_name: return len; } -static inline uint16_t get_true_host_name(SHFLROOT root, TSRDATAPTR data, uint8_t *dst, char __far *src, uint16_t buflen) +static inline uint16_t get_true_host_name(TSRDATAPTR data, int drive, uint8_t *dst, char __far *src, uint16_t buflen) { - return get_true_host_name_n(root, data, dst, src, buflen, buflen); + return get_true_host_name_n(data, drive, dst, src, buflen, buflen); } -#endif // IN_TSR #endif // LFN_H @@ -33,7 +33,7 @@ #include "dostsr.h" #include "mousetsr.h" -nl_catd cat; +static nl_catd cat; #if USE_WHEEL static void detect_wheel(LPTSRDATA data) @@ -389,23 +389,24 @@ static int driver_not_found(void) static void print_help(void) { - puts(_(0, 0, "\n" - "Usage: ")); - puts(_(0, 1, " VBMOUSE <ACTION> <ARGS..>\n")); - puts(_(0, 2, "Supported actions:")); - puts(_(0, 3, " install install the driver (default)")); - puts(_(0, 4, " low install in conventional memory (otherwise UMB)")); - puts(_(0, 5, " uninstall uninstall the driver from memory")); + putchar('\n'); + puts(_(0, 0, "Usage: ")); + puts(_(0, 1, " VBMOUSE <ACTION> <ARGS..>")); + putchar('\n'); + puts(_(0, 2, "Supported actions and options:")); + puts(_(0, 3, " install Install the driver (default).")); + puts(_(0, 4, " low Install in conventional memory (otherwise UMB).")); + puts(_(0, 5, " uninstall Uninstall the driver from memory.")); #if USE_WHEEL - puts(_(0, 6, " wheel <ON|OFF> enable/disable wheel API support")); - puts(_(0, 7, " wheelkey <KEY|OFF> emulate a specific keystroke on wheel scroll")); - puts(_(0, 8, " supported keys: updn, pageupdn")); + puts(_(0, 6, " wheel <ON|OFF> Enable/disable wheel API support.")); + puts(_(0, 7, " wheelkey <KEY|OFF> Emulate a specific keystroke on wheel scroll.")); + puts(_(0, 8, " Supported keys: updn, pageupdn.")); #endif #if USE_INTEGRATION - puts(_(0, 9, " integ <ON|OFF> enable/disable virtualbox integration")); - puts(_(0, 10, " hostcur <ON|OFF> enable/disable mouse cursor rendering in host")); + puts(_(0, 9, " integ <ON|OFF> Enable/disable VirtualBox integration.")); + puts(_(0, 10, " hostcur <ON|OFF> Enable/disable mouse cursor rendering in the host.")); #endif - puts(_(0, 11, " reset reset mouse driver settings")); + puts(_(0, 11, " reset Reset mouse driver.")); } static int invalid_arg(const char *s) @@ -451,7 +452,7 @@ int main(int argc, const char *argv[]) cat = kittenopen("vbmouse"); - printf(_(1, 20, "\nVBMouse %x.%x (like MSMOUSE %x.%x)\n"), VERSION_MAJOR, VERSION_MINOR, REPORTED_VERSION_MAJOR, REPORTED_VERSION_MINOR); + printf(_(1, 20, "\nVBMouse %x.%x (reporting as MSMOUSE %x.%x)\n"), VERSION_MAJOR, VERSION_MINOR, REPORTED_VERSION_MAJOR, REPORTED_VERSION_MINOR); if (argi >= argc || stricmp(argv[argi], "install") == 0) { bool high = true; @@ -50,6 +50,8 @@ static unsigned char nls_toupper( unsigned char c ) return ( c < 0x80 ? c : data.file_upper_case[c - 0x80] ); } +/** Convert str into uppercase as per current NLS rules. + * @return true if str was already uppercase. */ static inline bool nls_uppercase(SHFLSTRING *str) { int i; diff --git a/nls/vbmouse.en b/nls/vbmouse.en index feab00b..3febaba 100644 --- a/nls/vbmouse.en +++ b/nls/vbmouse.en @@ -4,18 +4,18 @@ # Spaces before text must be kept. Be sure that no spaces are # added to the end of the lines. # -0.0:\nUsage: -0.1: VBMOUSE <ACTION> <ARGS..>\n -0.2:Supported actions: -0.3: install install the driver (default) -0.4: low install in conventional memory (otherwise UMB) -0.5: uninstall uninstall the driver from memory -0.6: wheel <ON|OFF> enable/disable wheel API support -0.7: wheelkey <KEY|OFF> emulate a specific keystroke on wheel scroll -0.8: supported keys: updn, pageupdn -0.9: integ <ON|OFF> enable/disable virtualbox integration -0.10: hostcur <ON|OFF> enable/disable mouse cursor rendering in host -0.11: reset reset mouse driver settings +0.0:Usage: +0.1: VBMOUSE <ACTION> <ARGS..> +0.2:Supported actions and options: +0.3: install Install the driver (default). +0.4: low Install in conventional memory (otherwise UMB). +0.5: uninstall Uninstall the driver from memory. +0.6: wheel <ON|OFF> Enable/disable wheel API support. +0.7: wheelkey <KEY|OFF> Emulate a specific keystroke on wheel scroll. +0.8: Supported keys: updn, pageupdn. +0.9: integ <ON|OFF> Enable/disable VirtualBox integration. +0.10: hostcur <ON|OFF> Enable/disable mouse cursor rendering in the host. +0.11: reset Reset mouse driver. 1.0:Wheel mouse found and enabled\n 1.1:Setting wheel support to %s\n 1.2:enabled diff --git a/nls/vbmouse.es b/nls/vbmouse.es index 11f30a7..7b79bda 100644 --- a/nls/vbmouse.es +++ b/nls/vbmouse.es @@ -6,18 +6,18 @@ # Spaces before text must be kept. Be sure that no spaces are # added to the end of the lines. # -0.0:\nUso: -0.1: VBMOUSE <ACCIàN> <ARGS..>\n -0.2:Acciones soportadas: -0.3: install instala el controlador (por defecto) -0.4: low instala en memoria convencional (UMB si no) -0.5: uninstall desinstala el controlador de la memoria -0.6: wheel <ON|OFF> habilita/deshabilita soporte para la rueda -0.7: wheelkey <KEY|OFF> emula una tecla espec¡fica para el scroll por rueda -0.8: valores soportados para KEY: updn, pageupdn -0.9: integ <ON|OFF> habilita/deshabilita integraci¢n con virtualbox -0.10: hostcur <ON|OFF> habilita/deshabilita pintado del cursor en el anfitri¢n -0.11: reset reinicia ajustes del controlador del rat¢n +0.0:Uso: +0.1: VBMOUSE <ACCIàN> <ARGS..> +0.2:Acciones y opciones soportadas: +0.3: install Instala el controlador (por defecto). +0.4: low Instala en memoria convencional (UMB si no). +0.5: uninstall Desinstala el controlador de la memoria. +0.6: wheel <ON|OFF> Habilita/deshabilita el soporte para la rueda. +0.7: wheelkey <TECLA|OFF> Emula una tecla espec¡fica al rotar la rueda. +0.8: Teclas soportadas: updn, pageupdn. +0.9: integ <ON|OFF> Habilita/deshabilita integraci¢n con VirtualBox. +0.10: hostcur <ON|OFF> Habilita/deshabilita cursor dibujado por anfitri¢n. +0.11: reset Reinicia el controlador del rat¢n. 1.0:Rueda de rat¢n encontrada y activada\n 1.1:Soporte para rueda %s\n 1.2:habilitado diff --git a/nls/vbsf.en b/nls/vbsf.en index 9aac01e..e058178 100644 --- a/nls/vbsf.en +++ b/nls/vbsf.en @@ -4,23 +4,24 @@ # Spaces before text must be kept. Be sure that no spaces are # added to the end of the lines. # -0.0:\nUsage: -0.1: VBSF <ACTION> <ARGS..>\n -0.2:Supported actions: -0.3: install install the driver (default) -0.4: low install in conventional memory (otherwise UMB) -0.5: short use short file names from windows hosts -0.6: hash <n> number of hash generated chars following the '~' -0.7: for generating DOS valid files -0.8: (%d min, %d max, %d default)\n -0.9: uninstall uninstall the driver from memory -0.10: list list available shared folders -0.11: mount [/cs] <FOLD> <X:> mount a shared folder into drive X: -0.12: umount <X:> unmount shared folder from drive X: -0.13: rescan unmount everything and recreate automounts -0.14: use '/cs' if host filesystem is case sensitive +0.0:Usage: +0.1: VBSF <ACTION> <ARGS..> [<OPTIONS..>] +0.2:Supported actions and options: +0.3: install Install the driver (default). +0.4: low Install in conventional memory (otherwise UMB). +0.5: uninstall Uninstall the driver from memory. +0.6: list List available shared folders. +0.7: mount <FOLD> <X:> ... Mount a shared folder into drive X:. +0.8: /hash <n> Number of hash chars following the '~' +0.9: for generated DOS short filenames. +0.10: (between %d and %d; default %d; 0 to disable)\n +0.11: /host Use short file names from Windows hosts. +0.12: /upper Require uppercase host filenames. +0.13: remount <X:> ... Change mount options for mounted drive X:. +0.14: umount <X:> Unmount shared folder from drive X:. +0.15: rescan Unmount everything and recreate automounts. 1.0:Mounted drives:\n -1.1: %s on %c:\n +1.1: %s on %c: %s\n 1.2:Available shared folders:\n 1.3:Shared folder '%s' mounted as drive %c:\n 1.4:Drive %c: unmounted\n diff --git a/nls/vbsf.es b/nls/vbsf.es index 7e638be..32bac6d 100644 --- a/nls/vbsf.es +++ b/nls/vbsf.es @@ -6,23 +6,24 @@ # Spaces before text must be kept. Be sure that no spaces are # added to the end of the lines. # -0.0:\nUso: -0.1: VBSF <ACCIàN> <ARGS..>\n -0.2:Acciones soportadas: -0.3: install instala el controlador (por defecto) -0.4: low instala en memoria convencional (UMB si no) -0.5: short nombres cortos de archivos en anfitriones windows -0.6: hash <n> n£mero de caracteres generados por hash tras la '~' -0.7: para generar nombres de archivo v lidos en DOS -0.8: (%d m¡n, %d m x, %d por defecto)\n -0.9: uninstall desinstala el controlador de la memoria -0.10: list lista carpetas compartidas disponibles -0.11: mount [/cs] <CARP> <X:> monta una carpeta compartida en la unidad X: -0.12: umount <X:> desmonta la carpeta compartida de la unidad X: -0.13: rescan desmonta todo y recrea los automounts -0.14: usar '/cs' si el anfitri¢n distingue may£s/min£s +0.0:Uso: +0.1: VBSF <ACCIàN> <ARGS..> [<OPCIONES..>] +0.2:Acciones y opciones soportadas: +0.3: install Instala el controlador (por defecto). +0.4: low Instala en memoria convencional (UMB si no). +0.5: uninstall Desinstala el controlador de la memoria. +0.6: list Lista carpetas compartidas disponibles. +0.7: mount <CARP> <X:> ... Monta una carpeta compartida en la unidad X:. +0.8: /hash <n> N£mero de car cteres hash tras '~' +0.9: en los nombres de archivo cortos generados. +0.10: (entre %d y %d; por defecto %d; 0 desactiva)\n +0.11: /host Usa nombres cortos de anfitriones Windows. +0.12: /upper Require nombres de archivo en may£sculas. +0.13: remount <X:> ... Cambia opciones de montado para unidad X:. +0.14: umount <X:> Desmonta la carpeta compartida de la unidad X:. +0.15: rescan Desmonta todo y rehace los automontajes. 1.0:Unidades montadas:\n -1.1: %s en %c:\n +1.1: %s en %c: %s\n 1.2:Carpetas compartidas disponibles:\n 1.3:Carpeta compartida '%s' montada en la unidad %c:\n 1.4:Unidad %c: desmontada\n @@ -62,11 +62,31 @@ static char find_free_drive_letter() { return 0; } +static void list_mountopts(MOUNTOPTS __far *opts, char *s) +{ + s[0] = '\0'; + + if (opts->generate_sfn) { + if (opts->hash_chars != DEF_HASH_CHARS) { + s += sprintf(s, "/hash %u ", opts->hash_chars); + } + } else { + s += sprintf(s, "/nohash "); + } + if (opts->use_host_sfn) { + s += sprintf(s, "/host "); + } + if (opts->require_uppercase) { + s += sprintf(s, "/upper "); + } +} + static int list_folders(LPTSRDATA data) { int32_t err; SHFLMAPPING maps[SHFL_MAX_MAPPINGS]; SHFLSTRING_WITH_BUF(str, SHFL_MAX_LEN); + char optstr[32]; unsigned num_maps = sizeof(maps) / sizeof(SHFLMAPPING); unsigned i; @@ -85,7 +105,11 @@ static int list_folders(LPTSRDATA data) } (void)utf8_to_local(data, str.buf, str.buf, NULL); - printf(_(1, 1, " %s on %c:\n"), str.buf, drive_index_to_letter(i)); + + list_mountopts(&data->drives[i].opt, optstr); + + + printf(_(1, 1, " %s on %c: %s\n"), str.buf, drive_index_to_letter(i), optstr); } err = vbox_shfl_query_mappings(&data->vb, data->hgcm_client_id, 0, &num_maps, maps); @@ -144,7 +168,7 @@ static void close_openfiles(LPTSRDATA data, int drive) } } -static int mount_shfl(LPTSRDATA data, int drive, const char *folder, bool ci) +static int mount_shfl(LPTSRDATA data, int drive, const char *folder) { int32_t err; SHFLSTRING_WITH_BUF(str, SHFL_MAX_LEN); @@ -160,7 +184,6 @@ static int mount_shfl(LPTSRDATA data, int drive, const char *folder, bool ci) } data->drives[drive].root = root; - data->drives[drive].case_insensitive = ci; return 0; } @@ -183,19 +206,46 @@ static int unmount_shfl(LPTSRDATA data, int drive) return 0; } -static int mount(LPTSRDATA data, char *folder, char drive_letter, bool ci) +static void set_mountopts_defaults(MOUNTOPTS *opts) { - int drive = drive_letter_to_index(drive_letter); - DOSLOL __far *lol = dos_get_list_of_lists(); - DOSCDS __far *cds; + opts->generate_sfn = true; + opts->require_uppercase = false; + opts->use_host_sfn = false; + opts->hash_chars = DEF_HASH_CHARS; +} +static bool is_valid_drive(DOSLOL __far *lol, char drive_letter, int drive) +{ if (drive < 0) { fprintf(stderr, _(3, 6, "Invalid drive %c:\n"), drive_letter); - return EXIT_FAILURE; + return false; } if (drive >= lol->last_drive || drive >= NUM_DRIVES) { fprintf(stderr, _(3, 7, "Drive %c: is after LASTDRIVE\n"), drive_letter); + return false; + } + + return true; +} + +static bool is_mounted_drive(LPTSRDATA data, char drive_letter, int drive) +{ + if (data->drives[drive].root == SHFL_ROOT_NIL) { + fprintf(stderr, _(3, 11, "Drive %c not mounted\n"), drive_letter); + return false; + } + + return true; +} + +static int mount(LPTSRDATA data, char *folder, char drive_letter, MOUNTOPTS *opts) +{ + int drive = drive_letter_to_index(drive_letter); + DOSLOL __far *lol = dos_get_list_of_lists(); + DOSCDS __far *cds; + + if (!is_valid_drive(lol, drive_letter, drive)) { return EXIT_FAILURE; } @@ -211,11 +261,12 @@ static int mount(LPTSRDATA data, char *folder, char drive_letter, bool ci) return EXIT_FAILURE; } - if (mount_shfl(data, drive, folder, ci) != 0) { + if (mount_shfl(data, drive, folder) != 0) { fprintf(stderr, _(3, 10, "Cannot mount drive %c:\n"), drive_letter); return EXIT_FAILURE; } + data->drives[drive].opt = *opts; // Ok, set the network flag. // By setting the physical flag, we also let DOS know the drive is present @@ -233,33 +284,52 @@ static int unmount(LPTSRDATA data, char drive_letter) DOSLOL __far *lol = dos_get_list_of_lists(); DOSCDS __far *cds; - if (drive < 0) { - fprintf(stderr, _(3, 6, "Invalid drive %c:\n"), drive_letter); + if (!is_valid_drive(lol, drive_letter, drive) + || !is_mounted_drive(data, drive_letter, drive)) { return EXIT_FAILURE; } - if (drive >= lol->last_drive || drive >= NUM_DRIVES) { - fprintf(stderr, _(3, 7, "Drive %c: is after LASTDRIVE\n"), drive_letter); + if (unmount_shfl(data, drive) != 0) { return EXIT_FAILURE; } + // Hide the drive from DOS cds = &lol->cds[drive]; + cds->flags = 0; - if (data->drives[drive].root == SHFL_ROOT_NIL) { - fprintf(stderr, _(3, 11, "Drive %c not mounted\n"), drive_letter); - return EXIT_FAILURE; - } + // TODO Clear current directory ? - if (unmount_shfl(data, drive) != 0) { + printf(_(1, 4, "Drive %c: unmounted\n"), drive_letter); + + return EXIT_SUCCESS; +} + +static int get_mountopts(LPTSRDATA data, char drive_letter, MOUNTOPTS *opts) +{ + int drive = drive_letter_to_index(drive_letter); + DOSLOL __far *lol = dos_get_list_of_lists(); + + if (!is_valid_drive(lol, drive_letter, drive) + || !is_mounted_drive(data, drive_letter, drive)) { return EXIT_FAILURE; } - // Hide the drive from DOS - cds->flags = 0; + *opts = data->drives[drive].opt; - // TODO Clear current directory ? + return EXIT_SUCCESS; +} - printf(_(1, 4, "Drive %c: unmounted\n"), drive_letter); +static int set_mountopts(LPTSRDATA data, char drive_letter, MOUNTOPTS *opts) +{ + int drive = drive_letter_to_index(drive_letter); + DOSLOL __far *lol = dos_get_list_of_lists(); + + if (!is_valid_drive(lol, drive_letter, drive) + || !is_mounted_drive(data, drive_letter, drive)) { + return EXIT_FAILURE; + } + + data->drives[drive].opt = *opts; return EXIT_SUCCESS; } @@ -270,10 +340,13 @@ static int automount(LPTSRDATA data) SHFLMAPPING maps[SHFL_MAX_MAPPINGS]; SHFLSTRING_WITH_BUF(name, SHFL_MAX_LEN); SHFLSTRING_WITH_BUF(mountPoint, SHFL_MAX_LEN); + MOUNTOPTS opts; unsigned num_maps = sizeof(maps) / sizeof(SHFLMAPPING); unsigned flags, version; unsigned i; + set_mountopts_defaults(&opts); + err = vbox_shfl_query_mappings(&data->vb, data->hgcm_client_id, SHFL_MF_AUTOMOUNT, &num_maps, maps); if (err) { @@ -307,7 +380,7 @@ static int automount(LPTSRDATA data) drive_letter = find_free_drive_letter(); } - mount(data, name.buf, drive_letter, true); + mount(data, name.buf, drive_letter, &opts); } return 0; @@ -479,7 +552,7 @@ error: } -static int configure_driver(LPTSRDATA data, bool short_fnames, uint8_t hash_chars) +static int configure_driver(LPTSRDATA data) { unsigned i; int32_t err; @@ -515,12 +588,6 @@ static int configure_driver(LPTSRDATA data, bool short_fnames, uint8_t hash_char return -1; } - // Set use of short file names from Windows hosts - data->short_fnames = short_fnames; - - // Set number of hash generated characters - data->hash_chars = hash_chars < MIN_HASH_CHARS ? MIN_HASH_CHARS : hash_chars > MAX_HASH_CHARS ? MAX_HASH_CHARS : hash_chars; - // Now try to initialize VirtualBox communication err = vbox_init_device(&data->vb); if (err) { @@ -646,23 +713,25 @@ static int driver_not_found(void) static void print_help(void) { - puts(_(0, 0, "\n" - "Usage: ")); - puts(_(0, 1, " VBSF <ACTION> <ARGS..>\n")); - puts(_(0, 2, "Supported actions:")); - puts(_(0, 3, " install install the driver (default)")); - puts(_(0, 4, " low install in conventional memory (otherwise UMB)")); - puts(_(0, 5, " short use short file names from windows hosts")); - puts(_(0, 6, " hash <n> number of hash generated chars following the '~'")); - puts(_(0, 7, " for generating DOS valid files")); - printf(_(0, 8, " (%d min, %d max, %d default)\n"), - MIN_HASH_CHARS, MAX_HASH_CHARS, DEF_HASH_CHARS); - puts(_(0, 9, " uninstall uninstall the driver from memory")); - puts(_(0, 10, " list list available shared folders")); - puts(_(0, 11, " mount [/cs] <FOLD> <X:> mount a shared folder into drive X:")); - puts(_(0, 14, " use '/cs' if host filesystem is case sensitive")); - puts(_(0, 12, " umount <X:> unmount shared folder from drive X:")); - puts(_(0, 13, " rescan unmount everything and recreate automounts")); + putchar('\n'); + puts( _(0, 0, "Usage: ")); + puts( _(0, 1, " VBSF <ACTION> <ARGS..> [<OPTIONS..>]")); + putchar('\n'); + puts( _(0, 2, "Supported actions and options:")); + puts( _(0, 3, " install Install the driver (default).")); + puts( _(0, 4, " low Install in conventional memory (otherwise UMB).")); + puts( _(0, 5, " uninstall Uninstall the driver from memory.")); + puts( _(0, 6, " list List available shared folders.")); + puts( _(0, 7, " mount <FOLD> <X:> ... Mount a shared folder into drive X:.")); + puts( _(0, 8, " /hash <n> Number of hash chars following the '~'")); + puts( _(0, 9, " for generated DOS short filenames.")); + printf(_(0, 10, " (between %d and %d; default %d; 0 to disable)\n"), + MIN_HASH_CHARS, MAX_HASH_CHARS, DEF_HASH_CHARS); + puts( _(0, 11, " /host Use short file names from Windows hosts.")); + puts( _(0, 12, " /upper Require uppercase host filenames.")); + puts( _(0, 13, " remount <X:> ... Change mount options for mounted drive X:.")); + puts( _(0, 14, " umount <X:> Unmount shared folder from drive X:.")); + puts( _(0, 15, " rescan Unmount everything and recreate automounts.")); } static int invalid_arg(const char *s) @@ -701,6 +770,45 @@ static bool is_false(const char *s) || stricmp(s, "0") == 0; } +static int parse_mountopts(MOUNTOPTS *opts, int *argi, int argc, const char *argv[]) +{ + int i = *argi; + while (argv[i][0] == '/' && i < argc) { + if (stricmp(argv[i], "/hash") == 0) { + i++; + if (i >= argc) return arg_required("/hash"); + + if (is_false(argv[i])) { + opts->generate_sfn = false; + } else if (strlen(argv[i]) == 1 && isdigit(argv[i][0])) { + int hash_chars = argv[i][0] - '0'; + if (hash_chars < MIN_HASH_CHARS || hash_chars > MAX_HASH_CHARS) { + return invalid_arg(argv[i]); + } + opts->generate_sfn = true; + opts->hash_chars = hash_chars; + } else { + return invalid_arg(argv[i]); + } + } else if (stricmp(argv[i], "/nohash") == 0) { + opts->generate_sfn = false; + } else if (stricmp(argv[i], "/host") == 0) { + opts->use_host_sfn = true; + } else if (stricmp(argv[i], "/nohost") == 0) { + opts->use_host_sfn = false; + } else if (stricmp(argv[i], "/upper") == 0) { + opts->require_uppercase = true; + } else if (stricmp(argv[i], "/noupper") == 0) { + opts->require_uppercase = false; + } else { + return invalid_arg(argv[i]); + } + i++; + } + *argi = i; + return EXIT_SUCCESS; +} + int main(int argc, const char *argv[]) { LPTSRDATA data = get_tsr_data(true); @@ -712,7 +820,6 @@ int main(int argc, const char *argv[]) if (argi >= argc || stricmp(argv[argi], "install") == 0) { uint8_t hash_chars = DEF_HASH_CHARS; bool high = true; - bool short_fnames = false; argi++; for (; argi < argc; argi++) { @@ -720,15 +827,6 @@ int main(int argc, const char *argv[]) high = false; } else if (stricmp(argv[argi], "high") == 0) { high = true; - } else if (stricmp(argv[argi], "short") == 0) { - short_fnames = true; - } else if (stricmp(argv[argi], "hash") == 0) { - if (argc > argi && strlen(argv[argi+1]) == 1 && isdigit(argv[argi+1][0])) { - hash_chars = argv[++argi][0] - '0'; - } - else { - return arg_required(argv[argi]); - } } else { return invalid_arg(argv[argi]); } @@ -749,7 +847,7 @@ int main(int argc, const char *argv[]) } else { deallocate_environment(_psp); } - err = configure_driver(data, short_fnames, hash_chars); + err = configure_driver(data); if (err) { if (high) cancel_reallocation(FP_SEG(data)); return EXIT_FAILURE; @@ -773,26 +871,26 @@ int main(int argc, const char *argv[]) if (!data) return driver_not_found(); return list_folders(data); } else if (stricmp(argv[argi], "mount") == 0) { + MOUNTOPTS opts; char *folder; char drive; - bool ci = true; if (!data) return driver_not_found(); argi++; if (argi >= argc) return arg_required("mount"); - if (stricmp(argv[argi], "/cs") == 0) { - ci = false; - argi++; - if (argi >= argc) return arg_required("mount"); - } + folder = (char *) argv[argi]; argi++; if (argi >= argc) return arg_required("mount"); drive = get_drive_letter(argv[argi]); if (!drive) return invalid_arg(argv[argi]); + set_mountopts_defaults(&opts); + err = parse_mountopts(&opts, &argi, argc, argv); + if (err) return err; + local_to_utf8(data, utf8name.buf, folder, utf8name.shflstr.u16Size); - return mount(data, utf8name.buf, drive, ci); + return mount(data, utf8name.buf, drive, &opts); } else if (stricmp(argv[argi], "umount") == 0 || stricmp(argv[argi], "unmount") == 0) { char drive; if (!data) return driver_not_found(); @@ -803,6 +901,24 @@ int main(int argc, const char *argv[]) if (!drive) return invalid_arg(argv[argi]); return unmount(data, drive); + } else if (stricmp(argv[argi], "remount") == 0) { + MOUNTOPTS opts; + char drive; + if (!data) return driver_not_found(); + + argi++; + if (argi >= argc) return arg_required("remount"); + drive = get_drive_letter(argv[argi]); + if (!drive) return invalid_arg(argv[argi]); + + err = get_mountopts(data, drive, &opts); + if (err) return err; + + argi++; + err = parse_mountopts(&opts, &argi, argc, argv); + if (err) return err; + + return set_mountopts(data, drive, &opts); } else if (stricmp(argv[argi], "rescan") == 0) { if (!data) return driver_not_found(); return rescan(data); @@ -234,12 +234,19 @@ static int my_strrchr(const char __far *str, char c) return last; } -static inline bool translate_filename_from_host(SHFLSTRING *str, bool case_insensitive, bool uppercase) +/** Transform filename from host encoding to DOS local codepage + * @param to_uppercase transform to uppercase filename + * @param require_uppercase fail conversion if any char in source string is not in uppercase + * @returns true if conversion was succesful + */ +static inline bool translate_filename_from_host(SHFLSTRING *str, bool require_uppercase, bool to_uppercase) { bool valid = utf8_to_local(&data, str->ach, str->ach, &str->u16Length); - if (uppercase) { - valid = (nls_uppercase(str) || case_insensitive) && valid; + if (to_uppercase) { + if (!nls_uppercase(str) && require_uppercase) { + valid = false; + } } return valid; @@ -257,7 +264,7 @@ static const char * get_basename(const char *path) /** Tries to do some very simple heuristics to convert DOS-style wildcards * into win32-like (as expected by VirtualBox). */ -static void fix_wildcards(SHFLSTRING *str) +static void fix_wildcards(SHFLSTRING *str, bool expand_tilde) { unsigned i; @@ -287,39 +294,30 @@ static void fix_wildcards(SHFLSTRING *str) (str->ach[i-1] == '*' || str->ach[i-1] == '?') ) { str->ach[i] = '*'; } - } - // If there is a '~' in the pattern search, assume that this is a shortened - // file name. Nothing to do for windows short names, as they exist in the - // host, but for the hash ones, we have to replace the tilde and any following - // characters with an '*' - if (!data.short_fnames) { - for (i = 0; i < str->u16Length; i++) { - if ( str->ach[i] == '~') { - strcpy(&str->ach[i], "*"); - str->u16Length = i + 1; - break; - } + // If there is a '~' in the pattern search, assume that this is a shortened + // file name. We have to replace the tilde and any following characters with an '*'. + if (expand_tilde && str->ach[i] == '~') { + strcpy(&str->ach[i], "*"); + str->u16Length = i + 1; + break; } } } - -static void copy_drive_relative_filename(SHFLROOT root, SHFLSTRING *str, char __far *path) +static void translate_filename_to_host(int drive, SHFLSTRING *str, char __far *path) { - // Assume X:.... path for now, i.e. drive_relative path starts at char - - str->u16Length = get_true_host_name( root, &data, str->ach, path + 2, str->u16Size ); - + // Assume X:.... path for now, i.e. drive_relative path starts at char 2 + str->u16Length = get_true_host_name(&data, drive, str->ach, path + 2, str->u16Size); } -static void copy_drive_relative_dirname(SHFLROOT root, SHFLSTRING *str, char __far *path) +static void translate_dirname_to_host(int drive, SHFLSTRING *str, char __far *path) { int last_sep = my_strrchr(path + 2, '\\'); if (last_sep >= 0) { - str->u16Length = get_true_host_name_n( root, &data, str->ach, path + 2, str->u16Size, last_sep == 0 ? 1 : last_sep ); + str->u16Length = get_true_host_name_n(&data, drive, str->ach, path + 2, str->u16Size, last_sep == 0 ? 1 : last_sep); } else { - str->u16Length = get_true_host_name( root, &data, str->ach, path + 2, str->u16Size ); + str->u16Length = get_true_host_name(&data, drive, str->ach, path + 2, str->u16Size); } } @@ -513,7 +511,7 @@ static void handle_create_open_ex(union INTPACK __far *r) return; } - copy_drive_relative_filename(root, &shflstr.shflstr, path); + translate_filename_to_host(drive, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); if (action & OPENEX_REPLACE_IF_EXISTS) { @@ -857,7 +855,7 @@ static void handle_delete(union INTPACK __far *r) dprintf("handle_delete %Fs\n", path); - copy_drive_relative_filename(root, &shflstr.shflstr, path); + translate_filename_to_host(drive, &shflstr.shflstr, path); err = vbox_shfl_remove(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr, SHFL_REMOVE_FILE); @@ -885,10 +883,10 @@ static void handle_rename(union INTPACK __far *r) return; } - copy_drive_relative_filename(root, &shflstr.shflstr, src); + translate_filename_to_host(srcdrive, &shflstr.shflstr, src); // Reusing shfldirinfo buffer space here for our second filename - copy_drive_relative_filename(root, &shfldirinfo.dirinfo.name, dst); + translate_filename_to_host(dstdrive, &shfldirinfo.dirinfo.name, dst); err = vbox_shfl_rename(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr, &shfldirinfo.dirinfo.name, @@ -910,7 +908,7 @@ static void handle_getattr(union INTPACK __far *r) dprintf("handle_getattr %Fs\n", path); - copy_drive_relative_filename(root, &shflstr.shflstr, path); + translate_filename_to_host(drive, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); parms.create.CreateFlags = SHFL_CF_LOOKUP; @@ -946,13 +944,14 @@ static void handle_setattr(union INTPACK __far *r) /** Opens directory corresponding to file in path (i.e. we use the dirname), * filling corresponding openfile entry. */ -static vboxerr open_search_dir(unsigned openfile, SHFLROOT root, char __far *path) +static vboxerr open_search_dir(unsigned openfile, int drive, char __far *path) { + SHFLROOT root = data.drives[drive].root; vboxerr err; dprintf("open_search_dir openfile=%u path=%Fs\n", openfile, path); - copy_drive_relative_dirname(root, &shflstr.shflstr, path); + translate_dirname_to_host(drive, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); parms.create.CreateFlags = SHFL_CF_DIRECTORY @@ -1018,9 +1017,7 @@ static vboxerr find_next_from_vbox(unsigned openfile, char __far *path) DOSDIR __far *found_file = &data.dossda->found_file; uint16_t search_mask; vboxerr err; - uint32_t hash; int drive; - bool case_insensitive; // Always accept files with these attributes, even if mask says otherwise search_mask = ~(sdb->search_attr | _A_ARCH | _A_RDONLY); @@ -1029,12 +1026,10 @@ static vboxerr find_next_from_vbox(unsigned openfile, char __far *path) // a correct absolute mask with the correct wildcards; // this is what VirtualBox will use in future calls. if (path) { - SHFLROOT root; drive = drive_letter_to_index(path[0]); - root = data.drives[drive].root; - copy_drive_relative_filename(root, &shflstr.shflstr, path); - fix_wildcards(&shflstr.shflstr); + translate_filename_to_host(drive, &shflstr.shflstr, path); + fix_wildcards(&shflstr.shflstr, data.drives[drive].opt.generate_sfn); dprintf("fixed path=%s\n", shflstr.buf); @@ -1049,7 +1044,6 @@ static vboxerr find_next_from_vbox(unsigned openfile, char __far *path) shflstring_strcpy(&shflstr.shflstr, " "); } - case_insensitive = data.drives[drive].case_insensitive; while (1) { // Loop until we have a valid file (or an error) unsigned size = sizeof(shfldirinfo), resume = 0, count = 0; @@ -1095,9 +1089,10 @@ static vboxerr find_next_from_vbox(unsigned openfile, char __far *path) // Now convert the filename // Calculate hash using host file name - hash = lfn_name_hash( shfldirinfo.dirinfo.name.ach, shfldirinfo.dirinfo.name.u16Length ); - if (data.short_fnames && shfldirinfo.dirinfo.cucShortName != 0) { + + if (data.drives[drive].opt.use_host_sfn && shfldirinfo.dirinfo.cucShortName != 0) { + // Use shortname directly provided by host OS valid = utf16_to_local( &data, &shfldirinfo.dirinfo.name.ach, &shfldirinfo.dirinfo.uszShortName, shfldirinfo.dirinfo.cucShortName); if (!valid) { @@ -1108,19 +1103,33 @@ static vboxerr find_next_from_vbox(unsigned openfile, char __far *path) shfldirinfo.dirinfo.name.u16Length = shfldirinfo.dirinfo.cucShortName; dprintf(" Host short filename: '%s'\n", shfldirinfo.dirinfo.name.ach); } else { - valid = translate_filename_from_host(&shfldirinfo.dirinfo.name, case_insensitive, true); + valid = translate_filename_from_host(&shfldirinfo.dirinfo.name, data.drives[drive].opt.require_uppercase, true); } - + if (valid) { + // Does it fit into a 8.3 filename? if (!copy_to_8_3_filename(found_file->filename, &shfldirinfo.dirinfo.name)) { - dputs("Mangling long filename"); - mangle_to_8_3_filename(hash, found_file->filename, &shfldirinfo.dirinfo.name); + dputs(" detected long filename"); + valid = false; } } else { - dputs("Mangling filename with illegal character(s)"); - mangle_to_8_3_filename(hash, found_file->filename, &shfldirinfo.dirinfo.name); + dputs(" detected invalid characters in filename"); + } + + if (data.drives[drive].opt.generate_sfn && !valid) { + uint8_t hash_chars = data.drives[drive].opt.hash_chars; + uint32_t hash = lfn_name_hash(shfldirinfo.dirinfo.name.ach, shfldirinfo.dirinfo.name.u16Length, hash_chars); + dputs("mangling long filename"); + mangle_to_8_3_filename(hash, hash_chars, found_file->filename, &shfldirinfo.dirinfo.name); + valid = true; + } + + if (!valid) { + dputs("hiding file with invalid filename"); + continue; } + // Compare short filename against DOS wildcard pattern if (!matches_8_3_wildcard(found_file->filename, sdb->search_templ)) { dputs("hiding file with unwanted filename"); continue; @@ -1186,7 +1195,7 @@ static void handle_find_first(union INTPACK __far *r) return; } - err = open_search_dir(openfile, root, path); + err = open_search_dir(openfile, drive, path); if (err) { set_vbox_err(r, err); return; @@ -1268,7 +1277,7 @@ static void handle_chdir(union INTPACK __far *r) dprintf("handle_chdir %Fs\n", path); // Just have to check if the directory exists - copy_drive_relative_filename(root, &shflstr.shflstr, path); + translate_filename_to_host(drive, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); parms.create.CreateFlags = SHFL_CF_LOOKUP; @@ -1307,7 +1316,7 @@ static void handle_mkdir(union INTPACK __far *r) dprintf("handle_mkdir %Fs\n", path); - copy_drive_relative_filename(root, &shflstr.shflstr, path); + translate_filename_to_host(drive, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); parms.create.CreateFlags = SHFL_CF_DIRECTORY @@ -1347,7 +1356,7 @@ static void handle_rmdir(union INTPACK __far *r) dprintf("handle_rmdir %Fs\n", path); - copy_drive_relative_filename(root, &shflstr.shflstr, path); + translate_filename_to_host(drive, &shflstr.shflstr, path); err = vbox_shfl_remove(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr, SHFL_REMOVE_DIR); @@ -41,12 +41,38 @@ #define BYTES_PER_SECTOR 4096 #define BYTES_PER_CLUSTER (SECTORS_PER_CLUSTER * BYTES_PER_SECTOR) +/** Parameters for LFN names with hash (i.e. XXXX~HHH.XXX). */ +#define DEF_HASH_CHARS 3 +#define MIN_HASH_CHARS 2 +#define MAX_HASH_CHARS 6 + /** Size of the VBox buffer. The maximum message length that may be sent. - * Enough to fit an HGCM connect call, which is actually larger than most other calls we use ( <= 7 args ). */ + * Enough to fit an HGCM connect call, which is actually larger than the other calls we use ( <= 7 args ). */ #define VBOX_BUFFER_SIZE (200) #define INVALID_OPENFILE (-1) +/** Options available for each mounted shared folder. */ +typedef struct { + /** Number of characters to use for the hash part of generated SFNs (between MIN_HASH_CHARS and MAX_HASH_CHARS). */ + uint8_t hash_chars : 3; + /** Use short filenames provided by host. */ + bool use_host_sfn : 1 ; + /** Generate (hash-based) short filenames. If false, files with LFN will not appear. */ + bool generate_sfn : 1; + /** Generate hash SFNs for any filename with non-uppercase chars, even if otherwise fits in 8.3. */ + bool require_uppercase : 1; +} MOUNTOPTS; + +/** Struct representing a (potentially) mounted drive. */ +typedef struct { + /** VirtualBox "root" for this drive, or NIL if unmounted. */ + uint32_t root; + /** Mount options. */ + MOUNTOPTS opt; +} MOUNTEDDRIVE; + +/** Struct representing a (potentially) open file. */ typedef struct { uint32_t root; uint64_t handle; @@ -72,18 +98,10 @@ typedef struct { FCHAR __far *file_char; /** Codepage to unicode lookup table. */ uint16_t unicode_table[128]; - /** LFN support */ - bool short_fnames; - uint8_t hash_chars; // Current status /** Array of all possible DOS drives. */ - struct { - /** VirtualBox "root" for this drive, or NIL if unmounted. */ - uint32_t root; - /** Host file system is case sensitive flag. */ - bool case_insensitive; - } drives[NUM_DRIVES]; + MOUNTEDDRIVE drives[NUM_DRIVES]; /** All currently open files. */ OPENFILE files[NUM_FILES]; @@ -42,7 +42,7 @@ static inline uint8_t lookup_codepage( TSRDATAPTR data, uint16_t cp ) // dst and src CAN'T BE THE SAME !!!! // Returns resulting length or 0 if buffer overflow // -static uint16_t local_to_utf8_n( TSRDATAPTR data, uint8_t *dst, char far *src, uint16_t buflen, uint16_t count ) +static uint16_t local_to_utf8_n( TSRDATAPTR data, uint8_t *dst, const char far *src, uint16_t buflen, uint16_t count ) { uint16_t len = 0; // Resulting length uint16_t cp; // Unicode Code Point @@ -25,12 +25,13 @@ typedef uint32_t hgcm_client_id_t; +/** Busy-waits until the request is marked as complete by VirtualBox. */ 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? + // TODO yield guest CPU somehow? } } |