From 80f0debf2032e16629cb13682b8fb9ceccde423c Mon Sep 17 00:00:00 2001 From: Eduardo Casino Date: Tue, 24 May 2022 07:58:48 +0200 Subject: Add support for hash-generated short file names --- lfn.h | 442 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nls.h | 11 +- sfmain.c | 34 ++++- sftsr.c | 132 +++++++++++-------- sftsr.h | 5 +- unicode.h | 12 +- 6 files changed, 573 insertions(+), 63 deletions(-) create mode 100644 lfn.h diff --git a/lfn.h b/lfn.h new file mode 100644 index 0000000..d58807e --- /dev/null +++ b/lfn.h @@ -0,0 +1,442 @@ +/* + * VBSF - Long File Name support + * Copyright (C) 2011-2022 Eduardo Casino + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General 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 License for more details. + * + * You should have received a copy of the GNU General 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 LFN_H +#define LFN_H + +#include +#include "unicode.h" +#include "vboxshfl.h" +#include "sftsr.h" + +#define REVERSE_HASH 1 + +#define DEF_HASH_CHARS 3 +#define MIN_HASH_CHARS 2 +#define MAX_HASH_CHARS 6 + +#ifdef __IN_SFTSR__ + +static inline bool translate_filename_from_host(SHFLSTRING *, bool); +static bool matches_8_3_wildcard(const char __far *, const char __far *); +static int my_strrchr(const char __far *, char); + +/** Private buffer for resolving VirtualBox long filenames. */ +static SHFLSTRING_WITH_BUF(shflstrlfn, SHFL_MAX_LEN); + +static char fcb_name[12]; + +static char __far *_fstrchr_local(const char __far *str, char c) +{ + int i; + + for (i = 0; str[i] != '\0' && str[i] != c; ++i) + ; + + return (char __far *)&str[i]; +} + +static inline char __far *_fstrrchr_local(const char __far *str, char c) +{ + int i; + + for (i = 0; str[i] != '\0'; ++i) + ; + for (; i && str[i] != c; --i) + ; + + return (char __far *)&str[i]; +} + +static inline char *_fstrcpy_local(char *dst, const char __far *src) +{ + while (*dst++ = *src++) + ; + + return dst - 1; +} + +static inline char hex_digit(uint8_t val) +{ + return val > 9 ? val + 55 : val + 48; +} + +/* + * NOTE: The hash algorithm is from sdbm, a public domain clone of ndbm + * by Ozan Yigit (http://www.cse.yorku.ca/~oz/), with the result + * folded to the desired hash lengh (method from + * http://isthe.com/chongo/tech/comp/fnv). Hence, lfn_name_hash() is + * also in the PUBLIC DOMAIN. (The actual code is fom the version + * 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 +) +{ + uint8_t *be = name + len; /* beyond end of buffer */ + uint32_t hval = 0; + uint8_t hash_len = data.hash_chars << 2; + + while (name < be) + { + /* It seems that calculating the hash backwards gives an slightly + * better dispersion for files with names which are the same in their + * first part ( like image_000001.jpg, image_000002.jpg, ... ) + */ +#ifdef REVERSE_HASH + hval = *--be + (hval << 6) + (hval << 16) - hval; +#else + hval = *name++ + (hval << 6) + (hval << 16) - hval; +#endif + } + + // Fold result to the desired len + hval = (((hval >> hash_len) ^ hval) & FOLD_MASK(hash_len)); + + return hval; +} + +// 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 + char __far *fcb_name, // out : File name in FCB format. Must be 11 bytes long + SHFLSTRING *str // in : File name in DOS codepage +) +{ + char *d, *p, __far *s, *fname = str->ach; + int i; + uint8_t mangle = data.hash_chars; + static int ror[] = {0, 4, 8, 12, 16, 20}; + + *(uint32_t __far *)(&fcb_name[0]) = 0x20202020; + *(uint32_t __far *)(&fcb_name[4]) = 0x20202020; + *(uint16_t __far *)(&fcb_name[8]) = 0X2020; + *(uint8_t __far *)(&fcb_name[10]) = 0X20; + + // First, skip leading dots and spaces + // + while (*fname == ' ' || *fname == '.') + { + ++fname; + } + + // Then look for the last dot in the filename + // + d = p = (char *)_fstrrchr_local(fname, '.'); + + // There is an extension + // + if (p != fname) + { + *p = '\0'; + i = 0; + s = &fcb_name[8]; + while (*++p != '\0' && i < 3) + { + if (*p != ' ') + { + *s++ = illegal_char(*p) ? '_' : nls_toupper(*p); + ++i; + } + } + } + + i = 0; + s = fcb_name; + p = fname; + while (*p && i < 8) + { + if (*p != ' ' && *p != '.') + { + *s++ = illegal_char(*p) ? '_' : nls_toupper(*p); + ++i; + } + ++p; + } + + if (*d == '\0') + { + *d = '.'; + } + + // It is 7 because 1 char is for the tilde '~' + // + if (i > 7 - mangle) + { + i = 7 - mangle; + } + + fcb_name[i] = '~'; + + while (mangle--) + { + fcb_name[++i] = hex_digit((hash >> ror[mangle]) & 0xf); + } +} + +static inline bool match_to_8_3_filename(const char __far *dos_filename, const char *fcb_name) +{ + int len; + + for (len = 0; dos_filename[len] != '.' && dos_filename[len] != '\0' && len < 8; ++len) + { + if (dos_filename[len] != fcb_name[len]) + { + return false; + } + } + + // No extension + if (dos_filename[len] == '\0') + { + while (len < 11) + { + if (fcb_name[len++] != ' ') + { + return false; + } + } + return true; + } + + if (dos_filename[len] == '.') + { + int len2 = len + 1; + + while (len < 8) + { + if (fcb_name[len++] != ' ') + { + return false; + } + } + + while (dos_filename[len2] != '\0' && len < 11) + { + if (dos_filename[len2++] != fcb_name[len++]) + { + return false; + } + } + while (len < 11) + { + if (fcb_name[len++] != ' ') + { + return false; + } + } + return true; + } + + // Should not reach here + return false; +} + +static inline char *find_real_name( + SHFLROOT root, + TSRDATAPTR data, + char *dest, // out : destination buffer + char __far *path, // in : search path + char __far *filename, // in : DOS file name + uint16_t namelen, // in : file name length + uint16_t bufsiz // in : buffer size +) +{ + vboxerr err; + + dprintf("find_real_name path=%Fs filename=%Fs\n", path, filename); + + memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); + parms.create.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ; + shflstring_strcpy(&shflstrlfn.shflstr, path); + + err = vbox_shfl_open(&data->vb, data->hgcm_client_id, root, &shflstrlfn.shflstr, &parms.create); + if (err) + { + dputs("open search dir failed"); + goto not_found; + } + + // Check there is room for appending "\*" to the path + if (shflstrlfn.shflstr.u16Size < shflstrlfn.shflstr.u16Length + 2) + { + dputs("path too long"); + vbox_shfl_close(&data->vb, data->hgcm_client_id, root, parms.create.Handle); + goto not_found; + } + + *(uint16_t *)&shflstrlfn.shflstr.ach[shflstrlfn.shflstr.u16Length] = '*\\'; + shflstrlfn.shflstr.u16Length += 2; + shflstrlfn.shflstr.ach[shflstrlfn.shflstr.u16Length] = '\0'; + + for (;;) + { + unsigned size = sizeof(shfldirinfo), resume = 0, count = 0; + char *d; + uint32_t hash; + bool valid; + + err = vbox_shfl_list(&data->vb, data->hgcm_client_id, root, parms.create.Handle, + SHFL_LIST_RETURN_ONE, &size, &shflstrlfn.shflstr, &shfldirinfo.dirinfo, + &resume, &count); + + // Reset the size of the buffer + shfldirinfo.dirinfo.name.u16Size = sizeof(shfldirinfo.buf); + + if (err) + { + dputs("vbox_shfl_list() failed"); + vbox_shfl_close(&data->vb, data->hgcm_client_id, root, parms.create.Handle); + break; + } + + // Calculate hash using host file name + hash = lfn_name_hash(shfldirinfo.dirinfo.name.ach, shfldirinfo.dirinfo.name.u16Length); + + // Copy now, because translate_filename_from_host() converts fName to DOS codepage + // + if (shfldirinfo.dirinfo.name.u16Length > bufsiz) + { + vbox_shfl_close(&data->vb, data->hgcm_client_id, root, parms.create.Handle); + return (char *)0; + } + d = _fstrcpy_local(dest, &shfldirinfo.dirinfo.name.ach); + + translate_filename_from_host(&shfldirinfo.dirinfo.name, false); + mangle_to_8_3_filename(hash, fcb_name, &shfldirinfo.dirinfo.name); + + if (match_to_8_3_filename(filename, fcb_name)) + { + vbox_shfl_close(&data->vb, data->hgcm_client_id, root, parms.create.Handle); + return d; + } + } + +not_found: + if (namelen > bufsiz) + { + return (char *)0; + } + else + { + return _fstrcpy_local(dest, filename); + } +} + +static uint16_t get_true_host_name_n(SHFLROOT root, TSRDATAPTR data, 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) + { + return local_to_utf8_n(data, dst, src, buflen, count); + } + + if (count < buflen) + { + save_end = src[count]; + src[count] = '\0'; + } + + dprintf("get_true_host_name_n() src: %Fs\n", src); + + s = src; + d = dst; + + tl = _fstrchr_local(src, '~'); + + while (*tl != '\0') + { + // tl + // | + // ps ---+ | +--- ns + // | | | + // /path/fil~enam/ + // 0123456789 + + *tl = '\0'; + ps = _fstrrchr_local(s, '\\'); + *tl = '~'; + + ns = _fstrchr_local(tl, '\\'); + + save_ns = *ns; // Can be '\\' or '\0' + *ps = *ns = '\0'; + + ret = local_to_utf8(data, d, s, buflen - len); + + d += ret; + len += ret; + + if (buflen <= len) + { + goto return_name; + } + + *d++ = '\\'; + *d = '\0'; + ++len; + + d = find_real_name(root, data, d, dst, ps + 1, (uint16_t)(ns - ps + 1), buflen - len); + + if (d == (char *)0) + { + goto return_name; + } + + len = (uint16_t)(d - dst); + + *d = '\0'; + + *ps = '\\'; + *ns = save_ns; + + tl = _fstrchr_local(ns, '~'); + s = ns; + } + + len += local_to_utf8(data, d, s, buflen - len); + +return_name: + if (count < buflen) + { + src[count] = save_end; + } + + dprintf("True host name: '%s', len: %d\n", dst, len); + + return len; +} + +static inline uint16_t get_true_host_name(SHFLROOT root, TSRDATAPTR data, uint8_t *dst, char __far *src, uint16_t buflen) +{ + return get_true_host_name_n(root, data, dst, src, buflen, buflen); +} + +#endif // __IN_SFTSR__ +#endif // LFN_H diff --git a/nls.h b/nls.h index 9699bb1..124b703 100644 --- a/nls.h +++ b/nls.h @@ -50,14 +50,21 @@ static unsigned char nls_toupper( unsigned char c ) return ( c < 0x80 ? c : data.file_upper_case[c - 0x80] ); } -static inline void nls_uppercase(SHFLSTRING *str) +static inline bool nls_uppercase(SHFLSTRING *str) { int i; + bool upper_case = true; for (i= 0; i < str->u16Length; ++i) { - str->ach[i] = nls_toupper(str->ach[i]); + unsigned char upper = nls_toupper(str->ach[i]); + if (upper != str->ach[i]) { + str->ach[i] = upper; + upper_case = false; + } } + + return upper_case; } static inline bool valid_8_3_file_chars(char __far *fname ) diff --git a/sfmain.c b/sfmain.c index 3cbd336..68029c1 100644 --- a/sfmain.c +++ b/sfmain.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "version.h" #include "dlog.h" @@ -31,6 +32,7 @@ #include "dostsr.h" #include "sftsr.h" #include "unicode.h" +#include "lfn.h" static char get_drive_letter(const char *path) { if (!path || path[0] == '\0') return '\0'; @@ -145,6 +147,7 @@ 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; + unsigned flags = SHFL_MIQF_DRIVE_LETTER, version = 0; shflstring_strcpy(&str.shflstr, folder); @@ -156,6 +159,16 @@ static int mount_shfl(LPTSRDATA data, int drive, const char *folder) data->drives[drive].root = root; + err = vbox_shfl_query_map_info(&data->vb, data->hgcm_client_id, root, + &str.shflstr, &str.shflstr, &flags, &version); + if (err) { + printf("Error on Query Map Info for mounted drive, err=%ld\n", err); + return -1; + } + + // This is not a bug! VirtualBox sets SHFL_MIF_HOST_ICASE if host file system is case sensitive + data->drives[drive].case_insensitive = ~(flags & SHFL_MIF_HOST_ICASE); + return 0; } @@ -452,7 +465,7 @@ error: } -static int configure_driver(LPTSRDATA data, bool short_fnames) +static int configure_driver(LPTSRDATA data, bool short_fnames, uint8_t hash_chars) { unsigned i; int32_t err; @@ -491,6 +504,9 @@ static int configure_driver(LPTSRDATA data, bool short_fnames) // 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) { @@ -622,11 +638,15 @@ static void print_help(void) " install install the driver (default)\n" " low install in conventional memory (otherwise UMB)\n" " short use short file names from windows hosts\n" + " hash number of hash generated chars following the '~'\n" + " for generating DOS valid files\n" + " (%d min, %d max, %d default)\n" " uninstall uninstall the driver from memory\n" " list list available shared folders\n" " mount mount a shared folder into drive X:\n" " umount unmount shared folder from drive X:\n" - " rescan unmount everything and recreate automounts\n" + " rescan unmount everything and recreate automounts\n", + MIN_HASH_CHARS, MAX_HASH_CHARS, DEF_HASH_CHARS ); } @@ -672,6 +692,7 @@ int main(int argc, const char *argv[]) int err, argi = 1; if (argi >= argc || stricmp(argv[argi], "install") == 0) { + uint8_t hash_chars = DEF_HASH_CHARS; bool high = true; bool short_fnames = false; @@ -683,6 +704,13 @@ int main(int argc, const char *argv[]) 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 invalid_arg(argv[argi]); + } } else { return invalid_arg(argv[argi]); } @@ -703,7 +731,7 @@ int main(int argc, const char *argv[]) } else { deallocate_environment(_psp); } - err = configure_driver(data, short_fnames); + err = configure_driver(data, short_fnames, hash_chars); if (err) { if (high) cancel_reallocation(FP_SEG(data)); return EXIT_FAILURE; diff --git a/sftsr.c b/sftsr.c index b6d89c2..9282c58 100644 --- a/sftsr.c +++ b/sftsr.c @@ -54,9 +54,12 @@ TSRDATA data = { 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 } }; -#define __IN_SFTSR__ 1 -#include "unicode.h" -#include "nls.h" +static union { + SHFLVOLINFO volinfo; + SHFLFSOBJINFO objinfo; + SHFLCREATEPARMS create; +} parms; + /** Private buffer for VirtualBox filenames. */ static SHFLSTRING_WITH_BUF(shflstr, SHFL_MAX_LEN); @@ -64,11 +67,10 @@ static SHFLSTRING_WITH_BUF(shflstr, SHFL_MAX_LEN); /** Private buffer where we store VirtualBox-obtained dir entries. */ static SHFLDIRINFO_WITH_NAME_BUF(shfldirinfo, SHFL_MAX_LEN); -static union { - SHFLVOLINFO volinfo; - SHFLFSOBJINFO objinfo; - SHFLCREATEPARMS create; -} parms; +#define __IN_SFTSR__ 1 +#include "unicode.h" +#include "nls.h" +#include "lfn.h" static uint8_t map_shfl_attr_to_dosattr(const SHFLFSOBJATTR *a) { @@ -233,13 +235,11 @@ static int my_strrchr(const char __far *str, char c) return last; } -static inline bool translate_filename_from_host(SHFLSTRING *str) +static inline bool translate_filename_from_host(SHFLSTRING *str, bool case_insensitive) { - bool valid; + bool valid = utf8_to_local(&data, str->ach, str->ach, &str->u16Length); - valid = utf8_to_local( &data, str->ach, str->ach, &str->u16Length); - - nls_uppercase(str); + valid = (nls_uppercase(str) || case_insensitive) && valid; return valid; } @@ -287,22 +287,38 @@ static void fix_wildcards(SHFLSTRING *str) 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; + } + } + } } -static void copy_drive_relative_filename(SHFLSTRING *str, const char __far *path) +static void copy_drive_relative_filename(SHFLROOT root, SHFLSTRING *str, char __far *path) { - // Assume X:.... path for now, i.e. drive_relative path starts at char 2 - str->u16Length = local_to_utf8( &data, str->ach, path + 2, str->u16Size ); + // 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 ); + } -static void copy_drive_relative_dirname(SHFLSTRING *str, const char __far *path) +static void copy_drive_relative_dirname(SHFLROOT root, SHFLSTRING *str, char __far *path) { int last_sep = my_strrchr(path + 2, '\\'); if (last_sep >= 0) { - str->u16Length = local_to_utf8_n( &data, str->ach, path + 2, str->u16Size, last_sep == 0 ? 1 : last_sep ); + str->u16Length = get_true_host_name_n( root, &data, str->ach, path + 2, str->u16Size, last_sep == 0 ? 1 : last_sep ); } else { - str->u16Length = local_to_utf8( &data, str->ach, path + 2, str->u16Size ); + str->u16Length = get_true_host_name( root, &data, str->ach, path + 2, str->u16Size ); } } @@ -460,7 +476,7 @@ static void flush_sft_metadata(DOSSFT __far *sft) static void handle_create_open_ex(union INTPACK __far *r) { - const char __far *path = data.dossda->fn1; + 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->w.es, r->w.di); @@ -496,7 +512,7 @@ static void handle_create_open_ex(union INTPACK __far *r) return; } - copy_drive_relative_filename(&shflstr.shflstr, path); + copy_drive_relative_filename(root, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); if (action & OPENEX_REPLACE_IF_EXISTS) { @@ -833,14 +849,14 @@ static void handle_close_all(union INTPACK __far *r) static void handle_delete(union INTPACK __far *r) { - const char __far *path = data.dossda->fn1; + char __far *path = data.dossda->fn1; int drive = drive_letter_to_index(path[0]); SHFLROOT root = data.drives[drive].root; vboxerr err; dprintf("handle_delete %Fs\n", path); - copy_drive_relative_filename(&shflstr.shflstr, path); + copy_drive_relative_filename(root, &shflstr.shflstr, path); err = vbox_shfl_remove(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr, SHFL_REMOVE_FILE); @@ -854,9 +870,9 @@ static void handle_delete(union INTPACK __far *r) static void handle_rename(union INTPACK __far *r) { - const char __far *src = data.dossda->fn1; + char __far *src = data.dossda->fn1; int srcdrive = drive_letter_to_index(src[0]); - const char __far *dst = data.dossda->fn2; + char __far *dst = data.dossda->fn2; int dstdrive = drive_letter_to_index(dst[0]); SHFLROOT root = data.drives[srcdrive].root; vboxerr err; @@ -868,10 +884,10 @@ static void handle_rename(union INTPACK __far *r) return; } - copy_drive_relative_filename(&shflstr.shflstr, src); + copy_drive_relative_filename(root, &shflstr.shflstr, src); // Reusing shfldirinfo buffer space here for our second filename - copy_drive_relative_filename(&shfldirinfo.dirinfo.name, dst); + copy_drive_relative_filename(root, &shfldirinfo.dirinfo.name, dst); err = vbox_shfl_rename(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr, &shfldirinfo.dirinfo.name, @@ -886,14 +902,14 @@ static void handle_rename(union INTPACK __far *r) static void handle_getattr(union INTPACK __far *r) { - const char __far *path = data.dossda->fn1; + char __far *path = data.dossda->fn1; int drive = drive_letter_to_index(path[0]); SHFLROOT root = data.drives[drive].root; vboxerr err; dprintf("handle_getattr %Fs\n", path); - copy_drive_relative_filename(&shflstr.shflstr, path); + copy_drive_relative_filename(root, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); parms.create.CreateFlags = SHFL_CF_LOOKUP; @@ -929,13 +945,13 @@ 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, const char __far *path) +static vboxerr open_search_dir(unsigned openfile, SHFLROOT root, char __far *path) { vboxerr err; dprintf("open_search_dir openfile=%u path=%Fs\n", openfile, path); - copy_drive_relative_dirname(&shflstr.shflstr, path); + copy_drive_relative_dirname(root, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); parms.create.CreateFlags = SHFL_CF_DIRECTORY @@ -980,7 +996,7 @@ static vboxerr find_volume_label(SHFLROOT root) err = vbox_shfl_query_map_name(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr); if (err) return err; - (void) translate_filename_from_host(&shflstr.shflstr); + (void) translate_filename_from_host(&shflstr.shflstr, false); dprintf("label: %s\n", shflstr.buf); @@ -995,12 +1011,15 @@ static vboxerr find_volume_label(SHFLROOT root) } /** Gets and fills in the next directory entry from VirtualBox. */ -static vboxerr find_next_from_vbox(unsigned openfile, const char __far *path) +static vboxerr find_next_from_vbox(unsigned openfile, char __far *path) { + int drive = drive_letter_to_index(path[0]); + SHFLROOT root = data.drives[drive].root; DOSSDB __far *sdb = &data.dossda->sdb; DOSDIR __far *found_file = &data.dossda->found_file; uint16_t search_mask; vboxerr err; + uint32_t hash; // Always accept files with these attributes, even if mask says otherwise search_mask = ~(sdb->search_attr | _A_ARCH | _A_RDONLY); @@ -1009,7 +1028,7 @@ static vboxerr find_next_from_vbox(unsigned openfile, const char __far *path) // a correct absolute mask with the correct wildcards; // this is what VirtualBox will use in future calls. if (path) { - copy_drive_relative_filename(&shflstr.shflstr, path); + copy_drive_relative_filename(root, &shflstr.shflstr, path); fix_wildcards(&shflstr.shflstr); dprintf("fixed path=%s\n", shflstr.buf); @@ -1026,6 +1045,7 @@ static vboxerr find_next_from_vbox(unsigned openfile, const char __far *path) while (1) { // Loop until we have a valid file (or an error) unsigned size = sizeof(shfldirinfo), resume = 0, count = 0; + bool valid; err = vbox_shfl_list(&data.vb, data.hgcm_client_id, data.files[openfile].root, data.files[openfile].handle, @@ -1065,9 +1085,14 @@ static vboxerr find_next_from_vbox(unsigned openfile, const char __far *path) } // Now convert the filename - // Use the short filename if available from a windows host + + // 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 (!utf16_to_local( &data, &shfldirinfo.dirinfo.name.ach, &shfldirinfo.dirinfo.uszShortName, shfldirinfo.dirinfo.cucShortName)) { + valid = utf16_to_local( &data, &shfldirinfo.dirinfo.name.ach, &shfldirinfo.dirinfo.uszShortName, shfldirinfo.dirinfo.cucShortName); + + if (!valid) { // Should not happen as Windows short names are pure ascii dputs("hiding file with illegal character(s)"); continue; @@ -1075,14 +1100,19 @@ static vboxerr find_next_from_vbox(unsigned openfile, const char __far *path) shfldirinfo.dirinfo.name.u16Length = shfldirinfo.dirinfo.cucShortName; dprintf(" Host short filename: '%s'\n", shfldirinfo.dirinfo.name.ach); } - else if (!translate_filename_from_host(&shfldirinfo.dirinfo.name)) { - dputs("hiding file with illegal character(s)"); - continue; + else { + valid = translate_filename_from_host(&shfldirinfo.dirinfo.name, data.drives[drive].case_insensitive); } - - if (!copy_to_8_3_filename(found_file->filename, &shfldirinfo.dirinfo.name)) { - dputs("hiding file with long filename"); - continue; + + if (valid) { + 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); + } + } + else { + dputs("Mangling filename with illegal character(s)"); + mangle_to_8_3_filename(hash, found_file->filename, &shfldirinfo.dirinfo.name); } if (!matches_8_3_wildcard(found_file->filename, sdb->search_templ)) { @@ -1107,7 +1137,7 @@ static vboxerr find_next_from_vbox(unsigned openfile, const char __far *path) * Return the results in dossda.found_file (see find_next). */ static void handle_find_first(union INTPACK __far *r) { - const char __far *path = data.dossda->fn1; + char __far *path = data.dossda->fn1; const char __far *search_mask = data.dossda->fcb_fn1; int drive = drive_letter_to_index(path[0]); SHFLROOT root = data.drives[drive].root; @@ -1141,7 +1171,7 @@ static void handle_find_first(union INTPACK __far *r) dputs("search volid OK"); clear_dos_err(r); return; - } + } // First, open the desired directory for searching openfile = find_free_openfile(); @@ -1224,7 +1254,7 @@ static void handle_find_next(union INTPACK __far *r) static void handle_chdir(union INTPACK __far *r) { - const char __far *path = data.dossda->fn1; + char __far *path = data.dossda->fn1; int drive = drive_letter_to_index(path[0]); SHFLROOT root = data.drives[drive].root; vboxerr err; @@ -1232,7 +1262,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(&shflstr.shflstr, path); + copy_drive_relative_filename(root, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); parms.create.CreateFlags = SHFL_CF_LOOKUP; @@ -1264,14 +1294,14 @@ static void handle_chdir(union INTPACK __far *r) static void handle_mkdir(union INTPACK __far *r) { - const char __far *path = data.dossda->fn1; + char __far *path = data.dossda->fn1; int drive = drive_letter_to_index(path[0]); SHFLROOT root = data.drives[drive].root; vboxerr err; dprintf("handle_mkdir %Fs\n", path); - copy_drive_relative_filename(&shflstr.shflstr, path); + copy_drive_relative_filename(root, &shflstr.shflstr, path); memset(&parms.create, 0, sizeof(SHFLCREATEPARMS)); parms.create.CreateFlags = SHFL_CF_DIRECTORY @@ -1304,14 +1334,14 @@ static void handle_mkdir(union INTPACK __far *r) static void handle_rmdir(union INTPACK __far *r) { - const char __far *path = data.dossda->fn1; + char __far *path = data.dossda->fn1; int drive = drive_letter_to_index(path[0]); SHFLROOT root = data.drives[drive].root; vboxerr err; dprintf("handle_rmdir %Fs\n", path); - copy_drive_relative_filename(&shflstr.shflstr, path); + copy_drive_relative_filename(root, &shflstr.shflstr, path); err = vbox_shfl_remove(&data.vb, data.hgcm_client_id, root, &shflstr.shflstr, SHFL_REMOVE_DIR); diff --git a/sftsr.h b/sftsr.h index c832169..5fc7b0f 100644 --- a/sftsr.h +++ b/sftsr.h @@ -56,7 +56,7 @@ typedef struct { // but we still waste a full uint64_t to store a value that is always < 4K. // Similarly, at most 64 roots are supported, but we waste a uint32_t. -typedef struct { +typedef _Packed struct { // TSR installation data /** Previous int2f ISR, storing it for uninstall. */ void (__interrupt __far *prev_int2f_handler)(); @@ -74,12 +74,15 @@ typedef struct { 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]; /** All currently open files. */ diff --git a/unicode.h b/unicode.h index 81d187a..730d549 100644 --- a/unicode.h +++ b/unicode.h @@ -40,9 +40,9 @@ static inline uint8_t lookup_codepage( TSRDATAPTR data, uint16_t cp ) } // dst and src CAN'T BE THE SAME !!!! -// Returns resulting length or -1 if buffer overflow +// Returns resulting length or 0 if buffer overflow // -static uint16_t local_to_utf8_n( TSRDATAPTR data, uint8_t *dst, const char far *src, uint16_t buflen, uint16_t count ) +static uint16_t local_to_utf8_n( TSRDATAPTR data, uint8_t *dst, char far *src, uint16_t buflen, uint16_t count ) { uint16_t len = 0; // Resulting length uint16_t cp; // Unicode Code Point @@ -63,7 +63,7 @@ static uint16_t local_to_utf8_n( TSRDATAPTR data, uint8_t *dst, const char far * } else { - return -1; + return 0; } } @@ -83,7 +83,7 @@ static uint16_t local_to_utf8_n( TSRDATAPTR data, uint8_t *dst, const char far * } else { - return -1; + return 0; } } @@ -102,7 +102,7 @@ static uint16_t local_to_utf8_n( TSRDATAPTR data, uint8_t *dst, const char far * } else { - return -1; + return 0; } } cont: @@ -117,7 +117,7 @@ cont: } -static inline uint16_t local_to_utf8( TSRDATAPTR data, uint8_t *dst, const char far *src, uint16_t buflen ) +static inline uint16_t local_to_utf8( TSRDATAPTR data, uint8_t *dst, char far *src, uint16_t buflen ) { return local_to_utf8_n( data, dst, src, buflen, buflen ); } -- cgit v1.2.3