tests: add ncpfs quota smoke helpers
Some checks failed
Source release / source-package (push) Failing after 24s

This commit is contained in:
OpenAI
2026-06-10 15:48:35 +00:00
committed by Mario Fetka
parent 8e5b8a7de3
commit 09fd4ea8f4
5 changed files with 402 additions and 25 deletions

View File

@@ -58,11 +58,13 @@ static int act_umode_file=0;
#include "nwattrib.h"
#include "nwarchive.h"
#include "trustee.h"
#include "nwxattr.h"
#include "nwfile.h"
#include "nwconn.h"
#include "namspace.h"
#include "namedos.h"
#include "connect.h"
#include <nwfs/zXattr.h>
/* connect.h may already be include-guarded through another header before
* NW_VOL is visible, so keep this local forward declaration before the
@@ -1466,6 +1468,40 @@ static int set_ncp22_25_file_info(char *unixname,
}
static int set_ncp22_25_dir_quota_metadata(char *unixname, uint32 max_space)
{
zNW_metadata_s metadata;
ssize_t len;
size_t size;
SQUAD quota;
if (unixname == NULL)
return(-0x8c);
memset(&metadata, 0, sizeof(metadata));
len = mars_nwe_getxattr(unixname, zNW_METADATA, &metadata, sizeof(metadata));
if (len < 0) {
nwfs_metadata_init(&metadata);
} else if (nwfs_metadata_validate(&metadata, (size_t)len) != NWFS_OK) {
XDPRINTF((2,0, "invalid netware.metadata while setting dir quota: %s", unixname));
return(-0x8c);
}
if (max_space == MAX_U32 || max_space == 0x40000000UL)
quota = zDIR_NO_QUOTA;
else
quota = (SQUAD)max_space;
if (nwfs_metadata_set_quota_limit(&metadata, quota) != NWFS_OK)
return(-0x8c);
size = nwfs_metadata_size_for_trustees(metadata.nwm_trustee_num);
if (size == 0 || mars_nwe_setxattr(unixname, zNW_METADATA, &metadata, size, 0))
return(-0x8c);
return(0);
}
static int set_ncp22_25_maximum_space(int volume, char *unixname,
struct stat *stb,
NW_SET_DIR_INFO *f,
@@ -1473,16 +1509,15 @@ static int set_ncp22_25_maximum_space(int volume, char *unixname,
int is_dir)
{
uint32 max_space;
uint32 quota;
int result;
if (!(change_mask & DM_MAXIMUM_SPACE))
return(0);
/* NetWare exposes maximum space on directory entries. mars_nwe already
* has a Linux user-quota backend for volume restrictions; use that as the
* closest available mapping. Files have no max_space field in the DOS
* layout, so ignore the bit if a client sends it for a file.
/* NetWare/NSS exposes maximum space on directory entries. Keep it in the
* OES-compatible netware.metadata nwm_quota_limit field; user/volume
* restrictions are a separate netware.userquota data model and are handled
* by the volume-restriction NCPs.
*/
if (!is_dir) {
XDPRINTF((5,0,
@@ -1495,26 +1530,10 @@ static int set_ncp22_25_maximum_space(int volume, char *unixname,
return(-0x8c);
max_space = GET_BE32(f->u.d.max_space);
/* Treat the common unlimited sentinels as "remove restriction". */
if (max_space == MAX_U32 || max_space == 0x40000000UL)
quota = 0;
else
quota = max_space;
result = nw_set_vol_restrictions((uint8)volume, act_uid, quota);
if (result) {
/* If quota support is not compiled/enabled, do not break old clients. */
XDPRINTF((5,0,
"NCP22/25 maximum space quota ignored: vol=%d uid=%d max=0x%08lx rc=%d",
volume, act_uid, (unsigned long)max_space, result));
if (result == -0xfb)
return(0);
} else {
XDPRINTF((5,0,
"NCP22/25 maximum space quota set: vol=%d uid=%d max=0x%08lx quota=0x%08lx",
volume, act_uid, (unsigned long)max_space, (unsigned long)quota));
}
result = set_ncp22_25_dir_quota_metadata(unixname, max_space);
XDPRINTF((5,0,
"NCP22/25 directory quota metadata set: vol=%d path=%s max=0x%08lx rc=%d",
volume, unixname ? unixname : "", (unsigned long)max_space, result));
return(result);
}

View File

@@ -8,6 +8,29 @@ target_link_libraries(nwfs_xattr_dump PRIVATE mars_nwe::nwfs ${XATTR_LIBRARIES})
add_executable(nwfs_xattr_edit nwfs_xattr_edit.c)
target_link_libraries(nwfs_xattr_edit PRIVATE mars_nwe::nwfs ${XATTR_LIBRARIES})
find_path(NCPFS_INCLUDE_DIR
NAMES ncp/nwcalls.h
)
find_library(NCPFS_LIBRARY
NAMES ncp
)
set(NWFS_NCPFS_DIRQUOTA_HELPER "")
set(NWFS_NCPFS_USERQUOTA_HELPER "")
if(NCPFS_INCLUDE_DIR AND NCPFS_LIBRARY)
add_executable(nwfs_ncpfs_dirquota nwfs_ncpfs_dirquota.c)
target_include_directories(nwfs_ncpfs_dirquota PRIVATE ${NCPFS_INCLUDE_DIR})
target_link_libraries(nwfs_ncpfs_dirquota PRIVATE ${NCPFS_LIBRARY})
set(NWFS_NCPFS_DIRQUOTA_HELPER "${CMAKE_CURRENT_BINARY_DIR}/nwfs_ncpfs_dirquota")
add_executable(nwfs_ncpfs_userquota nwfs_ncpfs_userquota.c)
target_include_directories(nwfs_ncpfs_userquota PRIVATE ${NCPFS_INCLUDE_DIR})
target_link_libraries(nwfs_ncpfs_userquota PRIVATE ${NCPFS_LIBRARY})
set(NWFS_NCPFS_USERQUOTA_HELPER "${CMAKE_CURRENT_BINARY_DIR}/nwfs_ncpfs_userquota")
else()
message(STATUS "ncpfs/libncp not found; manual nwfs NCPFS quota smoke helpers disabled")
endif()
configure_file(nwfs_metadata_xattr_file_test.sh.in nwfs_metadata_xattr_file_test.sh @ONLY)
add_test(NAME nwfs_metadata_xattr_file_test COMMAND sh ${CMAKE_CURRENT_BINARY_DIR}/nwfs_metadata_xattr_file_test.sh)

View File

@@ -0,0 +1,125 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Manual NCPFS smoke helper for NetWare directory space limits.
*
* This is intentionally small and is based on the ncpfs contrib/testing
* dirlimit.c example by Petr Vandrovec. It mutates a directory through
* libncp/NCPFS so the host-side test can verify the mars-nwe
* netware.metadata directory-quota xattr afterwards.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ncp/nwcalls.h>
#include <ncp/nwnet.h>
static void usage(const char *prog)
{
fprintf(stderr,
"Usage: %s [-l LIMIT_4K] [-p VOLUME:PATH] NCPFS_MOUNTED_PATH\n"
"\n"
"Sets the NetWare DOS-info MaximumSpace field through libncp.\n"
"LIMIT_4K is in 4 KiB NetWare blocks, matching ncpfs dirlimit.c.\n",
prog);
}
int main(int argc, char **argv)
{
NWDSCCODE err;
NWCONN_HANDLE conn = NULL;
char volume[1000];
char volpath[1000];
char remote[2000];
unsigned char nwpath[1000];
const char *override_path = NULL;
const char *mounted_path;
unsigned long limit = 0;
int set_limit = 0;
int opt;
int len;
if (NWCallsInit(NULL, NULL)) {
fprintf(stderr, "NWCallsInit failed\n");
return 2;
}
while ((opt = getopt(argc, argv, "h?p:l:")) != -1) {
switch (opt) {
case 'l':
errno = 0;
limit = strtoul(optarg, NULL, 0);
if (errno || limit > 0xffffffffUL) {
fprintf(stderr, "invalid limit: %s\n", optarg);
return 2;
}
set_limit = 1;
break;
case 'p':
override_path = optarg;
break;
case 'h':
case '?':
usage(argv[0]);
return opt == 'h' ? 0 : 2;
default:
usage(argv[0]);
return 2;
}
}
if (!set_limit || optind + 1 != argc) {
usage(argv[0]);
return 2;
}
mounted_path = argv[optind];
err = NWParsePath(mounted_path, NULL, &conn, volume, volpath);
if (err) {
fprintf(stderr, "NWParsePath failed for %s: %s\n", mounted_path, strnwerror(err));
return 1;
}
if (!conn) {
fprintf(stderr, "path is not on an NCPFS mount: %s\n", mounted_path);
return 1;
}
if (override_path) {
len = ncp_path_to_NW_format(override_path, nwpath, sizeof(nwpath));
} else {
if ((size_t)snprintf(remote, sizeof(remote), "%s:%s", volume, volpath) >= sizeof(remote)) {
fprintf(stderr, "remote path too long\n");
ncp_close(conn);
return 2;
}
len = ncp_path_to_NW_format(remote, nwpath, sizeof(nwpath));
}
if (len < 0) {
fprintf(stderr, "cannot convert remote path: %s\n", strerror(-len));
ncp_close(conn);
return 1;
}
{
struct ncp_dos_info info;
memset(&info, 0, sizeof(info));
info.MaximumSpace = limit;
err = ncp_ns_modify_entry_dos_info(conn, NW_NS_DOS, SA_ALL,
NCP_DIRSTYLE_NOHANDLE,
0, 0, nwpath, len,
DM_MAXIMUM_SPACE, &info);
}
if (err) {
fprintf(stderr, "cannot set directory quota on %s: %s\n", mounted_path, strnwerror(err));
ncp_close(conn);
return 1;
}
printf("directory quota set path=%s limit4k=%lu\n", mounted_path, limit);
ncp_close(conn);
return 0;
}

View File

@@ -9,6 +9,8 @@ if [ "$#" -lt 4 ]; then
echo "example: $0 localhost supervisor secret /var/local/mars_nwe/SYS /mnt/nw-sys SYSTEM/NWFSTEST SYS" >&2
echo "TESTDIR defaults to NWFSTEST. If the volume root is not creatable through NCP, pass an existing writable directory below SYS." >&2
echo "Set NWFS_NCPFS_TRUSTEE_OBJECT and NWFS_NCPFS_TRUSTEE_TYPE to override the default GUEST user trustee mutation." >&2
echo "Set NWFS_NCPFS_DIR_QUOTA_4K to override the default directory quota limit; set it empty to skip." >&2
echo "Set NWFS_NCPFS_USER_QUOTA_4K to enable the user-volume restriction smoke. The server must run with NWFS_QUOTA_BACKEND=METADATAONLY for netware.userquota." >&2
exit 2
fi
@@ -23,6 +25,12 @@ TRUSTEE_OBJECT=${NWFS_NCPFS_TRUSTEE_OBJECT:-GUEST}
TRUSTEE_TYPE=${NWFS_NCPFS_TRUSTEE_TYPE:-1}
TRUSTEE_RIGHTS=${NWFS_NCPFS_TRUSTEE_RIGHTS:-RF}
DUMP="@CMAKE_CURRENT_BINARY_DIR@/nwfs_xattr_dump"
DIRQUOTA_HELPER="@NWFS_NCPFS_DIRQUOTA_HELPER@"
USERQUOTA_HELPER="@NWFS_NCPFS_USERQUOTA_HELPER@"
DIR_QUOTA_4K=${NWFS_NCPFS_DIR_QUOTA_4K-321}
USER_QUOTA_4K=${NWFS_NCPFS_USER_QUOTA_4K-}
USER_QUOTA_OBJECT=${NWFS_NCPFS_USER_QUOTA_OBJECT:-$TRUSTEE_OBJECT}
USER_QUOTA_TYPE=${NWFS_NCPFS_USER_QUOTA_TYPE:-$TRUSTEE_TYPE}
MKDIR_ERR=$(mktemp "${TMPDIR:-/tmp}/nwfs_ncpfs_mkdir.XXXXXX")
WRITE_ERR=$(mktemp "${TMPDIR:-/tmp}/nwfs_ncpfs_write.XXXXXX")
GRANT_ERR=$(mktemp "${TMPDIR:-/tmp}/nwfs_ncpfs_grant.XXXXXX")
@@ -129,3 +137,41 @@ if command -v nwrevoke >/dev/null 2>&1; then
else
echo "nwrevoke not found; skipping trustee-remove mutation" >&2
fi
if [ -n "$DIR_QUOTA_4K" ]; then
if [ -n "$DIRQUOTA_HELPER" ] && [ -x "$DIRQUOTA_HELPER" ]; then
echo "setting directory quota ${DIR_QUOTA_4K}x4K on $NCP_TEST_PATH" >&2
"$DIRQUOTA_HELPER" -l "$DIR_QUOTA_4K" "$NCP_TEST_PATH"
sync
printf '\n# after directory quota set\n'
"$DUMP" "$HOST_TEST_PATH"
else
echo "nwfs_ncpfs_dirquota helper not built; skipping directory quota mutation" >&2
fi
else
echo "NWFS_NCPFS_DIR_QUOTA_4K is empty; skipping directory quota mutation" >&2
fi
if [ -n "$USER_QUOTA_4K" ]; then
if [ -n "$USERQUOTA_HELPER" ] && [ -x "$USERQUOTA_HELPER" ]; then
echo "setting user quota ${USER_QUOTA_4K}x4K for $USER_QUOTA_OBJECT type $USER_QUOTA_TYPE on $VOLUME" >&2
"$USERQUOTA_HELPER" -S "$SERVER" -U "$USER" -P "$PASSWORD" \
--volume "$VOLUME" --object "$USER_QUOTA_OBJECT" --type "$USER_QUOTA_TYPE" \
--limit-4k "$USER_QUOTA_4K"
sync
printf '\n# after user quota set\n'
"$DUMP" "$SYSROOT"
echo "removing user quota for $USER_QUOTA_OBJECT type $USER_QUOTA_TYPE on $VOLUME" >&2
"$USERQUOTA_HELPER" -S "$SERVER" -U "$USER" -P "$PASSWORD" \
--volume "$VOLUME" --object "$USER_QUOTA_OBJECT" --type "$USER_QUOTA_TYPE" \
--remove
sync
printf '\n# after user quota remove\n'
"$DUMP" "$SYSROOT"
else
echo "nwfs_ncpfs_userquota helper not built; skipping user quota mutation" >&2
fi
else
echo "NWFS_NCPFS_USER_QUOTA_4K not set; skipping user quota mutation" >&2
fi

View File

@@ -0,0 +1,164 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Manual NCP/libncp smoke helper for NetWare volume/user restrictions.
*
* It uses the same public ncpfs/NWCalls APIs as ncpfs contrib/testing/pp/volres.c
* so tests mutate the server through NCP and verify netware.userquota on the
* host SYS root afterwards.
*/
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncp/nwcalls.h>
#include <ncp/nwnet.h>
#include <ncp/ncplib.h>
static void usage(const char *prog)
{
fprintf(stderr,
"Usage: %s [ncpfs options] --volume VOL --object NAME --type TYPE (--limit-4k LIMIT | --remove)\n"
"\n"
"ncpfs options are parsed by ncp_initialize(), for example:\n"
" -S MARS -U SUPERVISOR -P secret\n",
prog);
}
static int parse_u32(const char *text, uint32_t *value)
{
char *end = NULL;
unsigned long v;
errno = 0;
v = strtoul(text, &end, 0);
if (errno || !end || *end || v > 0xffffffffUL)
return -1;
*value = (uint32_t)v;
return 0;
}
int main(int argc, char **argv)
{
NWCONN_HANDLE conn;
long init_err = 0;
const char *volume = NULL;
const char *object = NULL;
uint32_t object_type = 1;
uint32_t limit = 0;
int have_limit = 0;
int remove_limit = 0;
int i;
NWCCODE err;
NWVOL_NUM volnum;
nuint32 object_id;
nuint32 restriction = 0;
nuint32 in_use = 0;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
usage(argv[0]);
return 0;
}
}
if (NWCallsInit(NULL, NULL)) {
fprintf(stderr, "NWCallsInit failed\n");
return 2;
}
conn = ncp_initialize(&argc, argv, 1, &init_err);
if (!conn) {
fprintf(stderr, "ncp_initialize/login failed: %ld\n", init_err);
usage(argv[0]);
return 2;
}
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--volume")) {
if (++i >= argc) {
usage(argv[0]);
ncp_close(conn);
return 2;
}
volume = argv[i];
} else if (!strcmp(argv[i], "--object")) {
if (++i >= argc) {
usage(argv[0]);
ncp_close(conn);
return 2;
}
object = argv[i];
} else if (!strcmp(argv[i], "--type")) {
if (++i >= argc || parse_u32(argv[i], &object_type)) {
fprintf(stderr, "invalid --type value\n");
ncp_close(conn);
return 2;
}
} else if (!strcmp(argv[i], "--limit-4k")) {
if (++i >= argc || parse_u32(argv[i], &limit)) {
fprintf(stderr, "invalid --limit-4k value\n");
ncp_close(conn);
return 2;
}
have_limit = 1;
} else if (!strcmp(argv[i], "--remove")) {
remove_limit = 1;
} else {
fprintf(stderr, "unknown argument: %s\n", argv[i]);
usage(argv[0]);
ncp_close(conn);
return 2;
}
}
if (!volume || !object || (have_limit == remove_limit)) {
usage(argv[0]);
ncp_close(conn);
return 2;
}
err = NWGetVolumeNumber(conn, volume, &volnum);
if (err) {
fprintf(stderr, "NWGetVolumeNumber(%s) failed: %s\n", volume, strnwerror(err));
ncp_close(conn);
return 1;
}
err = NWGetObjectID(conn, object, (NWObjectType)object_type, &object_id);
if (err) {
fprintf(stderr, "NWGetObjectID(%s,type=%u) failed: %s\n",
object, (unsigned)object_type, strnwerror(err));
ncp_close(conn);
return 1;
}
if (remove_limit)
err = NWRemoveObjectDiskRestrictions(conn, volnum, object_id);
else
err = NWSetObjectVolSpaceLimit(conn, volnum, object_id, limit);
if (err) {
fprintf(stderr, "%s object volume restriction failed: %s\n",
remove_limit ? "remove" : "set", strnwerror(err));
ncp_close(conn);
return 1;
}
err = NWGetObjDiskRestrictions(conn, volnum, object_id, &restriction, &in_use);
if (err) {
fprintf(stderr, "NWGetObjDiskRestrictions failed after mutation: %s\n", strnwerror(err));
ncp_close(conn);
return 1;
}
printf("userquota %s object=%s type=%u id=0x%08x volume=%s volnum=%u restriction4k=%u inuse4k=%u\n",
remove_limit ? "removed" : "set",
object, (unsigned)object_type, (unsigned)object_id,
volume, (unsigned)volnum, (unsigned)restriction, (unsigned)in_use);
ncp_close(conn);
return 0;
}