feat: add DOS long-name alias support

This commit is contained in:
Mario Fetka
2026-05-19 22:11:28 +02:00
parent 349c876788
commit e817405919
7 changed files with 776 additions and 89 deletions

View File

@@ -257,7 +257,7 @@ extern time_t nw_2_un_time(uint8 *d, uint8 *t);
extern void un_time_2_nw(time_t time, uint8 *d, int high_low);
extern void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp);
extern void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp, int len);
extern int nw_add_trustee(int dir_handle, uint8 *data, int len,
uint32 id, int trustee, int extended);

15
include/namedos.h Normal file
View File

@@ -0,0 +1,15 @@
/*
* namedos.h: DOS namespace helpers for mars_nwe
*/
#ifndef _NAMEDOS_H_
#define _NAMEDOS_H_
#if WITH_NAME_SPACE_CALLS
extern int dos_is_83_name(uint8 *name, int options);
extern int build_dos_83_alias(int options, uint8 *parent_unix,
uint8 *real_name, ino_t inode,
uint8 *alias, int alias_len);
extern void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp, int len);
#endif
#endif

View File

@@ -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 nwvolume.c nwfile.c unxfile.c nwqconn.c nameos2.c namedos.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} )

View File

@@ -58,8 +58,17 @@ static int act_umode_file=0;
#include "nwfile.h"
#include "nwconn.h"
#include "namspace.h"
#include "namedos.h"
#include "connect.h"
/* connect.h may already be include-guarded through another header before
* NW_VOL is visible, so keep this local forward declaration before the
* first call in build_dir_name().
*/
void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp, int len);
static void build_dos_attr_name(int volume, uint8 *path, char *unixname,
struct stat *stb, uint8 *out, int out_len);
typedef struct {
dev_t dev; /* unix dev */
@@ -107,6 +116,35 @@ static DIR_HANDLE dir_handles[MAX_DIRHANDLES];
static int anz_dirhandles=0;
/* Last real directory entry found by the old DOS scan code.
* Some callers rebuild unixname later from the synthetic DOS alias; keep the
* real parent/name pair so the final attribute formatter can still generate
* a collision-aware 8.3 alias for display.
*/
static int last_dos_alias_volume = -1;
static ino_t last_dos_alias_inode = 0;
static uint8 last_dos_alias_parent[300];
static uint8 last_dos_alias_real[256];
static uint8 last_dos_alias_alias[14];
static void remember_dos_alias_source(int volume, uint8 *parent, uint8 *real,
ino_t inode)
{
last_dos_alias_volume = volume;
last_dos_alias_inode = inode;
strmaxcpy(last_dos_alias_parent, parent ? (char*)parent : "",
sizeof(last_dos_alias_parent)-1);
strmaxcpy(last_dos_alias_real, real ? (char*)real : "",
sizeof(last_dos_alias_real)-1);
last_dos_alias_alias[0] = '\0';
if (parent && real) {
build_dos_83_alias(get_volume_options(volume), parent, real, inode,
last_dos_alias_alias, sizeof(last_dos_alias_alias));
}
}
static char *build_unix_name(NW_PATH *nwpath, int modus)
/*
* returns complete UNIX path
@@ -597,19 +635,25 @@ static int func_search_entry(NW_PATH *nwpath, int attrib,
if (dirbuff->d_ino) {
uint8 *name=(uint8*)(dirbuff->d_name);
uint8 dname[256];
uint8 dosalias[14];
xstrcpy(dname, name);
unix2doscharset(dname);
*kpath = '\0';
build_dos_83_alias(soptions, (uint8*)xkpath, name,
dirbuff->d_ino, dosalias, sizeof(dosalias));
okflag = (name[0] != '.' &&
( (!strcmp((char*)dname, (char*)entry))
|| fn_dos_match(dname, entry, soptions)));
( (!strcmp((char*)dosalias, (char*)entry))
|| fn_dos_match(dosalias, entry, soptions)));
if (okflag) {
*kpath = '\0';
strmaxcpy(kpath, (char*)name, sizeof(xkpath) - (int)(kpath-xkpath) -1 );
if (!s_stat(xkpath, &(fs->statb), NULL)) {
okflag = ( ( ( (fs->statb.st_mode & S_IFMT) == S_IFDIR) && (attrib & 0x10))
|| ( ( (fs->statb.st_mode & S_IFMT) != S_IFDIR) && !(attrib & 0x10)));
if (okflag){
xstrcpy(nwpath->fn, (char*)dname);
*kpath = '\0';
remember_dos_alias_source(volume, (uint8*)xkpath, name,
fs->statb.st_ino);
xstrcpy(nwpath->fn, (char*)dosalias);
XDPRINTF((5,0,"FOUND=:%s: attrib=0x%x", nwpath->fn, fs->statb.st_mode));
result = (*fs_func)(nwpath, fs);
if (result < 0) break;
@@ -678,13 +722,16 @@ static int get_dir_entry(NW_PATH *nwpath,
if (dirbuff->d_ino) {
uint8 *name=(uint8*)(dirbuff->d_name);
uint8 dname[256];
uint8 dosalias[14];
xstrcpy(dname, name);
unix2doscharset(dname);
*kpath = '\0';
build_dos_83_alias(soptions, (uint8*)xkpath, name,
dirbuff->d_ino, dosalias, sizeof(dosalias));
okflag = ((name[0] != '.' &&
( (!strcmp((char*)dname, (char*)entry))
|| fn_dos_match(dname, entry, soptions)))) ? 0 : -0xff;
( (!strcmp((char*)dosalias, (char*)entry))
|| fn_dos_match(dosalias, entry, soptions)))) ? 0 : -0xff;
if (!okflag) {
*kpath = '\0';
strmaxcpy(kpath, (char*)name, sizeof(xkpath) - (int)(kpath-xkpath) -1);
if (!s_stat(xkpath, statb, NULL)) {
okflag = (( ( ( (statb->st_mode & S_IFMT) == S_IFDIR) && (attrib & 0x10))
@@ -697,7 +744,11 @@ static int get_dir_entry(NW_PATH *nwpath,
if (soptions & VOL_OPTION_IS_PIPE) {
statb->st_size = 0x70000000|(statb->st_mtime&0xfffffff);
}
xstrcpy(nwpath->fn, (char*)dname);
*kpath = '\0';
remember_dos_alias_source(volume, (uint8*)xkpath, name,
statb->st_ino);
XDPRINTF((99,0,"DOSGETDIR parent='%s' real='%s' alias='%s' inode=%ld", xkpath, name, dosalias, (long)statb->st_ino));
xstrcpy(nwpath->fn, (char*)dosalias);
XDPRINTF((5,0,"FOUND=:%s: attrib=0x%x", nwpath->fn, statb->st_mode));
break; /* ready */
} else
@@ -781,11 +832,14 @@ static int get_dh_entry(DIR_HANDLE *dh,
if (dirbuff->d_ino) {
uint8 *name=(uint8*)(dirbuff->d_name);
uint8 dname[256];
uint8 dosalias[14];
xstrcpy(dname, name);
unix2doscharset(dname);
build_dos_83_alias(dh->vol_options, (uint8*)dh->unixname, name,
dirbuff->d_ino, dosalias, sizeof(dosalias));
okflag = (name[0] != '.' &&
( (!strcmp((char*)dname, (char*)entry))
|| fn_dos_match(dname, entry, dh->vol_options)));
( (!strcmp((char*)dosalias, (char*)entry))
|| fn_dos_match(dosalias, entry, dh->vol_options)));
if (okflag) {
strmaxcpy(dh->kpath, (char*)name,
@@ -805,7 +859,11 @@ 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);
*(dh->kpath) = '\0';
remember_dos_alias_source(dh->volume, (uint8*)dh->unixname,
name, statb->st_ino);
XDPRINTF((99,0,"DOSDH parent='%s' real='%s' alias='%s' inode=%ld", dh->unixname, name, dosalias, (long)statb->st_ino));
strmaxcpy((char*)search, (char*)dosalias, size_search-1);
break; /* ready */
}
} else okflag = 0;
@@ -1052,7 +1110,7 @@ static int build_dir_name(NW_PATH *nwpath, /* gets complete path */
dos2unixcharset(pp);
pp += offset;
pathlen -= offset;
mangle_dos_name(v, unixname, pp);
mangle_dos_name(v, unixname, pp, sizeof(unixname) - (int)(pp - unixname));
unix2doscharset(pp);
XDPRINTF((5, 0, "Mangled DOS/unixname=%s", unixname));
memcpy(ppp, pp, pathlen);
@@ -1183,10 +1241,12 @@ time_t nw_2_un_time(uint8 *d, uint8 *t)
static int get_file_attrib(NW_FILE_INFO *f, char *unixname, struct stat *stb,
NW_PATH *nwpath)
{
uint8 spath[14];
uint32 dwattrib;
strncpy((char*)f->name, (char*)nwpath->fn, sizeof(f->name));
f->attrib[0]=0; /* d->name could be too long */
up_fn(f->name);
build_dos_attr_name(nwpath->volume, nwpath->fn, unixname, stb,
spath, sizeof(spath));
memset(f->name, 0, sizeof(f->name));
strncpy((char*)f->name, (char*)spath, sizeof(f->name)-1);
dwattrib = get_nw_attrib_dword(nwpath->volume, unixname, stb);
U16_TO_16(dwattrib, f->attrib);
@@ -1203,11 +1263,13 @@ static int get_file_attrib(NW_FILE_INFO *f, char *unixname, struct stat *stb,
static int get_dir_attrib(NW_DIR_INFO *d, char *unixname, struct stat *stb,
NW_PATH *nwpath)
{
uint8 spath[14];
uint32 dwattrib;
XDPRINTF((5,0, "get_dir_attrib of %s", conn_get_nwpath_name(nwpath)));
strncpy((char*)d->name, (char*)nwpath->fn, sizeof(d->name));
d->attrib[0]=0; /* d->name could be too long */
up_fn(d->name);
build_dos_attr_name(nwpath->volume, nwpath->fn, unixname, stb,
spath, sizeof(spath));
memset(d->name, 0, sizeof(d->name));
strncpy((char*)d->name, (char*)spath, sizeof(d->name)-1);
dwattrib = get_nw_attrib_dword(nwpath->volume, unixname, stb);
U16_TO_16(dwattrib, d->attrib);
@@ -2385,9 +2447,44 @@ static int s_nw_scan_dir_info(int dir_handle,
XDPRINTF((5,0,"SCAN_DIR: von %s, found %s:", dh->unixname, dirname));
if (++aktsequence == dirsequence) { /* actual found */
uint8 dosalias[14];
char parent[300];
char *slash;
uint8 *leaf = dirname;
U16_TO_BE16(aktsequence, subnr);
up_fn(dirname);
strncpy((char*)subname, (char*)dirname, 16);
/*
* Do not trust dirname here. Some old DOS scan paths carry the
* raw 8 byte directory name only, which collapses colliding long
* directories to the same display name (LONG_DIR/LONG_DIR).
* Rebuild the final directory info name from the real full unixname
* returned by get_dh_entry(), where the long on-disk leaf is still
* available.
*/
if (unixname[0]) {
strmaxcpy(parent, unixname, sizeof(parent)-1);
slash = strrchr(parent, '/');
if (slash) {
leaf = (uint8*)(slash+1);
*slash = '\0';
if (!*parent) strcpy(parent, "/");
build_dos_83_alias(dh->vol_options, (uint8*)parent,
leaf, stbuff.st_ino,
dosalias, sizeof(dosalias));
} else {
build_dos_83_alias(dh->vol_options, NULL,
leaf, stbuff.st_ino,
dosalias, sizeof(dosalias));
}
} else {
build_dos_83_alias(dh->vol_options, (uint8*)dh->unixname,
dirname, stbuff.st_ino,
dosalias, sizeof(dosalias));
}
up_fn(dosalias);
strncpy((char*)subname, (char*)dosalias, 16);
U32_TO_BE32(get_file_owner(&stbuff), owner);
un_date_2_nw(stbuff.st_mtime, subdatetime, 1);
un_time_2_nw(stbuff.st_mtime, subdatetime+2, 1);
@@ -2440,6 +2537,60 @@ int nw_scan_dir_info(int dir_handle, uint8 *data, int len, uint8 *subnr,
static void build_dos_attr_name(int volume, uint8 *path, char *unixname,
struct stat *stb, uint8 *out, int out_len)
{
char parent[300];
char *slash;
uint8 *leaf = path;
int options = get_volume_options(volume);
if (!out || out_len < 2) return;
*out = '\0';
if (stb && volume == last_dos_alias_volume
&& stb->st_ino == last_dos_alias_inode
&& last_dos_alias_parent[0] && last_dos_alias_real[0]) {
if (last_dos_alias_alias[0]) {
strmaxcpy(out, last_dos_alias_alias, out_len-1);
up_fn(out);
} else {
build_dos_83_alias(options, last_dos_alias_parent,
last_dos_alias_real, stb->st_ino, out, out_len);
}
return;
}
/*
* Prefer the real unixname when it is available. Some namespace/DOS scan
* paths pass an already synthesized 8.3 name in `path`; if we return that
* blindly, colliding long directories can both be displayed as LONG_DIR.
*/
if (unixname && *unixname) {
strmaxcpy(parent, unixname, sizeof(parent)-1);
slash = strrchr(parent, '/');
if (slash) {
*slash = '\0';
if (*(slash+1)) leaf = (uint8*)(slash+1);
if (!*parent) strcpy(parent, "/");
build_dos_83_alias(options, (uint8*)parent,
leaf, stb ? stb->st_ino : 0, out, out_len);
return;
}
}
/* Fallback: keep a previously synthesized DOS alias only when there is no
* reliable real unixname to rebuild from. */
if (path && *path && dos_is_83_name(path, options)) {
strmaxcpy(out, path, out_len-1);
up_fn(out);
return;
}
build_dos_83_alias(options, NULL,
leaf, stb ? stb->st_ino : 0, out, out_len);
}
void get_dos_file_attrib(NW_DOS_FILE_INFO *f,
struct stat *stb,
int volume,
@@ -2448,9 +2599,9 @@ void get_dos_file_attrib(NW_DOS_FILE_INFO *f,
{
uint8 spath[14];
uint32 nw_owner=get_file_owner(stb);
f->namlen=min(strlen((char*)path), 12);
strmaxcpy(spath, path, 12);
up_fn(spath);
build_dos_attr_name(volume, path, unixname, stb, spath, sizeof(spath));
XDPRINTF((99,0,"DOSFILEATTR path='%s' unix='%s' alias='%s' inode=%ld", path ? (char*)path : "", unixname ? unixname : "", spath, stb ? (long)stb->st_ino : 0L));
f->namlen=min(strlen((char*)spath), 12);
strncpy((char*)f->name, (char*)spath, f->namlen);
U32_TO_32(get_nw_attrib_dword(volume, unixname, stb), f->attributes);
U16_TO_16(tru_get_inherited_mask(volume, unixname, stb),
@@ -2475,9 +2626,9 @@ void get_dos_dir_attrib(NW_DOS_DIR_INFO *f,
char *unixname)
{
uint8 spath[14];
f->namlen=min(strlen((char*)path), 12);
strmaxcpy(spath, path, 12);
up_fn(spath);
build_dos_attr_name(volume, path, unixname, stb, spath, sizeof(spath));
XDPRINTF((99,0,"DOSDIRATTR path='%s' unix='%s' alias='%s' inode=%ld", path ? (char*)path : "", unixname ? unixname : "", spath, stb ? (long)stb->st_ino : 0L));
f->namlen=min(strlen((char*)spath), 12);
strncpy((char*)f->name, (char*)spath, f->namlen);
U32_TO_32(get_nw_attrib_dword(volume, unixname, stb),
f->attributes);
@@ -2679,13 +2830,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);
}
/* DOS name mangling is provided by namedos.c. */
int nw_add_trustee(int dir_handle, uint8 *data, int len,

396
src/namedos.c Normal file
View File

@@ -0,0 +1,396 @@
/* namedos.c : NameSpace DOS Services, mars_nwe
*
* Adds DOS 8.3 alias generation/resolution for long filenames on DOS
* namespace clients. This file was originally derived from nameos2.c,
* but DOS needs synthetic aliases while OS/2 can address long names.
*/
#include "net.h"
#include <dirent.h>
#include <utime.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#ifndef LINUX
#include <errno.h>
#endif
#include "nwfname.h"
#include "nwvolume.h"
#include "connect.h"
#include "nwfile.h"
#include "unxfile.h"
#include "namedos.h"
#if WITH_NAME_SPACE_CALLS
#define MAX_NAME_DOS_CACHE 0
static int dos_is_alias_char(int c)
{
if (isalnum((unsigned char)c)) return(1);
switch (c) {
case '$': case '%': case '\'': case '-': case '_': case '@':
case '~': case '`': case '!': case '(': case ')': case '{':
case '}': case '^': case '#': case '&':
return(1);
default:
return(0);
}
}
static int dos_alias_char(int c)
{
if (c >= 'a' && c <= 'z') c -= ('a' - 'A');
if (dos_is_alias_char(c)) return(c);
return('_');
}
int dos_is_83_name(uint8 *name, int options)
{
uint8 *ss=name;
int len=0;
int pf=0;
if (!name || !*name) return(0);
if (!strcmp((char*)name, ".") || !strcmp((char*)name, "..")) return(1);
for (; *ss; ss++){
if (*ss == '.') {
if (pf++) return(0); /* no second dot */
if (!len) return(0); /* no empty base */
len=0;
} else {
if (!dos_is_alias_char(*ss)) return(0);
++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);
}
}
}
}
return(len > 0);
}
static void build_dos_83_raw(uint8 *real_name, uint8 *alias, int alias_len)
{
uint8 base[9];
uint8 ext[4];
uint8 *dot=NULL;
uint8 *s;
int bl=0, el=0;
memset(base, 0, sizeof(base));
memset(ext, 0, sizeof(ext));
if (!real_name || !*real_name) {
strmaxcpy(alias, "_", alias_len-1);
return;
}
if (real_name[0] != '.')
dot=(uint8*)strrchr((char*)real_name, '.');
for (s=real_name; *s && s != dot; s++) {
if (bl < 8) base[bl++] = (uint8)dos_alias_char(*s);
}
if (!bl) base[bl++] = '_';
if (dot) {
for (s=dot+1; *s; s++) {
if (el < 3) ext[el++] = (uint8)dos_alias_char(*s);
}
}
if (el)
snprintf((char*)alias, alias_len, "%s.%s", base, ext);
else
snprintf((char*)alias, alias_len, "%s", base);
}
static int alias_base_equal(uint8 *a, uint8 *b)
{
uint8 aa[14], bb[14];
uint8 *pa, *pb;
strmaxcpy(aa, a, sizeof(aa)-1);
strmaxcpy(bb, b, sizeof(bb)-1);
pa=(uint8*)strchr((char*)aa, '.');
pb=(uint8*)strchr((char*)bb, '.');
if (pa) *pa='\0';
if (pb) *pb='\0';
return(!strcmp((char*)aa, (char*)bb));
}
static char dos_digit(int n)
{
n &= 0x0f;
return((n < 10) ? ('0' + n) : ('A' + n - 10));
}
static void apply_collision_number(uint8 *alias, int collision)
{
uint8 *dot=(uint8*)strchr((char*)alias, '.');
uint8 ext[5];
uint8 base[9];
int bl;
memset(ext, 0, sizeof(ext));
memset(base, 0, sizeof(base));
if (dot) {
strmaxcpy(ext, dot, sizeof(ext)-1);
*dot='\0';
}
strmaxcpy(base, alias, sizeof(base)-1);
bl=strlen((char*)base);
if (bl >= 8) bl=7;
base[bl++] = dos_digit(collision);
base[bl] = '\0';
snprintf((char*)alias, 14, "%s%s", base, ext);
}
int build_dos_83_alias(int options, uint8 *parent_unix,
uint8 *real_name, ino_t inode,
uint8 *alias, int alias_len)
{
uint8 raw[14];
int collisions=0;
DIR *d;
(void)inode;
if (alias_len < 2) return(0);
*alias='\0';
if (dos_is_83_name(real_name, options)) {
strmaxcpy(alias, real_name, alias_len-1);
up_fn(alias);
return(strlen((char*)alias));
}
build_dos_83_raw(real_name, raw, sizeof(raw));
strmaxcpy(alias, raw, alias_len-1);
if (parent_unix && *parent_unix && NULL != (d=opendir((char*)parent_unix))) {
struct dirent *de;
while ((de=readdir(d)) != NULL) {
uint8 other[14];
if (!de->d_ino) continue;
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
if (!strcmp(de->d_name, (char*)real_name)) continue;
build_dos_83_raw((uint8*)de->d_name, other, sizeof(other));
if (!strcmp((char*)other, (char*)raw)) {
if (strcmp(de->d_name, (char*)real_name) < 0)
collisions++;
}
/* A real 8.3 name that equals the raw alias consumes that alias. */
if (dos_is_83_name((uint8*)de->d_name, options)) {
uint8 native[14];
strmaxcpy(native, (uint8*)de->d_name, sizeof(native)-1);
up_fn(native);
if (!strcmp((char*)native, (char*)raw)) collisions++;
}
}
closedir(d);
}
if (collisions > 0) {
strmaxcpy(alias, raw, alias_len-1);
apply_collision_number(alias, collisions-1);
}
up_fn(alias);
return(strlen((char*)alias));
}
static int dos_alias_match(uint8 *a, uint8 *b)
{
uint8 aa[14], bb[14];
strmaxcpy(aa, a, sizeof(aa)-1);
strmaxcpy(bb, b, sizeof(bb)-1);
up_fn(aa);
up_fn(bb);
return(!strcmp((char*)aa, (char*)bb));
}
void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp, int len)
{
uint8 wanted[14];
uint8 rest[512];
uint8 *slash;
uint8 *parent_slash;
DIR *d;
if (!vol || !unixname || !pp || !*pp) return;
if (len <= 1) return;
slash=(uint8*)strchr((char*)pp, '/');
memset(rest, 0, sizeof(rest));
if (slash) {
strmaxcpy(rest, slash, sizeof(rest)-1);
*slash='\0';
}
strmaxcpy(wanted, pp, sizeof(wanted)-1);
up_fn(wanted);
parent_slash = (pp > unixname && *(pp-1) == '/') ? pp-1 : NULL;
if (parent_slash) *parent_slash='\0';
d=opendir((char*)unixname);
if (d) {
struct dirent *de;
while ((de=readdir(d)) != NULL) {
uint8 alias[14];
if (!de->d_ino) continue;
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
build_dos_83_alias(vol->options, unixname, (uint8*)de->d_name,
de->d_ino, alias, sizeof(alias));
if (dos_alias_match(alias, wanted)) {
if (parent_slash) *parent_slash='/';
snprintf((char*)pp, len, "%s%s", de->d_name, rest);
closedir(d);
if (*rest) {
uint8 *next=(uint8*)strchr((char*)pp, '/');
if (next && *(next+1))
mangle_dos_name(vol, unixname, next+1, len - (int)((next+1) - pp));
}
return;
}
}
closedir(d);
}
if (parent_slash) *parent_slash='/';
if (slash) *slash='/';
}
static inline int get_n_p(uint8 **p)
{
int pc=**p;
(*p)++;
if (pc == '\\') {
pc=**p;
(*p)++;
} else if (pc == 255) {
pc=**p;
(*p)++;
switch (pc) {
case 0xaa :
case '*' : return(3000); /* star */
case 0xae :
case '.' : return(1000); /* point */
case 0xbf :
case '?' : return(2000); /* ? */
default : break;
}
}
return(pc);
}
static int ns_fn_dos_match(uint8 *s, uint8 *p, int soptions)
/* OS/2 name matching routine */
{
int pc, sc;
uint state = 0;
int anf, ende;
int not = 0;
uint found = 0;
while ( (pc = get_n_p(&p)) != 0) {
if (!(soptions & VOL_OPTION_IGNCASE)) {
if (soptions & VOL_OPTION_DOWNSHIFT){ /* only downshift chars */
if (*s >= 'A' && *s <= 'Z') return(0);
} else {
if (*s >= 'a' && *s <= 'z') return(0);
}
}
switch (state){
case 0 :
switch (pc) {
case '.' :
case 1000: if (*s && ('.' != *s++))
return(0);
break;
case '?' :
case 2000: if (!*s) return(0);
++s;
break;
case '*' :
case 3000: {
uint8 *pp;
int np;
if (!*p) return(1); /* last star */
while (*s) {
if (ns_fn_dos_match(s, p, soptions) == 1) return(1);
else if (*s=='.') {
pp=p;
if (!get_n_p(&p) || !get_n_p(&p))
return(0);
p=pp;
}
++s;
}
pp=p;
np=get_n_p(&p);
p=pp;
if (np == '.' || np == 1000)
return(ns_fn_dos_match(s, p, soptions));
}
return(0);
case '[' : if ( (*p == '!') || (*p == '^') ){
++p;
not = 1;
}
state = 1;
continue;
default : if (soptions & VOL_OPTION_IGNCASE) {
if (!dfn_imatch(*s, pc))
return(0);
} else if (pc != *s) return(0);
++s;
break;
} /* switch */
break;
case 1 : /* Bereich von Zeichen */
sc = *s++;
found = not;
if (!sc) return(0);
do {
if (pc == '\\') pc = *(p++);
if (!pc) return(0);
anf = pc;
if (*p == '-' && *(p+1) != ']'){
ende = *(++p);
p++;
}
else ende = anf;
if (found == not) { /* only if not found */
if (anf == sc || (anf <= sc && sc <= ende))
found = !not;
}
} while ((pc = *(p++)) != ']');
if (! found ) return(0);
not = 0;
found = 0;
state = 0;
break;
default : break;
} /* switch */
} /* while */
if (*s=='.' && *(s+1)=='\0') return(1);
return ( (*s) ? 0 : 1);
}
#endif

View File

@@ -45,6 +45,7 @@
#include "unxfile.h"
#include "namspace.h"
#include "nameos2.h"
#include "namedos.h"
#if WITH_NAME_SPACE_CALLS
@@ -654,11 +655,12 @@ leave_build_nwpath:
else
up_fn(pp);
mangle_dos_name(v, unixname, pp);
mangle_dos_name(v, unixname, pp, sizeof(unixname)-v->unixnamlen-npbeg);
if (nplen > 0) {
unix2doscharset(pp);
memcpy(nwpath->path+npbeg, pp, nplen);
strmaxcpy(nwpath->path+npbeg, pp,
sizeof(nwpath->path)-npbeg-1);
}
XDPRINTF((5,0, "Mangle DOS unixname='%s'", unixname));
}
@@ -908,46 +910,47 @@ static int build_base(int namespace,
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));
char *parent_unix;
int result;
N_NW_PATH parent_path;
int plen;
if (!e || !fname || size_fname < 2) return(0);
/*
* e->nwpath.path contains the full entry path and e->nwpath.fn points to
* the last component inside that buffer. alloc_nwpath2unix(..., 1|2)
* only gives the parent if fn is behind path. For entries directly in the
* volume root fn == path, so the old code accidentally used the entry
* itself as parent. That made collision checks local to each directory and
* both "long directory name one" and "long directory name two" became
* LONG_DIR.
*/
memcpy(&parent_path, &(e->nwpath), sizeof(parent_path));
if (e->nwpath.fn && e->nwpath.fn > e->nwpath.path) {
plen = (int)(e->nwpath.fn - e->nwpath.path);
if (plen > 0 && e->nwpath.path[plen-1] == '/') --plen;
memcpy(parent_path.path, e->nwpath.path, plen);
parent_path.path[plen] = '\0';
} else {
return(sprintf(fname, "%ld.___", (long)e->nwpath.statb.st_ino));
parent_path.path[0] = '\0';
}
parent_path.fn = parent_path.path + strlen((char*)parent_path.path);
parent_unix = alloc_nwpath2unix(&parent_path, 2);
result = build_dos_83_alias(get_volume_options(e->nwpath.volume),
(uint8*)parent_unix,
e->nwpath.fn,
e->nwpath.statb.st_ino,
fname,
size_fname);
XDPRINTF((99,0,"NSDOSNAME parent='%s' real='%s' alias='%s' inode=%ld",
parent_unix ? parent_unix : "",
e->nwpath.fn ? (char*)e->nwpath.fn : "",
fname ? (char*)fname : "",
(long)e->nwpath.statb.st_ino));
xfree(parent_unix);
return(result);
}
#if 0
@@ -1517,7 +1520,19 @@ static int search_match(struct dirent *dirbuff,
XDPRINTF((8,0,"search_match, Name='%s' dname='%s'", name, dname));
if (!inode_search) {
if (namespace == NAME_DOS) {
flag = (*name != '.' && fn_dos_match_old(dname, entry, vol_options));
if (*name != '.') {
uint8 dosalias[14];
build_dos_83_alias(vol_options, ds->unixname, name,
dirbuff->d_ino, dosalias, sizeof(dosalias));
flag = fn_dos_match_old(dosalias, entry, vol_options);
/*
* Keep dname as the real on-disk name. get_add_new_entry()
* appends dname to the parent and stats it; passing the synthetic
* DOS alias here makes long-name matches fail with
* "nw_search_file_dir:Cannot add entry". The alias is only for
* matching and for the returned INFO_MSK_ENTRY_NAME.
*/
}
} else if (namespace == NAME_OS2) {
flag = (*name != '.' || (*(name+1) != '.' && *(name+1) != '\0' ))
&& fn_os2_match(dname, entry, vol_options);

View File

@@ -64,6 +64,88 @@ char *path_trustees=NULL;
static int max_nw_vols=MAX_NW_VOLS;
/*
* Modern Linux filesystems can use inode numbers larger than 28 bits.
* The classic MARS_NWE handle format uses the upper 4 bits for the
* dev/namespace map index and the lower 28 bits for the inode. That
* breaks for volumes using multiple namespaces, for example DOS + OS2,
* when VOL_OPTION_ONE_DEV ('o') is not enabled.
*
* Keep the external 32-bit NetWare handle format, but map real Unix inode
* numbers to small pseudo inode numbers per volume/map entry. This allows
* OS2/NFS namespaces on large-inode filesystems without requiring the
* small 'o' ONE_DEV option.
*
* VOL_OPTION_ONE_DEV keeps its old direct 32-bit inode behaviour.
*/
typedef struct inode_handle_map_s {
int volume;
int map_entry;
ino_t inode;
uint32 handle_inode;
struct inode_handle_map_s *next;
} INODE_HANDLE_MAP;
static INODE_HANDLE_MAP *inode_handle_maps = NULL;
static uint32 next_handle_inode = 1;
static void free_inode_handle_maps(void)
{
INODE_HANDLE_MAP *m = inode_handle_maps;
while (m) {
INODE_HANDLE_MAP *n = m->next;
xfree(m);
m = n;
}
inode_handle_maps = NULL;
next_handle_inode = 1;
}
static uint32 inode_to_handle_inode(int volume, int map_entry, ino_t inode)
{
INODE_HANDLE_MAP *m;
for (m = inode_handle_maps; m; m = m->next) {
if (m->volume == volume && m->map_entry == map_entry && m->inode == inode)
return(m->handle_inode);
}
if (next_handle_inode > 0x0fffffff) {
XDPRINTF((1,0,
"No free pseudo inode handle for vol=%d map=%d inode=%ld",
volume, map_entry, (long)inode));
return(0);
}
m = (INODE_HANDLE_MAP*)xcmalloc(sizeof(INODE_HANDLE_MAP));
m->volume = volume;
m->map_entry = map_entry;
m->inode = inode;
m->handle_inode = next_handle_inode++;
m->next = inode_handle_maps;
inode_handle_maps = m;
XDPRINTF((99,0,
"Pseudo inode map vol=%d map=%d inode=%ld to pseudo=0x%x",
volume, map_entry, (long)inode, m->handle_inode));
return(m->handle_inode);
}
static ino_t handle_inode_to_inode(int volume, int map_entry, uint32 handle_inode)
{
INODE_HANDLE_MAP *m;
for (m = inode_handle_maps; m; m = m->next) {
if (m->volume == volume && m->map_entry == map_entry
&& m->handle_inode == handle_inode)
return(m->inode);
}
/* Compatibility fallback for old/direct handles. */
return((ino_t)handle_inode);
}
static void free_vol_trustee(NW_VOL *vol)
{
if (vol) {
@@ -143,6 +225,7 @@ void nw_init_volumes(FILE *f)
}
rewind(f);
used_nw_volumes = 0;
free_inode_handle_maps();
loaded_namespaces = 0;
new_str(path_vol_inodes_cache, "/var/spool/nwserv/.volcache");
new_str(path_attributes, "/var/nwserv/attrib");
@@ -393,8 +476,27 @@ static int look_name_space_map(NW_VOL *v, DEV_NAMESPACE_MAP *dnm,
int k=-1;
while (++k < v->maps_count) {
mp=v->dev_namespace_maps[k];
if (mp->dev == dnm->dev && mp->namespace == dnm->namespace)
return(k);
if (mp->dev == dnm->dev) {
/*
* VOL_OPTION_ONE_DEV encodes handles as the raw 32-bit inode:
*
* handle = inode
*
* In that mode there is no room in the handle for the dev/namespace
* map index. All namespaces on this one Unix device therefore have
* to share the same map entry. Otherwise a volume with both 'o' and
* 'O' set can map the DOS root entry, but later OS/2 namespace lookups
* fail with:
*
* Cannot map inode=..., dev=..., namespace=4 to vol=... handle
*
* Non-ONE_DEV volumes keep the old behaviour and still distinguish
* mappings by both device and namespace.
*/
if ((v->options & VOL_OPTION_ONE_DEV)
|| mp->namespace == dnm->namespace)
return(k);
}
}
if (do_insert && v->maps_count < v->max_maps_count) {
/* now do insert the new map */
@@ -411,20 +513,31 @@ uint32 nw_vol_inode_to_handle(int volume, ino_t inode,
{
if (volume > -1 && volume < used_nw_volumes) {
NW_VOL *v= &(nw_volumes[volume]);
if (inode > 0 && inode <= v->high_inode) {
if (inode > 0) {
int result = look_name_space_map(v, dnm, 1);
if (result > -1) {
uint32 handle = (v->options & VOL_OPTION_ONE_DEV)
? (uint32)inode
: (((uint32)result) << 28) | (uint32) inode;
XDPRINTF((3,0, "Handle map inode=%d, dev=%d, namespace=%d to handle 0x%x",
inode, dnm->dev, dnm->namespace, handle));
uint32 handle;
if (v->options & VOL_OPTION_ONE_DEV) {
if (inode > v->high_inode)
goto cannot_map;
handle = (uint32)inode;
} else {
uint32 handle_inode = inode_to_handle_inode(volume, result, inode);
if (!handle_inode)
goto cannot_map;
handle = (((uint32)result) << 28) | handle_inode;
}
XDPRINTF((3,0, "Handle map inode=%ld, dev=%d, namespace=%d to handle 0x%x",
(long)inode, dnm->dev, dnm->namespace, handle));
return(handle);
}
}
}
XDPRINTF((1,0, "Cannot map inode=%d, dev=%d, namespace=%d to vol=%d handle",
inode, dnm->dev, dnm->namespace, volume));
cannot_map:
XDPRINTF((1,0, "Cannot map inode=%ld, dev=%d, namespace=%d to vol=%d handle",
(long)inode, dnm->dev, dnm->namespace, volume));
return(0L);
}
@@ -438,13 +551,16 @@ ino_t nw_vol_handle_to_inode(int volume, uint32 handle,
? 0
: (int) ((handle >> 28) & 0xF);
if (entry > -1 && entry < v->maps_count) {
ino_t inode = (v->options & VOL_OPTION_ONE_DEV)
? (ino_t)(handle & v->high_inode)
: handle_inode_to_inode(volume, entry, handle & v->high_inode);
if (dnm) memcpy(dnm, v->dev_namespace_maps[entry],
sizeof(DEV_NAMESPACE_MAP));
XDPRINTF((3, 0, "vol=%d, handle=0x%x to ino=%d, dev=%d, namespace=%d",
volume, handle, (int)(handle & v->high_inode),
XDPRINTF((3, 0, "vol=%d, handle=0x%x to ino=%ld, dev=%d, namespace=%d",
volume, handle, (long)inode,
v->dev_namespace_maps[entry]->dev,
v->dev_namespace_maps[entry]->namespace));
return((ino_t) (handle & v->high_inode));
return(inode);
}
}
XDPRINTF((1, 0, "Can't vol=%d, handle=0x%x to inode", volume, handle));