From ccc7d63be90d6402a59f39f3dc8dae7d367fb0f7 Mon Sep 17 00:00:00 2001 From: OpenAI Date: Fri, 12 Jun 2026 08:07:56 +0000 Subject: [PATCH] quota: avoid nwquota fallback on linux set failures --- AI.md | 10 ++++++++++ REDESIGN.md | 21 +++++++++++++++++++++ TODO.md | 9 +++++++++ src/nwfs/quota/lnxquota.c | 16 ++++++++-------- src/nwvolume.c | 24 +++++++++--------------- 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/AI.md b/AI.md index aa28305..6dc6593 100644 --- a/AI.md +++ b/AI.md @@ -2313,3 +2313,13 @@ logs/archives are usually copied or uploaded later by a normal desktop user. later live smokes, `nw.log` slice, tar.gz and zip from being emitted. - Keep the archive outside the output directory (`/tmp/-quota-all-smoke.*`) so tar/zip do not recursively include their own output file. + +### 0384 Linuxquota clear/log cleanup note + +- Linuxquota set/clear must not echo a broad `dqb_valid` mask from `Q_GETQUOTA` + back into `Q_SETQUOTA`. Set only block-limit fields for NetWare user-volume + restrictions; usage and inode fields remain kernel-owned. +- AUTO fallback to NWQUOTA is for genuinely unavailable Linux quota devices + (`no-device`, `unsupported`, `probe-failed`). A real Linuxquota `set-failed` + on a QUOTA-style volume is an error to fix, not a reason to create a parallel + metadata-authoritative state. diff --git a/REDESIGN.md b/REDESIGN.md index d11ddcd..b7497c7 100644 --- a/REDESIGN.md +++ b/REDESIGN.md @@ -3469,3 +3469,24 @@ logs/archives are usually copied or uploaded later by a normal desktop user. later live smokes, `nw.log` slice, tar.gz and zip from being emitted. - Keep the archive outside the output directory (`/tmp/-quota-all-smoke.*`) so tar/zip do not recursively include their own output file. + +### 0384 Linuxquota clear/log cleanup + +The all-quota smoke after 0383 showed the quota functionality green, but the +server log still contained an ugly QUOTA-side message during userquota remove: +`Linux quota set unavailable ... result=set-failed; using nwquota metadata backend`. +That is contrary to the backend ownership rule. On Linuxquota volumes the +kernel quota backend is authoritative; NWQUOTA metadata is only a mirror and a +restore source, not an alternate authority after a kernel `set-failed`. + +0384 tightens this in two ways: + +- `lnxquota.c` no longer reuses the broad `dqb_valid` mask returned by + `Q_GETQUOTA` when calling `Q_SETQUOTA`; it marks only block limits valid. + This matches the project-quota fix made earlier and avoids kernels rejecting + attempts to set usage/grace/inode fields when mars-nwe only means to update a + NetWare user-volume block restriction. +- `nw_set_vol_restrictions()` falls back to NWQUOTA only when Linux quota is + unavailable (`no-device`, `unsupported`, `probe-failed`). A real `set-failed` + remains a Linuxquota error instead of silently switching QUOTA to a parallel + metadata-authoritative path. diff --git a/TODO.md b/TODO.md index 31a171c..6e9e600 100644 --- a/TODO.md +++ b/TODO.md @@ -2572,3 +2572,12 @@ logs/archives are usually copied or uploaded later by a normal desktop user. later live smokes, `nw.log` slice, tar.gz and zip from being emitted. - Keep the archive outside the output directory (`/tmp/-quota-all-smoke.*`) so tar/zip do not recursively include their own output file. + +### 0384 quota cleanup follow-up + +- DONE in 0384: remove the misleading AUTO fallback on Linuxquota `set-failed`; + fallback to NWQUOTA only when Linux quota is unavailable/probe-failed. +- DONE in 0384: Linux userquota `Q_SETQUOTA` sets only block-limit fields so + clear/remove restrictions should not log `set-failed; using nwquota metadata backend`. +- After applying 0384, rerun `nwfs_ncpfs_all_quota_smoke.sh` and verify the + `nw.log` slice has no QUOTA-side `set-failed; using nwquota metadata backend`. diff --git a/src/nwfs/quota/lnxquota.c b/src/nwfs/quota/lnxquota.c index 0b95d0f..50d5051 100644 --- a/src/nwfs/quota/lnxquota.c +++ b/src/nwfs/quota/lnxquota.c @@ -170,16 +170,16 @@ int nwfs_lnxquota_set_restriction(const char *volume_root, int uid, dqblk.dqb_bhardlimit = quota1k; dqblk.dqb_bsoftlimit = quota1k; + /* Q_GETQUOTA can return a broad dqb_valid mask. Do not echo it back to + * Q_SETQUOTA, because some kernels reject attempts to set usage/grace-time + * fields together with the block limits. NetWare user-volume restrictions + * map only to block limits here; inode limits and usage stay kernel-owned. + */ # ifdef QIF_BLIMITS - dqblk.dqb_valid |= QIF_BLIMITS; + dqblk.dqb_valid = QIF_BLIMITS; +# else + dqblk.dqb_valid = 0; # endif - if (quota1k == 0) { - dqblk.dqb_ihardlimit = 0; - dqblk.dqb_isoftlimit = 0; -# ifdef QIF_ILIMITS - dqblk.dqb_valid |= QIF_ILIMITS; -# endif - } res = nwfs_lnxquota_quotactl(QCMD(NWFS_LNXQUOTA_SET_CMD, USRQUOTA), device, uid, (caddr_t)&dqblk); diff --git a/src/nwvolume.c b/src/nwvolume.c index 2322f31..82c8e05 100644 --- a/src/nwvolume.c +++ b/src/nwvolume.c @@ -972,9 +972,10 @@ int nw_set_vol_restrictions(uint8 volnr, int uid, uint32 quota) return(0); } - if (backend == NWFS_QUOTA_BACKEND_ID_AUTO || - lnxres == NWFS_LNXQUOTA_NO_DEVICE || - lnxres == NWFS_LNXQUOTA_UNSUPPORTED) { + if (backend == NWFS_QUOTA_BACKEND_ID_AUTO && + (lnxres == NWFS_LNXQUOTA_NO_DEVICE || + lnxres == NWFS_LNXQUOTA_UNSUPPORTED || + lnxres == NWFS_LNXQUOTA_PROBE_FAILED)) { XDPRINTF((2,0, "Linux quota set unavailable volume=%d path=%s uid=%d result=%s; using nwquota metadata backend", volnr, nw_volumes[volnr].unixname, uid, @@ -982,14 +983,6 @@ int nw_set_vol_restrictions(uint8 volnr, int uid, uint32 quota) return(nwfs_nwquota_set_restriction((const char*)nw_volumes[volnr].unixname, uid, quota) ? -0xfb : 0); } - if (lnxres == NWFS_LNXQUOTA_PROBE_FAILED) { - XDPRINTF((2,0, - "Linux quota probe failed volume=%d path=%s uid=%d result=%s", - volnr, nw_volumes[volnr].unixname, uid, - nwfs_lnxquota_result_name(lnxres))); - return(0); /* Explicit LINUXQUOTA old compatibility path. */ - } - XDPRINTF((2,0, "Linux quota set failed volume=%d path=%s uid=%d result=%s", volnr, nw_volumes[volnr].unixname, uid, @@ -1024,10 +1017,11 @@ int nw_get_vol_restrictions(uint8 volnr, int uid, uint32 *quota, uint32 *inuse) nw_restore_lnxquota_from_nwquota(volnr, uid, quota, inuse)) return(0); - if (backend == NWFS_QUOTA_BACKEND_ID_AUTO || - lnxres == NWFS_LNXQUOTA_NO_DEVICE || - lnxres == NWFS_LNXQUOTA_UNSUPPORTED || - lnxres == NWFS_LNXQUOTA_NO_ENTRY) { + if (backend == NWFS_QUOTA_BACKEND_ID_AUTO && + (lnxres == NWFS_LNXQUOTA_NO_DEVICE || + lnxres == NWFS_LNXQUOTA_UNSUPPORTED || + lnxres == NWFS_LNXQUOTA_PROBE_FAILED || + lnxres == NWFS_LNXQUOTA_NO_ENTRY)) { XDPRINTF((2,0, "Linux quota get unavailable volume=%d path=%s uid=%d result=%s; using nwquota metadata backend", volnr, nw_volumes[volnr].unixname, uid,