quota: auto-select linux or nwquota backend
All checks were successful
Source release / source-package (push) Successful in 1m3s

This commit is contained in:
OpenAI
2026-06-11 05:17:47 +00:00
committed by Mario Fetka
parent 9186ef3404
commit b63aa71715
4 changed files with 78 additions and 45 deletions

View File

@@ -241,9 +241,19 @@ typedef struct zNW_trustee_name_s {
#define NWFS_METADATA_FIXED_SIZE offsetof(zNW_metadata_s, nwm_trustee)
#define NWFS_USERQUOTA_FIXED_SIZE offsetof(zNW_user_quota_s, nwuq_user)
#define NWFS_QUOTA_BACKEND_ENV "NWFS_QUOTA_BACKEND"
#define NWFS_QUOTA_BACKEND_AUTO "AUTO"
#define NWFS_QUOTA_BACKEND_LINUXQUOTA "LINUXQUOTA"
#define NWFS_QUOTA_BACKEND_METADATAONLY "METADATAONLY"
#define NWFS_QUOTA_BACKEND_NSS "NSS"
#define NWFS_QUOTA_BACKEND_NWQUOTA "NWQUOTA"
typedef enum nwfs_quota_backend_id_e {
NWFS_QUOTA_BACKEND_ID_AUTO = 0,
NWFS_QUOTA_BACKEND_ID_LINUXQUOTA = 1,
NWFS_QUOTA_BACKEND_ID_METADATAONLY = 2,
NWFS_QUOTA_BACKEND_ID_NSS = 3,
NWFS_QUOTA_BACKEND_ID_NWQUOTA = 4
} nwfs_quota_backend_id_e;
typedef enum nwfs_status_e {
NWFS_OK = 0,

View File

@@ -309,12 +309,6 @@ int nwfs_metadata_get_quota_limit(const zNW_metadata_s *metadata, SQUAD *quota_l
return NWFS_OK;
}
enum {
NWFS_QUOTA_BACKEND_ID_LINUXQUOTA = 0,
NWFS_QUOTA_BACKEND_ID_METADATAONLY = 1,
NWFS_QUOTA_BACKEND_ID_NSS = 2
};
static int nwfs_ascii_casecmp(const char *left, const char *right)
{
unsigned char lc;
@@ -343,7 +337,10 @@ static int nwfs_ascii_casecmp(const char *left, const char *right)
int nwfs_quota_backend_from_string(const char *name)
{
if (name == NULL || *name == '\0' ||
nwfs_ascii_casecmp(name, NWFS_QUOTA_BACKEND_LINUXQUOTA) == 0) {
nwfs_ascii_casecmp(name, NWFS_QUOTA_BACKEND_AUTO) == 0) {
return NWFS_QUOTA_BACKEND_ID_AUTO;
}
if (nwfs_ascii_casecmp(name, NWFS_QUOTA_BACKEND_LINUXQUOTA) == 0) {
return NWFS_QUOTA_BACKEND_ID_LINUXQUOTA;
}
if (nwfs_ascii_casecmp(name, NWFS_QUOTA_BACKEND_METADATAONLY) == 0) {
@@ -352,18 +349,25 @@ int nwfs_quota_backend_from_string(const char *name)
if (nwfs_ascii_casecmp(name, NWFS_QUOTA_BACKEND_NSS) == 0) {
return NWFS_QUOTA_BACKEND_ID_NSS;
}
if (nwfs_ascii_casecmp(name, NWFS_QUOTA_BACKEND_NWQUOTA) == 0) {
return NWFS_QUOTA_BACKEND_ID_NWQUOTA;
}
return NWFS_ERR_INVAL;
}
const char *nwfs_quota_backend_name(int backend)
{
switch (backend) {
case NWFS_QUOTA_BACKEND_ID_AUTO:
return NWFS_QUOTA_BACKEND_AUTO;
case NWFS_QUOTA_BACKEND_ID_LINUXQUOTA:
return NWFS_QUOTA_BACKEND_LINUXQUOTA;
case NWFS_QUOTA_BACKEND_ID_METADATAONLY:
return NWFS_QUOTA_BACKEND_METADATAONLY;
case NWFS_QUOTA_BACKEND_ID_NSS:
return NWFS_QUOTA_BACKEND_NSS;
case NWFS_QUOTA_BACKEND_ID_NWQUOTA:
return NWFS_QUOTA_BACKEND_NWQUOTA;
default:
return NULL;
}

View File

@@ -1095,38 +1095,35 @@ int nw_set_vol_restrictions(uint8 volnr, int uid, uint32 quota)
const char *device;
struct dqblk dqblk;
int res;
int backend = nwfs_quota_backend_current();
uint32 quota4k = quota;
XDPRINTF((2,0, "nw_set_vol_restrictions vol=%d uid=%d quota=%d blocks",
volnr, uid, quota));
XDPRINTF((2,0, "nw_set_vol_restrictions vol=%d uid=%d quota=%d blocks backend=%s",
volnr, uid, quota, nwfs_quota_backend_name(backend)));
if (nwfs_quota_backend_current() == 1)
return(nw_set_vol_restrictions_metadata(volnr, uid, quota));
/* Convert from blocks to K */
quota *= 4;
if (backend == NWFS_QUOTA_BACKEND_ID_METADATAONLY ||
backend == NWFS_QUOTA_BACKEND_ID_NWQUOTA)
return(nw_set_vol_restrictions_metadata(volnr, uid, quota4k));
if (volnr >= used_nw_volumes || nw_volumes == (NW_VOL *) NULL)
return(-0x98);
/* AUTO means: use Linux quota when the backing filesystem proves it can,
* otherwise store the NetWare restriction in netware.userquota for the
* internal nwquota simulation path. Keep explicit LINUXQUOTA strict enough
* for administrators who want to notice missing kernel quotas.
*/
device=find_device_file(nw_volumes[volnr].unixname);
if (device == (char *) NULL) {
XDPRINTF((2,0, "nw_set_vol_restrictions: no device for volume=%d path=%s, using metadata fallback",
volnr, nw_volumes[volnr].unixname));
/* Classic mars-nwe volumes such as SYS are often directories below a
* non-quota filesystem or pseudo/home volumes without a quotactl-capable
* backing device. SYSCON still uses User Volume/Disk Restrictions for
* NetWare 3.x/4.x style administration. Preserve the working Linux quota
* path when a device exists, but store restrictions in the NSS-compatible
* netware.userquota xattr when no kernel quota device can be found.
*/
quota /= 4; /* metadata stores NetWare 4K blocks, not Linux K blocks */
return(nw_set_vol_restrictions_metadata(volnr, uid, quota));
XDPRINTF((2,0,
"nw_set_vol_restrictions: no quota device for volume=%d path=%s, using nwquota metadata backend",
volnr, nw_volumes[volnr].unixname));
return(nw_set_vol_restrictions_metadata(volnr, uid, quota4k));
}
/* If this call fails then it it probable that quotas are not enabled
* on the specified device. Someone needs to set the error number
* to whatever will make most sense to netware.
*/
/* Convert from NetWare 4K blocks to Linux 1K quota blocks. */
quota *= 4;
res=su_quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device, uid, (caddr_t) &dqblk);
if (res != 0) {
@@ -1138,10 +1135,15 @@ int nw_set_vol_restrictions(uint8 volnr, int uid, uint32 quota)
XDPRINTF((2,0, "No quota record yet device=%s uid=%d, creating one",
device, uid));
memset(&dqblk, 0, sizeof(dqblk));
} else if (backend == NWFS_QUOTA_BACKEND_ID_AUTO) {
XDPRINTF((2,0,
"Linux quota probe failed device=%s uid=%d errno=%d; using nwquota metadata backend",
device, uid, err));
return(nw_set_vol_restrictions_metadata(volnr, uid, quota4k));
} else {
XDPRINTF((2,0, "Get quota before set failed device=%s uid=%d errno=%d",
device, uid, err));
return(0); /* Quotas are probably not enabled. Keep old compatibility. */
return(0); /* Explicit LINUXQUOTA old compatibility path. */
}
}
dqblk.dqb_bhardlimit = quota;
@@ -1156,14 +1158,20 @@ int nw_set_vol_restrictions(uint8 volnr, int uid, uint32 quota)
#endif
}
XDPRINTF((2,0, "Set quota device=%s uid=%d %d(%d)K %d(%d) files",
device, uid,
dqblk.dqb_bhardlimit,
(uint32)(dqblk.dqb_curspace / 1024),
dqblk.dqb_ihardlimit,
dqblk.dqb_curinodes));
device, uid,
dqblk.dqb_bhardlimit,
(uint32)(dqblk.dqb_curspace / 1024),
dqblk.dqb_ihardlimit,
dqblk.dqb_curinodes));
res=su_quotactl(QCMD(MARS_Q_SETQUOTA, USRQUOTA), device, uid, (caddr_t) &dqblk);
if (res != 0) {
if (backend == NWFS_QUOTA_BACKEND_ID_AUTO) {
XDPRINTF((2,0,
"Set Linux quota failed device=%s uid=%d quota=%dK errno=%d; using nwquota metadata backend",
device, uid, quota, errno));
return(nw_set_vol_restrictions_metadata(volnr, uid, quota4k));
}
XDPRINTF((2,0, "Set quota failed device=%s uid=%d quota=%dK errno=%d",
device, uid, quota, errno));
return(-0xfb);
@@ -1177,24 +1185,27 @@ int nw_get_vol_restrictions(uint8 volnr, int uid, uint32 *quota, uint32 *inuse)
const char *device;
struct dqblk dqblk;
int res;
int backend = nwfs_quota_backend_current();
*quota = 0x40000000;
*inuse = 0;
if (volnr >= used_nw_volumes || nw_volumes == (NW_VOL *) NULL)
return(-0x98);
if (nwfs_quota_backend_current() == 1)
if (backend == NWFS_QUOTA_BACKEND_ID_METADATAONLY ||
backend == NWFS_QUOTA_BACKEND_ID_NWQUOTA)
return(nw_get_vol_restrictions_metadata(volnr, uid, quota, inuse));
device=find_device_file(nw_volumes[volnr].unixname);
if (device == (char *) NULL) {
XDPRINTF((2,0,
"nw_get_vol_restrictions: no quota device for volume=%d path=%s, using metadata fallback",
"nw_get_vol_restrictions: no quota device for volume=%d path=%s, using nwquota metadata backend",
volnr, nw_volumes[volnr].unixname));
return(nw_get_vol_restrictions_metadata(volnr, uid, quota, inuse));
}
XDPRINTF((2,0, "Get quota for uid %d on device %s",
uid, device));
uid, device));
res=su_quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device, uid, (caddr_t) &dqblk);
@@ -1208,9 +1219,15 @@ int nw_get_vol_restrictions(uint8 volnr, int uid, uint32 *quota, uint32 *inuse)
*inuse = 0;
return(0);
}
if (backend == NWFS_QUOTA_BACKEND_ID_AUTO) {
XDPRINTF((2,0,
"Linux quota probe failed device=%s uid=%d errno=%d; using nwquota metadata backend",
device, uid, err));
return(nw_get_vol_restrictions_metadata(volnr, uid, quota, inuse));
}
XDPRINTF((2,0, "Get quota failed device=%s uid=%d errno=%d",
device, uid, err));
return(0); /* Quotas are probably not enabled */
return(0); /* Explicit LINUXQUOTA old compatibility path. */
}
if (dqblk.dqb_bhardlimit == 0) {
*quota = 0x40000000;

View File

@@ -93,11 +93,13 @@ int main(void)
CHECK(nwfs_xattr_name_visible(zNW_METADATA, 1));
CHECK(nwfs_xattr_name_visible(zNW_QUOTA, 0));
CHECK(nwfs_quota_backend_from_string(NULL) == 0);
CHECK(nwfs_quota_backend_from_string(NWFS_QUOTA_BACKEND_LINUXQUOTA) == 0);
CHECK(nwfs_quota_backend_from_string(NWFS_QUOTA_BACKEND_METADATAONLY) == 1);
CHECK(nwfs_quota_backend_from_string(NWFS_QUOTA_BACKEND_NSS) == 2);
CHECK(nwfs_quota_backend_name(1) != NULL);
CHECK(nwfs_quota_backend_from_string(NULL) == NWFS_QUOTA_BACKEND_ID_AUTO);
CHECK(nwfs_quota_backend_from_string(NWFS_QUOTA_BACKEND_AUTO) == NWFS_QUOTA_BACKEND_ID_AUTO);
CHECK(nwfs_quota_backend_from_string(NWFS_QUOTA_BACKEND_LINUXQUOTA) == NWFS_QUOTA_BACKEND_ID_LINUXQUOTA);
CHECK(nwfs_quota_backend_from_string(NWFS_QUOTA_BACKEND_METADATAONLY) == NWFS_QUOTA_BACKEND_ID_METADATAONLY);
CHECK(nwfs_quota_backend_from_string(NWFS_QUOTA_BACKEND_NSS) == NWFS_QUOTA_BACKEND_ID_NSS);
CHECK(nwfs_quota_backend_from_string(NWFS_QUOTA_BACKEND_NWQUOTA) == NWFS_QUOTA_BACKEND_ID_NWQUOTA);
CHECK(nwfs_quota_backend_name(NWFS_QUOTA_BACKEND_ID_NWQUOTA) != NULL);
CHECK(nwfs_quota_backend_from_string("bogus") == NWFS_ERR_INVAL);
nwfs_quota_init(&quota);