aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEduardo Casino <mail@eduardocasino.es>2022-05-24 07:58:48 +0200
committerJavier <dev.git@javispedro.com>2022-06-12 17:13:03 +0200
commit80f0debf2032e16629cb13682b8fb9ceccde423c (patch)
tree401b662542df92e1d4e8527a73e21531850ce109
parenta56c45b42d5d1bf3b161d051fbe879454856b2ff (diff)
downloadvbados-80f0debf2032e16629cb13682b8fb9ceccde423c.tar.gz
vbados-80f0debf2032e16629cb13682b8fb9ceccde423c.zip
Add support for hash-generated short file names
-rw-r--r--lfn.h442
-rw-r--r--nls.h11
-rw-r--r--sfmain.c34
-rw-r--r--sftsr.c132
-rw-r--r--sftsr.h5
-rw-r--r--unicode.h12
6 files changed, 573 insertions, 63 deletions
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 <stdint.h>
+#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 <time.h>
#include <dos.h>
#include <sys/stat.h>
+#include <ctype.h>
#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 <n> 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 <FOLD> <X:> mount a shared folder into drive X:\n"
" umount <X:> 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 );
}