From 3140e3fd8fc1f072e4f45421a6ff69ff2610c5f4 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Tue, 19 May 2026 22:11:28 +0200 Subject: [PATCH] Add first implementation of dosmangle 8.3 support for long file names --- include/dosmangle.h | 34 ++++ src/CMakeLists.txt | 2 +- src/connect.c | 29 ++-- src/dosmangle.c | 391 ++++++++++++++++++++++++++++++++++++++++++++ src/namspace.c | 81 ++++----- 5 files changed, 482 insertions(+), 55 deletions(-) create mode 100644 include/dosmangle.h create mode 100644 src/dosmangle.c diff --git a/include/dosmangle.h b/include/dosmangle.h new file mode 100644 index 0000000..0502b9c --- /dev/null +++ b/include/dosmangle.h @@ -0,0 +1,34 @@ +#ifndef _DOSMANGLE_H_ +#define _DOSMANGLE_H_ + +/* + * dosmangle.h + * + * Centralized DOS 8.3 short-name support for MARS_NWE. + */ + +#include "net.h" +#include "nwvolume.h" + +#define DOS83_NAME_MAX 13 /* 8 + '.' + 3 + NUL */ + +int dos83_is_valid_name(const uint8 *name, int options); +int dos83_build_name_in_dir(const char *dir_unix, + const uint8 *src, + uint8 *out, + int out_size, + int options); +int dos83_match_name_in_dir(const char *dir_unix, + const uint8 *unix_name, + const uint8 *dos_pattern, + int options, + uint8 *out_dos_name, + int out_dos_name_size); +int dos83_resolve_component(const char *dir_unix, + const uint8 *dos_name, + uint8 *resolved_name, + int resolved_size, + int options); +void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp); + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1ce040..c785ad9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,7 +57,7 @@ ELSE(ENABLE_INTERNAL_RIP_SAP) ENDIF(ENABLE_INTERNAL_RIP_SAP) add_executable(nwserv nwserv.c net1.c tools.c ${EMUTLI} ${EMUTLI1} ${NWROUTE_0} ) -add_executable(nwconn nwconn.c net1.c tools.c connect.c namspace.c nwvolume.c nwfile.c unxfile.c nwqconn.c nameos2.c nwfname.c nwshare.c extpipe.c nwattrib.c trustee.c ${EMUTLI} ) +add_executable(nwconn nwconn.c net1.c tools.c connect.c namspace.c dosmangle.c nwvolume.c nwfile.c unxfile.c nwqconn.c nameos2.c nwfname.c nwshare.c extpipe.c nwattrib.c trustee.c ${EMUTLI} ) add_executable(ncpserv ncpserv.c net1.c tools.c ${EMUTLI} ) add_executable(nwclient nwclient.c net1.c tools.c ${EMUTLI} ) add_executable(nwbind nwbind.c net1.c tools.c nwdbm.c nwcrypt.c unxlog.c sema.c nwqueue.c unxfile.c ${EMUTLI} ) diff --git a/src/connect.c b/src/connect.c index fd5fb81..1589701 100644 --- a/src/connect.c +++ b/src/connect.c @@ -24,6 +24,7 @@ #include "net.h" #include "unxfile.h" +#include "dosmangle.h" #include #include @@ -59,6 +60,7 @@ static int act_umode_file=0; #include "nwconn.h" #include "namspace.h" #include "connect.h" +#include "dosmangle.h" typedef struct { @@ -780,12 +782,16 @@ static int get_dh_entry(DIR_HANDLE *dh, dh->sequence++; if (dirbuff->d_ino) { uint8 *name=(uint8*)(dirbuff->d_name); - uint8 dname[256]; - xstrcpy(dname, name); - unix2doscharset(dname); - okflag = (name[0] != '.' && - ( (!strcmp((char*)dname, (char*)entry)) - || fn_dos_match(dname, entry, dh->vol_options))); + uint8 dosname[DOS83_NAME_MAX + 1]; + + if (name[0] != '.') { + okflag = dos83_match_name_in_dir(dh->unixname, + name, + entry, + dh->vol_options, + dosname, + sizeof(dosname)); + } if (okflag) { strmaxcpy(dh->kpath, (char*)name, @@ -805,7 +811,8 @@ static int get_dh_entry(DIR_HANDLE *dh, if (okflag){ if (unixname) strmaxcpy(unixname, dh->unixname, size_unixname-1); - strmaxcpy((char*)search, (char*)dname, size_search-1); + /* Return the DOS-visible alias instead of the raw name. */ + strmaxcpy((char*)search, (char*)dosname, size_search-1); break; /* ready */ } } else okflag = 0; @@ -2679,13 +2686,7 @@ static int get_match(uint8 *unixname, uint8 *p) return(0); } -void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp) -{ - struct stat stb; - if (!s_stat(unixname, &stb, NULL)) /* path is ok I hope */ - return; - get_match(unixname, pp-1); -} +/* mangle_dos_name() moved to dosmangle.c */ int nw_add_trustee(int dir_handle, uint8 *data, int len, diff --git a/src/dosmangle.c b/src/dosmangle.c new file mode 100644 index 0000000..eb07bef --- /dev/null +++ b/src/dosmangle.c @@ -0,0 +1,391 @@ +/* + * dosmangle.c + * + * Centralized DOS 8.3 short-name support. + */ + +#include "net.h" + +#include +#include +#include +#include +#include + +#include "nwfname.h" +#include "nwvolume.h" +#include "connect.h" +#include "dosmangle.h" + +static int dos83_valid_char(int c) +{ + if (c >= 'A' && c <= 'Z') return 1; + if (c >= 'a' && c <= 'z') return 1; + if (c >= '0' && c <= '9') return 1; + if (c == '_' || c == '-') return 1; + return 0; +} + +static char dos83_upchar(int c) +{ + if (c >= 'a' && c <= 'z') + return (char)(c - 'a' + 'A'); + return (char)c; +} + +static void dos83_split_name(const uint8 *src, + char *base, int base_sz, + char *ext, int ext_sz) +{ + const char *dot = strrchr((const char *)src, '.'); + int len; + + if (base_sz > 0) base[0] = '\0'; + if (ext_sz > 0) ext[0] = '\0'; + + if (dot && dot != (const char *)src) { + len = (int)(dot - (const char *)src); + if (len >= base_sz) len = base_sz - 1; + memcpy(base, src, len); + base[len] = '\0'; + + strncpy(ext, dot + 1, ext_sz - 1); + ext[ext_sz - 1] = '\0'; + } else { + strncpy(base, (const char *)src, base_sz - 1); + base[base_sz - 1] = '\0'; + } +} + +static void dos83_normalize_part(const char *src, char *dst, int dst_sz, int maxlen) +{ + int di = 0; + + while (*src && di < dst_sz - 1 && di < maxlen) { + unsigned char c = (unsigned char)*src++; + if (c == ' ') + continue; + if (dos83_valid_char(c)) + dst[di++] = dos83_upchar(c); + } + dst[di] = '\0'; +} + +static void dos83_normalize_part_full(const char *src, char *dst, int dst_sz) +{ + int di = 0; + + while (*src && di < dst_sz - 1) { + unsigned char c = (unsigned char)*src++; + if (c == ' ') + continue; + if (dos83_valid_char(c)) + dst[di++] = dos83_upchar(c); + } + dst[di] = '\0'; +} + +int dos83_is_valid_name(const uint8 *name, int options) +{ + const uint8 *ss = name; + int len = 0; + int pf = 0; + + for (; *ss; ss++) { + if (*ss == '.') { + if (pf++) return 0; + len = 0; + } else { + ++len; + if ((pf && len > 3) || len > 8) + return 0; + + if (!(options & VOL_OPTION_IGNCASE)) { + if (options & VOL_OPTION_DOWNSHIFT) { + if (*ss >= 'A' && *ss <= 'Z') + return 0; + } else { + if (*ss >= 'a' && *ss <= 'z') + return 0; + } + } + + if (!dos83_valid_char(*ss)) + return 0; + } + } + return (*name != '\0'); +} + +static void dos83_build_alias_candidate(const uint8 *src, int seq, + uint8 *out, int out_size) +{ + char base[260], ext[260]; + char nbase_full[260], next[4]; + char stem[9]; + char seqbuf[16]; + int stem_len; + + dos83_split_name(src, base, sizeof(base), ext, sizeof(ext)); + dos83_normalize_part_full(base, nbase_full, sizeof(nbase_full)); + dos83_normalize_part(ext, next, sizeof(next), 3); + + if (!*nbase_full) + strcpy(nbase_full, "FILE"); + + sprintf(seqbuf, "~%d", seq); + + stem_len = 8 - (int)strlen(seqbuf); + if (stem_len > 6) stem_len = 6; + if (stem_len < 1) stem_len = 1; + if (stem_len > (int)strlen(nbase_full)) + stem_len = (int)strlen(nbase_full); + + memcpy(stem, nbase_full, stem_len); + stem[stem_len] = '\0'; + + if (*next) + snprintf((char *)out, out_size, "%s%s.%s", stem, seqbuf, next); + else + snprintf((char *)out, out_size, "%s%s", stem, seqbuf); +} + +static int dos83_alias_used_in_dir(const char *dir_unix, + const uint8 *src, + const uint8 *candidate, + int options) +{ + DIR *d; + struct dirent *dirbuff; + + d = opendir(dir_unix); + if (!d) { + if (seteuid(0)) {} + d = opendir(dir_unix); + reseteuid(); + } + if (!d) + return 0; + + while ((dirbuff = readdir(d)) != NULL) { + uint8 other[DOS83_NAME_MAX + 1]; + + if (!dirbuff->d_ino) + continue; + if (!strcmp(dirbuff->d_name, ".") || !strcmp(dirbuff->d_name, "..")) + continue; + if (!strcmp(dirbuff->d_name, (const char *)src)) + continue; + + if (dos83_is_valid_name((uint8 *)dirbuff->d_name, options)) { + strncpy((char *)other, dirbuff->d_name, sizeof(other) - 1); + other[sizeof(other) - 1] = '\0'; + up_fn(other); + } else { + dos83_build_alias_candidate((uint8 *)dirbuff->d_name, 1, other, sizeof(other)); + } + + if (!strcmp((char *)other, (const char *)candidate)) { + closedir(d); + return 1; + } + } + + closedir(d); + return 0; +} + +int dos83_build_name_in_dir(const char *dir_unix, + const uint8 *src, + uint8 *out, + int out_size, + int options) +{ + char base[260], ext[260]; + char nbase[9], next[4]; + int seq; + + if (!src || !*src || !out || out_size <= 0) + return 0; + + if (dos83_is_valid_name(src, options)) { + strncpy((char *)out, (const char *)src, out_size - 1); + out[out_size - 1] = '\0'; + up_fn(out); + return (int)strlen((char *)out); + } + + dos83_split_name(src, base, sizeof(base), ext, sizeof(ext)); + dos83_normalize_part(base, nbase, sizeof(nbase), 8); + dos83_normalize_part(ext, next, sizeof(next), 3); + + if (*nbase) { + if (*next) + snprintf((char *)out, out_size, "%s.%s", nbase, next); + else + snprintf((char *)out, out_size, "%s", nbase); + + if (!dir_unix || !dos83_alias_used_in_dir(dir_unix, src, out, options)) + return (int)strlen((char *)out); + } + + for (seq = 1; seq < 1000000; seq++) { + dos83_build_alias_candidate(src, seq, out, out_size); + if (!dir_unix || !dos83_alias_used_in_dir(dir_unix, src, out, options)) + return (int)strlen((char *)out); + } + + strncpy((char *)out, "FILE~1", out_size - 1); + out[out_size - 1] = '\0'; + return (int)strlen((char *)out); +} + +static int dos83_strip_magic(const uint8 *in, uint8 *out, int out_size) +{ + const uint8 *p = in; + uint8 *q = out; + + while (*p && (q - out) < out_size - 1) { + if (*p != 0xff) + *q++ = *p; + p++; + } + *q = '\0'; + return (int)(q - out); +} + +static int dos83_x_str_match(const uint8 *s, const uint8 *p, int soptions) +{ + uint8 pbuf[256]; + + dos83_strip_magic(p, pbuf, sizeof(pbuf)); + return fn_dos_match((uint8 *)s, pbuf, soptions); +} + +int dos83_match_name_in_dir(const char *dir_unix, + const uint8 *unix_name, + const uint8 *dos_pattern, + int options, + uint8 *out_dos_name, + int out_dos_name_size) +{ + uint8 dos_name[DOS83_NAME_MAX + 1]; + + if (!unix_name || !dos_pattern) + return 0; + + dos83_build_name_in_dir(dir_unix, unix_name, dos_name, sizeof(dos_name), options); + + if (out_dos_name && out_dos_name_size > 0) { + strncpy((char *)out_dos_name, (const char *)dos_name, out_dos_name_size - 1); + out_dos_name[out_dos_name_size - 1] = '\0'; + } + + return dos83_x_str_match(dos_name, dos_pattern, options); +} + +int dos83_resolve_component(const char *dir_unix, + const uint8 *dos_name, + uint8 *resolved_name, + int resolved_size, + int options) +{ + DIR *d; + struct dirent *dirbuff; + uint8 clean_name[256]; + + if (!dir_unix || !dos_name || !*dos_name || !resolved_name || resolved_size <= 0) + return 0; + + dos83_strip_magic(dos_name, clean_name, sizeof(clean_name)); + up_fn(clean_name); + + d = opendir(dir_unix); + if (!d) { + if (seteuid(0)) {} + d = opendir(dir_unix); + reseteuid(); + } + if (!d) + return 0; + + while ((dirbuff = readdir(d)) != NULL) { + uint8 dos_alias[DOS83_NAME_MAX + 1]; + + if (!dirbuff->d_ino) + continue; + + dos83_build_name_in_dir(dir_unix, + (uint8 *)dirbuff->d_name, + dos_alias, + sizeof(dos_alias), + options); + + if (!strcmp((char *)dos_alias, (const char *)clean_name)) { + strncpy((char *)resolved_name, dirbuff->d_name, resolved_size - 1); + resolved_name[resolved_size - 1] = '\0'; + closedir(d); + return 1; + } + } + + closedir(d); + return 0; +} + +static int dos83_get_match(uint8 *unixname, uint8 *p, int options) +{ + DIR *d; + + if (!p || !*p) + return 1; + + *p = '\0'; + + d = opendir((char *)unixname); + if (!d) { + if (seteuid(0)) {} + d = opendir((char *)unixname); + reseteuid(); + } + + if (d) { + struct dirent *dirbuff; + *p = '/'; + + while ((dirbuff = readdir(d)) != NULL) { + int len; + uint8 resolved[256]; + + if (!dirbuff->d_ino) + continue; + + if (dos83_resolve_component((char *)unixname, + p + 1, + resolved, + sizeof(resolved), + options)) { + strncpy((char *)(p + 1), (const char *)resolved, 255); + closedir(d); + + len = (int)strlen((char *)(p + 1)); + return dos83_get_match(unixname, p + 1 + len, options); + } + } + closedir(d); + } else { + *p = '/'; + } + + return 0; +} + +void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp) +{ + struct stat stbuff; + + if (!stat((char *)unixname, &stbuff)) + return; + + dos83_get_match(unixname, pp - 1, vol->options); +} diff --git a/src/namspace.c b/src/namspace.c index 8f68b77..8150c18 100644 --- a/src/namspace.c +++ b/src/namspace.c @@ -45,6 +45,7 @@ #include "unxfile.h" #include "namspace.h" #include "nameos2.h" +#include "dosmangle.h" #if WITH_NAME_SPACE_CALLS @@ -906,48 +907,48 @@ static int build_base(int namespace, return(result); } +/* + * Build the DOS-visible short name for one namespace directory entry. + * + * This wrapper delegates all DOS short-name generation to dosmangle.c so + * that DOS namespace listings use the same naming rules as DOS path lookup + * and DOS wildcard matching. + */ static int build_dos_name(DIR_BASE_ENTRY *e, uint8 *fname, int size_fname) { - uint8 *ss=e->nwpath.fn; - int len=0; - int pf=0; - int is_ok=1; - int options=get_volume_options(e->nwpath.volume); - for (; *ss; ss++){ - if (*ss == '.') { - if (pf++) { /* no 2. point */ - is_ok=0; - break; - } - len=0; - } else { - ++len; - if ((pf && len > 3) || len > 8) { - is_ok=0; - break; - } - if (!(options & VOL_OPTION_IGNCASE)){ - if (options & VOL_OPTION_DOWNSHIFT){ /* only downshift chars */ - if (*ss >= 'A' && *ss <= 'Z') { - is_ok=0; - break; - } - } else { /* only upshift chars */ - if (*ss >= 'a' && *ss <= 'z') { - is_ok=0; - break; - } - } - } - } - } - if (is_ok) { - strmaxcpy(fname, e->nwpath.fn, size_fname-1); - up_fn(fname); - return(strlen(fname)); - } else { - return(sprintf(fname, "%ld.___", (long)e->nwpath.statb.st_ino)); - } + char dir_unix[1024]; + char *slash; + uint8 *unixname; + int options; + + if (!e || !fname || size_fname <= 0) + return 0; + + options = get_volume_options(e->nwpath.volume); + + /* + * Convert the full entry path to Unix form, then strip the last component. + * dosmangle.c uses the containing directory to check for alias collisions. + */ + unixname = alloc_nwpath2unix(&(e->nwpath), 2); + if (!unixname) + return 0; + + strncpy(dir_unix, (char *)unixname, sizeof(dir_unix) - 1); + dir_unix[sizeof(dir_unix) - 1] = ''; + xfree(unixname); + + slash = strrchr(dir_unix, '/'); + if (slash) + *slash = ''; + else + strcpy(dir_unix, "."); + + return dos83_build_name_in_dir(dir_unix, + e->nwpath.fn, + fname, + size_fname, + options); } #if 0