quota: split generic quota and nwquota files
All checks were successful
Source release / source-package (push) Successful in 1m4s

This commit is contained in:
Mario Fetka
2026-06-11 12:22:50 +00:00
parent 66cc0a81d7
commit d223e8ef00
9 changed files with 143 additions and 82 deletions

16
AI.md
View File

@@ -2074,12 +2074,24 @@ and all submodules, then rebuild a clean tree before producing new patches.
Next patch number should be `0271`.
### 0343 quota file/name split handoff note
0343 keeps the quota backends deliberately distinguishable. The generic quota
frontend helpers live in `include/nwfs/quota.h` and `src/nwfs/quota/quota.c`
with `nwfs_quota_*` names only. The NetWare metadata backend lives in
`include/nwfs/nwquota.h` and `src/nwfs/quota/nwquota.c` with
`nwfs_nwquota_*` public names and `nwfs_nwquota_*` private helpers.
Do not merge Linux quota and NWQUOTA back into one source file. Future Linux
`quotactl()` relocation should get a separate backend implementation while
keeping the generic `quota.c` file backend-neutral.
### 0342 quota relocation handoff note
0342 starts the planned quota move into `libnwfs`. It moves the
metadata/NWQUOTA backend helpers from `src/nwvolume.c` into
`src/nwfs/quota/nwfsQuota.c` with public declarations in
`include/nwfs/nwfsQuota.h`. `src/nwvolume.c` remains the mars-nwe volume/NCP
`src/nwfs/quota/quota.c` and `src/nwfs/quota/nwquota.c` with public declarations in
`include/nwfs/quota.h` and `include/nwfs/nwquota.h`. `src/nwvolume.c` remains the mars-nwe volume/NCP
entry point and still handles Linux `quotactl()` probing, but now calls libnwfs
for NWQUOTA restriction, usage, and adjust operations.

View File

@@ -3207,13 +3207,13 @@ work where project-level headers are added.
Third-party imports remain under their own upstream licenses inside the relevant
`third_party/` subtree and must be documented separately.
### Quota backend placement after 0342
### Quota backend placement after 0343
The first quota relocation step moves the metadata/NWQUOTA implementation into
The quota relocation keeps the two quota systems deliberately separated inside
`libnwfs`:
- public API: `include/nwfs/nwfsQuota.h`
- implementation: `src/nwfs/quota/nwfsQuota.c`
- generic quota frontend/API: `include/nwfs/quota.h`, `src/nwfs/quota/quota.c`, `nwfs_quota_*`
- NetWare metadata backend/API: `include/nwfs/nwquota.h`, `src/nwfs/quota/nwquota.c`, `nwfs_nwquota_*`
- volume-layer callers: `src/nwvolume.c`
`src/nwvolume.c` remains the NCP/volume entry point and still owns Linux

15
TODO.md
View File

@@ -2323,12 +2323,17 @@ Follow-up:
system-collision risk. If yes, give the library, headers, CMake package, and
imported tools an `nw` namespace from the first patch.
### Quota relocation follow-ups after 0342
### Quota relocation follow-ups after 0343
- Re-run `tests/nwfs/nwfs_ncpfs_userquota_dual_smoke.sh` after the 0342
structural move. Expected result stays the same as 0340: Linuxquota and
metadata/NWQUOTA both deny the next 4K write before data.
- Re-run `tests/nwfs/nwfs_ncpfs_userquota_dual_smoke.sh` after the 0343
file/function split. Expected result stays the same as 0340/0342:
Linuxquota and metadata/NWQUOTA both deny the next 4K write before data.
- Keep generic quota helpers in `src/nwfs/quota/quota.c` /
`include/nwfs/quota.h` with `nwfs_quota_*` names only.
- Keep NetWare metadata quota in `src/nwfs/quota/nwquota.c` /
`include/nwfs/nwquota.h` with `nwfs_nwquota_*` names.
- Move the remaining Linux `quotactl()` backend out of `src/nwvolume.c` into
`src/nwfs/quota/` once 0342 is confirmed green.
its own `src/nwfs/quota/` backend file once 0343 is confirmed green; do not
merge it into `nwquota.c`.
- Keep `src/nwvolume.c` as the stable NCP volume API shim until namespace and
quota are both sufficiently represented in `libnwfs`.

