From 223533af1ef3ee629405660ad4591d12c10d2b01 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Wed, 20 May 2026 20:19:13 +0200 Subject: [PATCH] nwvolume: map large Unix inodes without ONE_DEV Add internal inode mapping for normal multi-namespace handle mode so large Unix inode numbers can be represented in the 28-bit inode field. This allows OS/2 namespace clients such as Linux ncpfs to access long names without requiring the 'o' volume option. --- src/nwvolume.c | 119 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 11 deletions(-) diff --git a/src/nwvolume.c b/src/nwvolume.c index 1d12ad1..2b1c62c 100644 --- a/src/nwvolume.c +++ b/src/nwvolume.c @@ -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"); @@ -430,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); } @@ -457,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));