From 181c20620c2cdafe63d573c768c5a6ac051320ac Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Tue, 26 May 2026 14:29:51 +0200 Subject: [PATCH] Fix rename trustee rights and invalidate trustee cache Align rename permission checks with NetWare trustee semantics. A same-directory rename should be controlled by the Modify right instead of requiring a broader R/W/M combination. For moves to another parent directory, require Create rights on the destination parent. Also invalidate the trustee rights cache after adding or deleting trustee assignments so newly granted rights are visible immediately to subsequent operations. This fixes the MARIO trustee test where rename failed with R/W/M/F and even R/W/C/E/M/F, while the same operation succeeded only with the broader all-rights set. --- src/namspace.c | 35 ++++++++++++++++++++++++++++++----- src/trustee.c | 8 +++++++- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/namspace.c b/src/namspace.c index 1cb2fc7..8897335 100644 --- a/src/namspace.c +++ b/src/namspace.c @@ -2395,12 +2395,37 @@ static int nw_rename_file_dir(int namespace, uint8 *unname_d = (uint8*)alloc_nwpath2unix_extra(&(dbe_d->nwpath), 0, last_part); - if (tru_eff_rights_exists(dbe_s->nwpath.volume, unname_s, - &dbe_s->nwpath.statb, TRUSTEE_W|TRUSTEE_M|TRUSTEE_R)) - result=-0x8b; - else if (tru_eff_rights_exists(dbe_d->nwpath.volume, unname_dp, - &dbe_d->nwpath.statb, TRUSTEE_W)) + /* NetWare trustee semantics: M (Modify) is the right for renaming + * files/directories. A is for trustee/IRM changes, not rename. + * + * The old check required R|W|M on the source and W on the destination + * parent. That is stricter than NetWare for a same-directory rename and + * makes REN fail for users with Modify rights. + */ + if (tru_eff_rights_exists(dbe_s->nwpath.volume, unname_s, + &dbe_s->nwpath.statb, TRUSTEE_M)) { + XDPRINTF((5, 0, "Rename denied: missing M on source `%s`", unname_s)); result=-0x8b; + } else { + char src_parent[1024]; + char *slash; + + strmaxcpy((uint8*)src_parent, unname_s, sizeof(src_parent)-1); + slash = strrchr(src_parent, '/'); + if (slash && slash > src_parent) + *slash = '\0'; + + /* Only require destination Create rights when this is a move to a + * different parent directory. Same-directory rename is covered by M. + */ + if (slash && strcmp(src_parent, (char*)unname_dp) && + tru_eff_rights_exists(dbe_d->nwpath.volume, unname_dp, + &dbe_d->nwpath.statb, TRUSTEE_C)) { + XDPRINTF((5, 0, "Rename denied: missing C on dest parent `%s`", + unname_dp)); + result=-0x8b; + } + } if (result > -1) { if (seteuid(0)) {} diff --git a/src/trustee.c b/src/trustee.c index b55f778..4118900 100644 --- a/src/trustee.c +++ b/src/trustee.c @@ -465,8 +465,11 @@ int tru_del_trustee(int volume, uint8 *unixname, struct stat *stb, uint32 id) ( (tru_get_eff_rights(volume, unixname, stb) & TRUSTEE_A) || (act_id_flags&1)) ) { result=del_trustee_from_disk(volume, stb->st_dev, stb->st_ino, id); - if (!result) + if (!result) { tru_vol_sernum(volume, 1); /* trustee sernum needs updated */ + /* Trustee data changed on disk; cached effective rights are stale. */ + tru_free_cache(volume); + } } MDEBUG(D_TRUSTEES, { xdprintf(1,0, "tru_del_trustee: id=%08lx, volume=%d, file=`%s`, result=-0x%x", @@ -677,6 +680,9 @@ int tru_add_trustee_set(int volume, uint8 *unixname, } (void)reseteuid(); tru_vol_sernum(volume, 1); /* trustee sernum needs updated */ + /* local_tru_add_trustee_set() built/read the cache before writing the + * new trustee entry. Drop it so the next user sees the new rights. */ + tru_free_cache(volume); } return (result); }