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); }