diff --git a/include/nwfs/zXattr.h b/include/nwfs/zXattr.h index 149511a..a00df50 100644 --- a/include/nwfs/zXattr.h +++ b/include/nwfs/zXattr.h @@ -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, diff --git a/src/nwfs/lsaComn.c b/src/nwfs/lsaComn.c index a2b0765..a0ff727 100644 --- a/src/nwfs/lsaComn.c +++ b/src/nwfs/lsaComn.c @@ -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; } diff --git a/src/nwvolume.c b/src/nwvolume.c index 5452fc4..0d35f2c 100644 --- a/src/nwvolume.c +++ b/src/nwvolume.c @@ -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; diff --git a/tests/nwfs/nwfs_xattr_roundtrip_test.c b/tests/nwfs/nwfs_xattr_roundtrip_test.c index c9c263b..acd2b08 100644 --- a/tests/nwfs/nwfs_xattr_roundtrip_test.c +++ b/tests/nwfs/nwfs_xattr_roundtrip_test.c @@ -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("a);