tests: add AFP metadata rights negative smoke
All checks were successful
Source release / source-package (push) Successful in 52s
All checks were successful
Source release / source-package (push) Successful in 52s
Extend the AFP Set File Information smoke helper with an explicit expected-completion mode so negative WebSDK/NWAFP probes can assert the server completion byte instead of treating any non-zero completion as a helper failure. Use that support from afp_smoke_suite.sh to add optional Modify-rights negative coverage for AFP metadata writes. When a readonly test user such as NOPASSUSER is supplied, the suite now verifies that FinderInfo and AFP-only Invisible/System xattr writes are rejected with completion 0x8c, matching the mars_nwe trustee/effective-rights Modify policy gate added to afp_set_file_information(). The optional --prepare-readonly-rights mode deliberately reuses the standard ncpfs trustee utilities rather than adding ad-hoc test NCPs: nwrevoke removes an explicit assignment for the readonly user on the smoke target, nwgrant grants only read/file-scan rights, and nwrevoke restores the object to inherited rights after the negative probes. A cleanup trap also revokes the temporary assignment if the suite exits early. This keeps the AFP smoke harness aligned with the convergence plan: AFP remains an Apple-facing adapter over existing mars_nwe NetWare trustee semantics, while test setup uses existing NetWare administration utilities. Tests: bash -n tests/linux/afp_smoke_suite.sh; gcc -Iinclude -I/mnt/data/stubs -fsyntax-only tests/linux/afp_set_file_info_smoke.c; git diff --check
This commit is contained in:
7
TODO.md
7
TODO.md
@@ -440,6 +440,13 @@ AFP Set File Information metadata-rights convergence:
|
||||
and Modify timestamp continues to route through `nw_utime_node()`. The new
|
||||
check only covers metadata that has to remain AFP-specific, keeping AFP as an
|
||||
Apple-facing adapter over mars_nwe policy rather than a parallel file server.
|
||||
- The Linux AFP smoke suite now has optional negative coverage for this policy
|
||||
gate. With `--readonly-user NOPASSUSER --readonly-no-password`, the suite can
|
||||
run FinderInfo, Invisible, and System writes as a user that is expected to lack
|
||||
Modify rights and assert completion `0x8c`. With `--prepare-readonly-rights`,
|
||||
the suite uses the existing ncpfs `nwgrant`/`nwrevoke` trustee utilities to
|
||||
grant only read/file-scan rights (`[RF]` by default) before the negative probe
|
||||
and revoke the explicit trustee assignment afterwards.
|
||||
|
||||
Endpoint order:
|
||||
|
||||
|
||||
@@ -87,6 +87,28 @@ collected separately. Use `--stop-on-failure` for strict bisect-style runs; by
|
||||
default the script keeps going so one failing endpoint does not hide later AFP
|
||||
output from the report.
|
||||
|
||||
The suite can optionally exercise the Modify-rights negative path with a second
|
||||
user. For a no-password test user such as `NOPASSUSER`, run from the build
|
||||
`tests/linux` directory:
|
||||
|
||||
```sh
|
||||
./afp_smoke_suite.sh \
|
||||
-S MARS -U SUPERVISOR -P secret \
|
||||
--path SYS:PUBLIC/pmdflts.ini \
|
||||
--unix-path /var/mars_nwe/SYS/public/pmdflts.ini \
|
||||
--readonly-user NOPASSUSER --readonly-no-password \
|
||||
--prepare-readonly-rights \
|
||||
--out /tmp/mars-afp-smoke.txt
|
||||
```
|
||||
|
||||
`--prepare-readonly-rights` uses the standard ncpfs trustee utilities instead
|
||||
of ad-hoc test NCPs: it calls `nwrevoke` to remove any explicit assignment for
|
||||
the readonly user on the smoke file, then `nwgrant -r '[RF]'` to grant read and
|
||||
file-scan rights without Modify. After the negative probes it runs `nwrevoke`
|
||||
again so the file returns to inherited rights. Use this only on smoke files or
|
||||
paths where removing an explicit trustee assignment for the readonly test user
|
||||
is acceptable.
|
||||
|
||||
AFP metadata writes and NetWare Modify rights:
|
||||
|
||||
FinderInfo and AFP-only attribute metadata are stored in `org.mars-nwe.afp.*`
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [--afp09|--afp20] [--allow-invalid-namespace] [--allow-invalid-path] "
|
||||
"Usage: %s [--afp09|--afp20] [--expect-completion CODE] [--allow-invalid-namespace] [--allow-invalid-path] "
|
||||
"[--volume N] [--entry-id ID] [--type FOUR] [--creator FOUR] "
|
||||
"[--invisible|--clear-invisible|--system|--clear-system|--archive|--clear-archive] "
|
||||
"[--mtime-epoch SECONDS] [--finder-info-only|--attributes-only|--timestamp-only] "
|
||||
@@ -52,8 +52,9 @@ static void usage(const char *prog)
|
||||
" %s -S MARS -U SUPERVISOR -P secret --invisible --attributes-only SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret --archive --attributes-only SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret --mtime-epoch 1700000000 --timestamp-only SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s --allow-invalid-namespace -S MARS SYS:PUBLIC/pmdflts.ini\n",
|
||||
prog, prog, prog, prog, prog, prog);
|
||||
" %s --allow-invalid-namespace -S MARS SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s --expect-completion 0x8c -S MARS -U NOPASSUSER -n --finder-info-only SYS:PUBLIC/pmdflts.ini\n",
|
||||
prog, prog, prog, prog, prog, prog, prog);
|
||||
}
|
||||
|
||||
static int parse_u32(const char *text, uint32_t *value)
|
||||
@@ -160,6 +161,7 @@ int main(int argc, char **argv)
|
||||
const char *path = NULL;
|
||||
int allow_invalid_namespace = 0;
|
||||
int allow_invalid_path = 0;
|
||||
int expect_completion = -1;
|
||||
uint32_t volume_number = 0;
|
||||
uint32_t entry_id = 0;
|
||||
uint8_t finder_info[32];
|
||||
@@ -200,6 +202,14 @@ int main(int argc, char **argv)
|
||||
set_subfunction = AFP_SET_FILE_INFORMATION;
|
||||
} else if (!strcmp(argv[i], "--afp20")) {
|
||||
set_subfunction = AFP20_SET_FILE_INFORMATION;
|
||||
} else if (!strcmp(argv[i], "--expect-completion")) {
|
||||
uint32_t completion;
|
||||
if (++i >= argc || parse_u32(argv[i], &completion) || completion > 255) {
|
||||
fprintf(stderr, "invalid --expect-completion value\n");
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
expect_completion = (int)completion;
|
||||
} else if (!strcmp(argv[i], "--allow-invalid-namespace")) {
|
||||
allow_invalid_namespace = 1;
|
||||
} else if (!strcmp(argv[i], "--allow-invalid-path")) {
|
||||
@@ -347,6 +357,20 @@ int main(int argc, char **argv)
|
||||
request,
|
||||
data_off,
|
||||
NULL);
|
||||
if (expect_completion >= 0) {
|
||||
if ((((unsigned int)err) & 0xff) == (unsigned int)expect_completion) {
|
||||
printf("AFP Set File Information returned expected completion 0x%02x: subfunction=0x%02x path=%s bitmap=0x%04x\n",
|
||||
expect_completion, set_subfunction, path, request_mask);
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr,
|
||||
"AFP Set File Information expected completion 0x%02x but got 0x%02x (%u): subfunction=0x%02x path=%s bitmap=0x%04x\n",
|
||||
expect_completion, (unsigned int)err & 0xff, (unsigned int)err,
|
||||
set_subfunction, path, request_mask);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
if (err == NWE_INVALID_NAMESPACE && allow_invalid_namespace) {
|
||||
printf("AFP Set File Information returned invalid namespace as expected for path=%s\n", path);
|
||||
ncp_close(conn);
|
||||
|
||||
@@ -18,6 +18,11 @@ OUT_FILE=""
|
||||
FINDER_TYPE="TEXT"
|
||||
FINDER_CREATOR="MARS"
|
||||
TIMESTAMP_EPOCH="1700000000"
|
||||
READONLY_USER=""
|
||||
READONLY_PASSWORD=""
|
||||
READONLY_NO_PASSWORD=0
|
||||
PREPARE_READONLY_RIGHTS=0
|
||||
READONLY_RIGHTS="[RF]"
|
||||
KEEP_GOING=1
|
||||
CAPTURE_LOG=1
|
||||
|
||||
@@ -33,6 +38,11 @@ Options:
|
||||
--type FOURCC FinderInfo type written by Set File Info (default: $FINDER_TYPE)
|
||||
--creator FOURCC FinderInfo creator written by Set File Info (default: $FINDER_CREATOR)
|
||||
--mtime-epoch SECONDS AFP modify timestamp to write (default: $TIMESTAMP_EPOCH)
|
||||
--readonly-user USER Optional user expected to lack Modify rights
|
||||
--readonly-password PASS Password for --readonly-user
|
||||
--readonly-no-password Use ncpfs -n for --readonly-user
|
||||
--prepare-readonly-rights Prepare/revoke explicit rights around the negative test
|
||||
--readonly-rights RIGHTS Rights granted by --prepare-readonly-rights (default: $READONLY_RIGHTS)
|
||||
--no-log Do not tail/grep the server log
|
||||
--stop-on-failure Stop after the first failing smoke command
|
||||
-h, --help Show this help
|
||||
@@ -65,6 +75,16 @@ while [ $# -gt 0 ]; do
|
||||
FINDER_CREATOR=$2; shift 2 ;;
|
||||
--mtime-epoch)
|
||||
TIMESTAMP_EPOCH=$2; shift 2 ;;
|
||||
--readonly-user)
|
||||
READONLY_USER=$2; shift 2 ;;
|
||||
--readonly-password)
|
||||
READONLY_PASSWORD=$2; READONLY_NO_PASSWORD=0; shift 2 ;;
|
||||
--readonly-no-password)
|
||||
READONLY_PASSWORD=""; READONLY_NO_PASSWORD=1; shift ;;
|
||||
--prepare-readonly-rights)
|
||||
PREPARE_READONLY_RIGHTS=1; shift ;;
|
||||
--readonly-rights)
|
||||
READONLY_RIGHTS=$2; shift 2 ;;
|
||||
--no-log)
|
||||
CAPTURE_LOG=0; shift ;;
|
||||
--stop-on-failure)
|
||||
@@ -102,8 +122,14 @@ REPORT_TMP=$(mktemp "${TMPDIR:-/tmp}/mars-afp-smoke.XXXXXX")
|
||||
LOG_TMP=$(mktemp "${TMPDIR:-/tmp}/mars-afp-log.XXXXXX")
|
||||
LOG_PID=""
|
||||
FAILURES=0
|
||||
RIGHTS_PREPARED=0
|
||||
|
||||
cleanup() {
|
||||
if [ "$RIGHTS_PREPARED" -eq 1 ] && [ -n "$READONLY_USER" ]; then
|
||||
nwrevoke -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \
|
||||
-o "$READONLY_USER" -t 1 "$NETWARE_PATH" >/dev/null 2>&1 || true
|
||||
RIGHTS_PREPARED=0
|
||||
fi
|
||||
if [ -n "$LOG_PID" ] && kill -0 "$LOG_PID" 2>/dev/null; then
|
||||
kill "$LOG_PID" 2>/dev/null || true
|
||||
wait "$LOG_PID" 2>/dev/null || true
|
||||
@@ -140,6 +166,27 @@ run_cmd() {
|
||||
fi
|
||||
}
|
||||
|
||||
run_optional_cmd() {
|
||||
local label=$1
|
||||
local printable=$2
|
||||
shift 2
|
||||
|
||||
section "$label"
|
||||
emit "\$ $printable"
|
||||
"$@" 2>&1 | tee -a "$REPORT_TMP"
|
||||
local status=${PIPESTATUS[0]}
|
||||
emit "[exit=$status]"
|
||||
return "$status"
|
||||
}
|
||||
|
||||
readonly_print_auth() {
|
||||
if [ "$READONLY_NO_PASSWORD" -eq 1 ]; then
|
||||
printf '%s' "-n"
|
||||
else
|
||||
printf '%s' "-P ******"
|
||||
fi
|
||||
}
|
||||
|
||||
finish_report() {
|
||||
if [ "$CAPTURE_LOG" -eq 1 ]; then
|
||||
sleep 1
|
||||
@@ -175,6 +222,16 @@ emit "log=$LOG_FILE"
|
||||
emit "finder_type=$FINDER_TYPE"
|
||||
emit "finder_creator=$FINDER_CREATOR"
|
||||
emit "mtime_epoch=$TIMESTAMP_EPOCH"
|
||||
if [ -n "$READONLY_USER" ]; then
|
||||
emit "readonly_user=$READONLY_USER"
|
||||
if [ "$READONLY_NO_PASSWORD" -eq 1 ]; then
|
||||
emit "readonly_auth=no-password"
|
||||
else
|
||||
emit "readonly_auth=password"
|
||||
fi
|
||||
emit "prepare_readonly_rights=$PREPARE_READONLY_RIGHTS"
|
||||
emit "readonly_rights=$READONLY_RIGHTS"
|
||||
fi
|
||||
|
||||
for helper in \
|
||||
afp_entry_id_smoke \
|
||||
@@ -205,6 +262,23 @@ fi
|
||||
|
||||
COMMON_PRINT="-S $SERVER -U $USER_NAME -P ******"
|
||||
|
||||
if [ -n "$READONLY_USER" ] && [ "$PREPARE_READONLY_RIGHTS" -eq 1 ]; then
|
||||
# Establish a conservative explicit trustee assignment for the negative
|
||||
# metadata-write probes. The final nwrevoke below removes only this
|
||||
# explicit assignment and returns the object to its inherited rights.
|
||||
run_optional_cmd \
|
||||
"Prepare readonly trustee cleanup" \
|
||||
"nwrevoke -S $SERVER -U $USER_NAME -P ****** -o $READONLY_USER -t 1 '$NETWARE_PATH'" \
|
||||
nwrevoke -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \
|
||||
-o "$READONLY_USER" -t 1 "$NETWARE_PATH" || true
|
||||
run_cmd \
|
||||
"Prepare readonly trustee rights" \
|
||||
"nwgrant -S $SERVER -U $USER_NAME -P ****** -o $READONLY_USER -t 1 -r '$READONLY_RIGHTS' '$NETWARE_PATH'" \
|
||||
nwgrant -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \
|
||||
-o "$READONLY_USER" -t 1 -r "$READONLY_RIGHTS" "$NETWARE_PATH"
|
||||
RIGHTS_PREPARED=1
|
||||
fi
|
||||
|
||||
run_cmd \
|
||||
"AFP Entry ID From Path Name" \
|
||||
"./afp_entry_id_smoke $COMMON_PRINT '$NETWARE_PATH'" \
|
||||
@@ -318,6 +392,43 @@ run_cmd \
|
||||
"$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \
|
||||
--attributes-only --clear-archive "$NETWARE_PATH"
|
||||
|
||||
if [ -n "$READONLY_USER" ]; then
|
||||
READONLY_PRINT="-S $SERVER -U $READONLY_USER $(readonly_print_auth)"
|
||||
if [ "$READONLY_NO_PASSWORD" -eq 1 ]; then
|
||||
READONLY_AUTH_ARGS=(-n)
|
||||
else
|
||||
READONLY_AUTH_ARGS=(-P "$READONLY_PASSWORD")
|
||||
fi
|
||||
|
||||
run_cmd \
|
||||
"AFP metadata Modify rights rejected: FinderInfo" \
|
||||
"./afp_set_file_info_smoke --expect-completion 0x8c $READONLY_PRINT --finder-info-only --type '$FINDER_TYPE' --creator '$FINDER_CREATOR' '$NETWARE_PATH'" \
|
||||
"$SCRIPT_DIR/afp_set_file_info_smoke" --expect-completion 0x8c \
|
||||
-S "$SERVER" -U "$READONLY_USER" "${READONLY_AUTH_ARGS[@]}" \
|
||||
--finder-info-only --type "$FINDER_TYPE" --creator "$FINDER_CREATOR" "$NETWARE_PATH"
|
||||
run_cmd \
|
||||
"AFP metadata Modify rights rejected: Invisible" \
|
||||
"./afp_set_file_info_smoke --expect-completion 0x8c $READONLY_PRINT --attributes-only --invisible '$NETWARE_PATH'" \
|
||||
"$SCRIPT_DIR/afp_set_file_info_smoke" --expect-completion 0x8c \
|
||||
-S "$SERVER" -U "$READONLY_USER" "${READONLY_AUTH_ARGS[@]}" \
|
||||
--attributes-only --invisible "$NETWARE_PATH"
|
||||
run_cmd \
|
||||
"AFP metadata Modify rights rejected: System" \
|
||||
"./afp_set_file_info_smoke --expect-completion 0x8c $READONLY_PRINT --attributes-only --system '$NETWARE_PATH'" \
|
||||
"$SCRIPT_DIR/afp_set_file_info_smoke" --expect-completion 0x8c \
|
||||
-S "$SERVER" -U "$READONLY_USER" "${READONLY_AUTH_ARGS[@]}" \
|
||||
--attributes-only --system "$NETWARE_PATH"
|
||||
fi
|
||||
|
||||
if [ -n "$READONLY_USER" ] && [ "$PREPARE_READONLY_RIGHTS" -eq 1 ]; then
|
||||
run_cmd \
|
||||
"Restore readonly trustee assignment" \
|
||||
"nwrevoke -S $SERVER -U $USER_NAME -P ****** -o $READONLY_USER -t 1 '$NETWARE_PATH'" \
|
||||
nwrevoke -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \
|
||||
-o "$READONLY_USER" -t 1 "$NETWARE_PATH"
|
||||
RIGHTS_PREPARED=0
|
||||
fi
|
||||
|
||||
if command -v stat >/dev/null 2>&1; then
|
||||
run_cmd \
|
||||
"Linux stat: AFP Modify Timestamp" \
|
||||
|
||||
Reference in New Issue
Block a user