View File

@@ -1,13 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _NWFS_QUOTA_H_
#define _NWFS_QUOTA_H_ 1
#ifndef _NWFS_NWQUOTA_H_
#define _NWFS_NWQUOTA_H_ 1
#include <stdint.h>
#include <sys/types.h>
uint32_t nwfs_quota_blocks_for_size(off_t size);
int nwfs_quota_backend_current(void);
/*
* NetWare metadata quota backend. This owns the volume-root
* netware.userquota xattr and keeps restriction/usage accounting separate
* from Linux kernel quotactl quota.
*/
int nwfs_nwquota_set_restriction(const char *volume_root, int uid,
uint32_t quota4k);
int nwfs_nwquota_get_restriction(const char *volume_root, int uid,

16
include/nwfs/quota.h Normal file
View File

@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _NWFS_QUOTA_FRONTEND_H_
#define _NWFS_QUOTA_FRONTEND_H_ 1
#include <stdint.h>
#include <sys/types.h>
/*
* Generic quota helpers shared by quota backends and the mars_nwe volume
* layer. Backend-specific APIs live in separate headers such as nwquota.h;
* do not add NWQUOTA or Linux quotactl entry points here.
*/
uint32_t nwfs_quota_blocks_for_size(off_t size);
int nwfs_quota_backend_current(void);
#endif

View File

@@ -8,7 +8,8 @@ add_library(nwfs SHARED
unixNSpace.c
longNSpace.c
dataStreamNSpace.c
quota/nwfsQuota.c)
quota/quota.c
quota/nwquota.c)
add_library(mars_nwe::nwfs ALIAS nwfs)
set_target_properties(nwfs PROPERTIES
@@ -34,7 +35,8 @@ install(TARGETS nwfs
install(FILES
"${CMAKE_SOURCE_DIR}/include/nwfs/zXattr.h"
"${CMAKE_SOURCE_DIR}/include/nwfs/nwfsQuota.h"
"${CMAKE_SOURCE_DIR}/include/nwfs/quota.h"
"${CMAKE_SOURCE_DIR}/include/nwfs/nwquota.h"
"${CMAKE_SOURCE_DIR}/include/nwfs/zasAuthModel.h"
"${CMAKE_SOURCE_DIR}/include/nwfs/nameSpaceModel.h"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nwfs")

View File

@@ -1,12 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/*
* NetWare-compatible quota helpers for mars_nwe/libnwfs.
* NetWare metadata quota backend for libnwfs.
*
* This file contains the NWQUOTA metadata backend that stores per-user
* restrictions and live usage in the volume-root netware.userquota xattr.
* Linux kernel quota probing is still driven by the mars_nwe volume layer;
* this file owns the NetWare metadata backend so other nwfs namespace code can
* eventually share it as the filesystem compatibility layer grows.
* This file owns NWQUOTA accounting: per-user restrictions and live usage are
* stored in the volume-root netware.userquota xattr. Keep this backend
* separate from the generic quota frontend (quota.c) and from the future Linux
* kernel quotactl backend.
*/
#if defined(__has_include)
# if __has_include("config.h")
@@ -32,34 +34,23 @@
#endif
#include <nwfs/zXattr.h>
#include <nwfs/nwfsQuota.h>
#include <nwfs/quota.h>
#include <nwfs/nwquota.h>
uint32_t nwfs_quota_blocks_for_size(off_t size)
{
if (size <= 0)
return(0);
return((uint32_t)(((unsigned long long)size + 4095ULL) / 4096ULL));
}
int nwfs_quota_backend_current(void)
{
return(nwfs_quota_backend_from_string(getenv(NWFS_QUOTA_BACKEND_ENV)));
}
static void nwfs_mars_uid_to_guid(int uid, GUID_t *guid)
static void nwfs_nwquota_mars_uid_to_guid(int uid, GUID_t *guid)
{
memset(guid, 0, sizeof(*guid));
guid->timeLow = (LONG)uid;
}
static int nwfs_guid_to_mars_uid(const GUID_t *guid)
static int nwfs_nwquota_guid_to_mars_uid(const GUID_t *guid)
{
return((int)guid->timeLow);
}
#if XATTR_SUPPORT
static const char *nwfs_xattr_name(const char *name, char *buffer,
size_t buffer_len)
static const char *nwfs_nwquota_xattr_name(const char *name, char *buffer,
size_t buffer_len)
{
if (!name || !*name)
return(name);
@@ -85,12 +76,12 @@ static const char *nwfs_xattr_name(const char *name, char *buffer,
}
#endif
static ssize_t nwfs_quota_getxattr(const char *path, const char *name,
void *value, size_t size)
static ssize_t nwfs_nwquota_getxattr(const char *path, const char *name,
void *value, size_t size)
{
#if XATTR_SUPPORT
char xname[256 + 5];
return(getxattr(path, nwfs_xattr_name(name, xname, sizeof(xname)),
return(getxattr(path, nwfs_nwquota_xattr_name(name, xname, sizeof(xname)),
value, size));
#else
(void)path;
@@ -102,12 +93,12 @@ static ssize_t nwfs_quota_getxattr(const char *path, const char *name,
#endif
}
static int nwfs_quota_setxattr(const char *path, const char *name,
const void *value, size_t size, int flags)
static int nwfs_nwquota_setxattr(const char *path, const char *name,
const void *value, size_t size, int flags)
{
#if XATTR_SUPPORT
char xname[256 + 5];
return(setxattr(path, nwfs_xattr_name(name, xname, sizeof(xname)),
return(setxattr(path, nwfs_nwquota_xattr_name(name, xname, sizeof(xname)),
value, size, flags));
#else
(void)path;
@@ -120,8 +111,8 @@ static int nwfs_quota_setxattr(const char *path, const char *name,
#endif
}
static int read_userquota_from_volume(const char *volume_root,
zNW_user_quota_s *userquota)
static int nwfs_nwquota_read_userquota_from_volume(const char *volume_root,
zNW_user_quota_s *userquota)
{
ssize_t len;
int euid;
@@ -142,8 +133,8 @@ static int read_userquota_from_volume(const char *volume_root,
return(-1);
memset(userquota, 0, sizeof(*userquota));
len = nwfs_quota_getxattr(volume_root, zNW_USERQUOTA, userquota,
sizeof(*userquota));
len = nwfs_nwquota_getxattr(volume_root, zNW_USERQUOTA, userquota,
sizeof(*userquota));
if (len < 0) {
rc = -1;
} else if (nwfs_userquota_validate(userquota, (size_t)len) != NWFS_OK) {
@@ -155,8 +146,8 @@ static int read_userquota_from_volume(const char *volume_root,
return(rc);
}
static int write_userquota_to_volume(const char *volume_root,
zNW_user_quota_s *userquota)
static int nwfs_nwquota_write_userquota_to_volume(const char *volume_root,
zNW_user_quota_s *userquota)
{
size_t size;
int euid;
@@ -173,20 +164,20 @@ static int write_userquota_to_volume(const char *volume_root,
if (euid != 0 && seteuid(0))
return(-1);
rc = nwfs_quota_setxattr(volume_root, zNW_USERQUOTA, userquota, size, 0);
rc = nwfs_nwquota_setxattr(volume_root, zNW_USERQUOTA, userquota, size, 0);
if (euid != 0 && seteuid(euid))
abort();
return(rc);
}
static int nwfs_volume_file_owned_by_quota_user(const struct stat *st, int uid)
static int nwfs_nwquota_file_owned_by_quota_user(const struct stat *st, int uid)
{
return(st && st->st_uid == (uid_t)uid);
}
static uint32_t nwfs_volume_user_usage_4k_scan(const char *path, int uid,
dev_t root_dev)
static uint32_t nwfs_nwquota_user_usage_4k_scan(const char *path, int uid,
dev_t root_dev)
{
DIR *dir;
struct dirent *de;
@@ -197,7 +188,7 @@ static uint32_t nwfs_volume_user_usage_4k_scan(const char *path, int uid,
return(0);
if (st.st_dev != root_dev)
return(0);
if (S_ISREG(st.st_mode) && nwfs_volume_file_owned_by_quota_user(&st, uid))
if (S_ISREG(st.st_mode) && nwfs_nwquota_file_owned_by_quota_user(&st, uid))
return(nwfs_quota_blocks_for_size(st.st_size));
if (!S_ISDIR(st.st_mode))
return(0);
@@ -222,13 +213,13 @@ static uint32_t nwfs_volume_user_usage_4k_scan(const char *path, int uid,
child[len] = '\0';
}
strcpy(child + len, de->d_name);
blocks += nwfs_volume_user_usage_4k_scan(child, uid, root_dev);
blocks += nwfs_nwquota_user_usage_4k_scan(child, uid, root_dev);
}
closedir(dir);
return(blocks);
}
static uint32_t nwfs_volume_user_usage_4k(const char *volume_root, int uid)
static uint32_t nwfs_nwquota_user_usage_4k(const char *volume_root, int uid)
{
struct stat st;
@@ -236,11 +227,11 @@ static uint32_t nwfs_volume_user_usage_4k(const char *volume_root, int uid)
return(0);
if (stat(volume_root, &st) != 0)
return(0);
return(nwfs_volume_user_usage_4k_scan(volume_root, uid, st.st_dev));
return(nwfs_nwquota_user_usage_4k_scan(volume_root, uid, st.st_dev));
}
static uint32_t nwfs_userquota_used_4k(zNW_user_restriction_s *entry,
uint32_t scanned)
static uint32_t nwfs_nwquota_used_4k(zNW_user_restriction_s *entry,
uint32_t scanned)
{
uint32_t stored;
@@ -267,13 +258,13 @@ int nwfs_nwquota_set_restriction(const char *volume_root, int uid,
uint32_t inuse;
int i;
if (read_userquota_from_volume(volume_root, &userquota))
if (nwfs_nwquota_read_userquota_from_volume(volume_root, &userquota))
nwfs_userquota_init(&userquota);
inuse = nwfs_volume_user_usage_4k(volume_root, uid);
inuse = nwfs_nwquota_user_usage_4k(volume_root, uid);
for (i = 0; i < userquota.nwuq_num_users; i++) {
if (nwfs_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) {
if (nwfs_nwquota_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) {
if (quota4k == 0) {
if (i + 1 < userquota.nwuq_num_users) {
memmove(&userquota.nwuq_user[i], &userquota.nwuq_user[i + 1],
@@ -285,7 +276,7 @@ int nwfs_nwquota_set_restriction(const char *volume_root, int uid,
userquota.nwuq_user[i].nwur_restriction = (SQUAD)quota4k;
userquota.nwuq_user[i].nwur_reserved_2 = (QUAD)inuse;
}
return(write_userquota_to_volume(volume_root, &userquota));
return(nwfs_nwquota_write_userquota_to_volume(volume_root, &userquota));
}
}
@@ -294,11 +285,12 @@ int nwfs_nwquota_set_restriction(const char *volume_root, int uid,
if (userquota.nwuq_num_users >= zMAX_XATTR_USERS)
return(-1);
nwfs_mars_uid_to_guid(uid, &userquota.nwuq_user[userquota.nwuq_num_users].nwur_user);
nwfs_nwquota_mars_uid_to_guid(
uid, &userquota.nwuq_user[userquota.nwuq_num_users].nwur_user);
userquota.nwuq_user[userquota.nwuq_num_users].nwur_restriction = (SQUAD)quota4k;
userquota.nwuq_user[userquota.nwuq_num_users].nwur_reserved_2 = (QUAD)inuse;
userquota.nwuq_num_users++;
return(write_userquota_to_volume(volume_root, &userquota));
return(nwfs_nwquota_write_userquota_to_volume(volume_root, &userquota));
}
int nwfs_nwquota_get_restriction(const char *volume_root, int uid,
@@ -312,17 +304,17 @@ int nwfs_nwquota_get_restriction(const char *volume_root, int uid,
*quota4k = 0x40000000;
*inuse4k = 0;
if (read_userquota_from_volume(volume_root, &userquota))
if (nwfs_nwquota_read_userquota_from_volume(volume_root, &userquota))
return(0);
for (i = 0; i < userquota.nwuq_num_users; i++) {
if (nwfs_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) {
uint32_t scanned = nwfs_volume_user_usage_4k(volume_root, uid);
if (nwfs_nwquota_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) {
uint32_t scanned = nwfs_nwquota_user_usage_4k(volume_root, uid);
if (userquota.nwuq_user[i].nwur_restriction <= 0)
*quota4k = 0x40000000;
else
*quota4k = (uint32_t)userquota.nwuq_user[i].nwur_restriction;
*inuse4k = nwfs_userquota_used_4k(&userquota.nwuq_user[i], scanned);
*inuse4k = nwfs_nwquota_used_4k(&userquota.nwuq_user[i], scanned);
return(0);
}
}
@@ -334,11 +326,11 @@ int nwfs_nwquota_has_restriction(const char *volume_root, int uid)
zNW_user_quota_s userquota;
int i;
if (read_userquota_from_volume(volume_root, &userquota))
if (nwfs_nwquota_read_userquota_from_volume(volume_root, &userquota))
return(0);
for (i = 0; i < userquota.nwuq_num_users; i++) {
if (nwfs_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid)
if (nwfs_nwquota_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid)
return(1);
}
return(0);
@@ -354,13 +346,13 @@ int nwfs_nwquota_adjust_usage(const char *volume_root, int uid,
*used4k = 0;
if (delta4k == 0)
return(0);
if (read_userquota_from_volume(volume_root, &userquota))
if (nwfs_nwquota_read_userquota_from_volume(volume_root, &userquota))
return(0);
for (i = 0; i < userquota.nwuq_num_users; i++) {
if (nwfs_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) {
uint32_t scanned = nwfs_volume_user_usage_4k(volume_root, uid);
uint32_t used = nwfs_userquota_used_4k(&userquota.nwuq_user[i], scanned);
if (nwfs_nwquota_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) {
uint32_t scanned = nwfs_nwquota_user_usage_4k(volume_root, uid);
uint32_t used = nwfs_nwquota_used_4k(&userquota.nwuq_user[i], scanned);
if (delta4k < 0) {
uint32_t dec = (uint32_t)(-delta4k);
@@ -373,7 +365,7 @@ int nwfs_nwquota_adjust_usage(const char *volume_root, int uid,
userquota.nwuq_user[i].nwur_reserved_2 = (QUAD)used;
if (used4k)
*used4k = used;
return(write_userquota_to_volume(volume_root, &userquota));
return(nwfs_nwquota_write_userquota_to_volume(volume_root, &userquota));
}
}
return(0);

32
src/nwfs/quota/quota.c Normal file
View File

@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Generic quota helpers for libnwfs.
*
* Keep this file backend-neutral. NetWare metadata quota lives in nwquota.c;
* Linux kernel quotactl quota should get its own backend file when it moves
* from the mars_nwe volume layer.
*/
#if defined(__has_include)
# if __has_include("config.h")
# include "config.h"
# endif
#endif
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <nwfs/zXattr.h>
#include <nwfs/quota.h>
uint32_t nwfs_quota_blocks_for_size(off_t size)
{
if (size <= 0)
return(0);
return((uint32_t)(((unsigned long long)size + 4095ULL) / 4096ULL));
}
int nwfs_quota_backend_current(void)
{
return(nwfs_quota_backend_from_string(getenv(NWFS_QUOTA_BACKEND_ENV)));
}

View File

@@ -55,7 +55,8 @@
#include "unxfile.h"
#include "nwxattr.h"
#include <nwfs/zXattr.h>
#include <nwfs/nwfsQuota.h>
#include <nwfs/quota.h>
#include <nwfs/nwquota.h>
#define VOLOPTIONS_DEFAULT VOL_OPTION_ATTRIBUTES