tests: document salvage recycle behaviour

This commit is contained in:
OpenAI
2026-05-31 14:17:12 +00:00
committed by Mario Fetka
parent 5d574078ff
commit 3466ae05b4
5 changed files with 209 additions and 83 deletions

43
TODO.md
View File

@@ -187,6 +187,33 @@ Follow-up:
the WebSDK TTS calls include begin/end/abort transaction, status, threshold,
and control/statistics operations.
### Salvage / deleted-entry backend
Current status:
- The shared salvage backend lives in `src/nwsalvage.c` /
`include/nwsalvage.h` and is hooked into the normal server delete path.
- Deleted payloads are moved to `.recycle` and per-object JSON sidecars are
written below `.salvage`.
- The sidecar schema preserves NetWare archive/fileinfo xattrs, AFP metadata
when present, inherited rights mask, and explicit trustee entries.
- The runtime configuration uses low `nw.ini` sections `51-59`. Section `53`
maps Samba-style behaviour flags (`k`, `v`, `t`, `m`), and sections
`55-58` cover size filters, exclude, exclude_dir, and noversions lists.
- NCP delete and Samba-compatible history naming are covered by smoke tests in
`tests/salvage/`.
Follow-up:
- Implement the normal NetWare salvage family on top of the shared backend:
- `NCP 0x2222 / 87 / 16` - Scan Salvageable Files
- `NCP 0x2222 / 87 / 17` - Recover Salvageable File
- `NCP 0x2222 / 87 / 18` - Purge Salvageable File
- optional legacy `NCP 0x2222 / 22 / 27` - Scan Salvageable File (old)
- Add endpoint-level tests for scan/recover/purge under `tests/salvage/`.
- Add cleanup coverage for stale `.salvage` JSON when the matching `.recycle`
payload has been manually removed.
### AFP / Mac namespace backend
Current status:
@@ -195,19 +222,15 @@ Current status:
tests under `tests/afp/`. Endpoint inventory, WebSDK audit notes, and AFP
implementation history live in that directory instead of this project-level
TODO file.
- The only AFP endpoint still tracked here is `0x13 AFP Get Macintosh Info On
Deleted Files`. It depends on a real mars_nwe salvage/deleted-entry backend
and must not be implemented as an AFP-local deleted-file scan.
- AFP `0x13 Get Macintosh Info On Deleted Files` remains unsupported until the
NCP salvage scan/recover/purge endpoints expose the shared deleted-entry
backend semantics.
Follow-up:
- Implement or verify the normal NetWare salvage family first:
- `NCP 0x2222 / 87 / 16` - Scan Salvageable Files
- `NCP 0x2222 / 87 / 17` - Recover Salvageable File
- `NCP 0x2222 / 87 / 18` - Purge Salvageable File
- optional legacy `NCP 0x2222 / 22 / 27` - Scan Salvageable File (old)
- After that backend exists, implement AFP `0x13` as an adapter over the
mars_nwe salvage/deleted-entry record.
- After the NetWare salvage endpoints exist, implement AFP `0x13` as an adapter
over the mars_nwe salvage/deleted-entry record.
- AFP must not scan `.recycle` or `.salvage` directly.
- Keep the detailed AFP TODO, inventory, and audit notes in `tests/afp/`.
## Deferred / optional protocol work

29
tests/README.md Normal file
View File

@@ -0,0 +1,29 @@
# mars_nwe test helpers
The test tree contains small smoke helpers for protocol areas that are easier to
exercise outside the normal install flow.
## AFP
`tests/afp` contains the current AFP endpoint smoke suite and AFP-specific
notes. AFP deleted-file endpoint `0x13` is intentionally still unsupported
until the shared salvage backend exposes scan/recover/purge semantics.
## Salvage
`tests/salvage` contains the shared NetWare salvage coverage. These tests are
kept outside `tests/afp` because AFP `0x13` must become a thin adapter over the
same backend that serves the NetWare NCP salvage calls.
The current salvage tests cover:
- local layout contract for `.recycle` payloads and `.salvage` JSON metadata,
- NCP create/delete capture through the normal server delete path,
- Samba-compatible history naming with `Copy #1 of NAME`,
- report-file generation with `--out FILE`.
The NCP smoke scripts are intended to run as the same Unix user that normally
runs the test client, not necessarily as root. Pre-clean of old `.recycle` or
`.salvage` artifacts is therefore best-effort: permission failures are reported
as warnings and do not by themselves fail the smoke. The actual pass/fail check
is based on artifacts created by the NCP delete path.

View File

@@ -1,103 +1,134 @@
# Salvage smoke tests
This directory is reserved for NetWare salvage/deleted-entry coverage. The
backend is intentionally shared by NCP salvage calls and the later AFP `0x13`
This directory contains the shared mars_nwe NetWare salvage/deleted-entry tests.
The backend is shared by NetWare NCP salvage calls and the later AFP `0x13`
`Get Macintosh Info On Deleted Files` adapter; AFP must not grow a parallel
local deleted-file scanner.
The first executable coverage is layout-only. It records the repository
contract before `src/nwsalvage.c` exists:
## Configuration covered by the tests
- deleted file payloads live below the recycle repository,
- per-object JSON metadata lives below the salvage repository,
- JSON metadata preserves mars_nwe xattr-backed NetWare/AFP fields
from `nwarchive` and `nwatalk`,
- there is no large per-directory index file,
- stale JSON metadata is detectable when the matching recycle payload is gone.
Default repository names come from the `nwserv.conf`/`nw.ini` template options:
The active salvage configuration block is intentionally in a low `nwserv.conf` /
`nw.ini` range:
```text
51 1
52 .recycle .salvage
53 kv
54 0700 0700
55 0 0
56 -
57 -
58 -
59 -
```
Section `53` is a compact behaviour-flag string inspired by Samba
`vfs_recycle` options. Flags can be combined in one token: `k` = keeptree,
`v` = versions, `t` = touch, and `m` = touch_mtime. Section `55`
contains min/max file sizes and accepts raw bytes or case-insensitive `kb`,
`mb`, and `gb` suffixes. Sections `56`, `57`, and `58` mirror Samba's
`exclude`, `exclude_dir`, and `noversions` pattern lists.
Section meanings:
Expected layout for a deleted `SYS:PUBLIC/PMDFLTS.INI` owned by `SUPERVISOR`.
The sidecar JSON must carry the server-only metadata needed for exact recover,
including NetWare archive/fileinfo xattrs and AFP FinderInfo/entry-id/attribute
xattrs:
- `51` enables/disables salvage.
- `52` names the recycle payload repository and the `.salvage` metadata
repository.
- `53` is a compact Samba-`vfs_recycle`-style behaviour flag string:
- `k` = keeptree,
- `v` = versions,
- `t` = touch,
- `m` = touch_mtime.
- `54` reserves directory/subdirectory modes for generated repositories.
- `55` is `minsize maxsize`. Values may be raw bytes or case-insensitive
`kb`, `mb`, or `gb` units, for example `55 1kb 100MB`.
- `56` is the `exclude` pattern list. Matching files are deleted directly.
- `57` is the `exclude_dir` pattern list. Matching directories bypass salvage.
- `58` is the `noversions` pattern list. Matching files are recycled, but old
recycle names are replaced instead of getting `Copy #x of NAME` history.
- `59` is reserved for cleanup/history policy.
## Repository layout
With `k`/keeptree enabled, a deleted `SYS:PUBLIC/PMDFLTS.INI` owned by
`SUPERVISOR` is expected to produce:
```text
SYS/.recycle/SUPERVISOR/PUBLIC/PMDFLTS.INI
SYS/.salvage/SUPERVISOR/PUBLIC/PMDFLTS.INI.json
```
Future runtime tests should add direct coverage for:
The `.recycle` tree contains deleted payload files. The `.salvage` tree
contains one JSON sidecar per deleted object. There is no large per-directory
index file.
- `NCP 0x2222 / 22 / 27` Scan Salvageable Files (old),
- `NCP 0x2222 / 87 / 16` Scan Salvageable Files,
- `NCP 0x2222 / 87 / 17` Recover Salvageable File,
- `NCP 0x2222 / 87 / 18` Purge Salvageable File,
- AFP `0x13` as a thin adapter over the shared salvage backend.
The sidecar JSON must preserve mars_nwe server metadata needed for exact
recover, including:
Trustee metadata
----------------
- NetWare archive/fileinfo xattrs from `nwarchive`,
- AFP metadata from `nwatalk` when present,
- inherited rights mask and explicit trustee object/right pairs,
- selected recycle/salvage relative paths.
NetWare trustee rights are stored by mars_nwe in the trustee store, keyed by
volume/dev/inode, rather than in the file payload itself. Salvage metadata
therefore needs to preserve the inherited rights mask and the explicit trustee
object/right pairs so recovery can recreate equivalent trustee records for the
restored object.
`finder_info_hex` should only be present when FinderInfo metadata actually
exists. Missing FinderInfo must not be represented as synthetic all-zero data.
## NCP delete capture smoke
## Samba-compatible version naming
`salvage_ncp_delete_smoke.sh` is the integration check for the first real
server-side salvage hook. It uses `ncp_delete_smoke`, a small libncp client, to
create and delete a file through the classic NetWare NCP file functions:
- `0x43` create file
- `0x42` close file
- `0x44` delete file
The script does not call local `rm`/`unlink` for the tested live path. Local
filesystem access is used only after the NCP delete to inspect the expected
recycle payload and JSON sidecar:
With the `v` flag enabled, mars_nwe follows Samba `vfs_recycle` naming on
collision:
```text
SYS/.recycle/SUPERVISOR/PUBLIC/SLVGCHK.TXT
SYS/.salvage/SUPERVISOR/PUBLIC/SLVGCHK.TXT.json
SYS/.recycle/SUPERVISOR/PUBLIC/Copy #1 of SLVGCHK.TXT
SYS/.recycle/SUPERVISOR/PUBLIC/Copy #2 of SLVGCHK.TXT
```
The `.salvage` sidecar uses the same selected payload name plus `.json`:
```text
SYS/.salvage/SUPERVISOR/PUBLIC/SLVGCHK.TXT.json
SYS/.salvage/SUPERVISOR/PUBLIC/Copy #1 of SLVGCHK.TXT.json
```
If `v` is not enabled, or a file matches the `noversions` list, the old recycle
payload/metadata is replaced instead of creating a `Copy #x of ...` history
entry.
## Layout smoke
`salvage_layout_smoke.sh` is a local filesystem-only contract test. It does not
require a running mars_nwe server and does not use NCP.
```sh
./tests/salvage/salvage_layout_smoke.sh
```
It verifies the basic `.recycle`/`.salvage` directory contract and stale JSON
detection.
## NCP delete capture smoke
`salvage_ncp_delete_smoke.sh` is the integration check for the server-side
delete hook. It uses `ncp_delete_smoke`, a small libncp client, to create and
delete a file through classic NetWare NCP file functions. The script does not
call local `rm`/`unlink` for the tested live path; local filesystem access is
used only after the NCP delete to inspect the expected recycle payload and JSON
sidecar.
Example:
```sh
./tests/salvage/salvage_ncp_delete_smoke.sh \
-S MARS -U SUPERVISOR -P secret \
--path SYS:PUBLIC/SLVGCHK.TXT \
--unix-path /var/mars_nwe/SYS/public/SLVGCHK.TXT
--unix-path /var/mars_nwe/SYS/public/SLVGCHK.TXT \
--volume-root /var/mars_nwe/SYS \
--out /tmp/mars-salvage-report.txt
```
This check lives under `tests/salvage` rather than `tests/afp` because later
salvage scan/recover/purge and versioning tests should share the same fixture.
AFP can use the same backend later as an adapter for `0x13`.
The script can run as a normal user. Best-effort pre-clean of stale test
artifacts may warn when existing `.recycle`/`.salvage` files are owned by the
server user or root; those warnings do not fail the smoke by themselves.
## NCP history/version smoke
`salvage_ncp_history_smoke.sh` deletes the same NetWare path twice through
NCP and verifies Samba-compatible version naming. With the `v` flag enabled,
the first recycled payload keeps its original name and the second is written as
`salvage_ncp_history_smoke.sh` deletes the same NetWare path twice through NCP
and verifies Samba-compatible version naming. With the `v` flag enabled, the
first recycled payload keeps its original name and the second is written as
`Copy #1 of NAME` in the same recycle directory. The `.salvage` sidecar uses
the same selected payload name with a `.json` suffix.
@@ -111,3 +142,17 @@ Example:
--volume-root /var/mars_nwe/SYS \
--out /tmp/mars-salvage-history-report.txt
```
Like the delete smoke, stale artifact cleanup is best-effort and warning-only.
This matters when the script is run as a normal user while `.recycle` and
`.salvage` entries are owned by the mars_nwe server account.
## Still to implement
Runtime endpoint tests should be added as the NCP salvage calls are implemented:
- `NCP 0x2222 / 22 / 27` Scan Salvageable Files (old),
- `NCP 0x2222 / 87 / 16` Scan Salvageable Files,
- `NCP 0x2222 / 87 / 17` Recover Salvageable File,
- `NCP 0x2222 / 87 / 18` Purge Salvageable File,
- AFP `0x13` as a thin adapter over the shared salvage backend.

View File

@@ -33,14 +33,21 @@ SYS/.recycle/SUPERVISOR/PUBLIC/PMDFLTS.INI
SYS/.salvage/SUPERVISOR/PUBLIC/PMDFLTS.INI.json
```
When a name collides, mars_nwe should version the recycle name in a
Samba-like way and use the same versioned name for the sidecar JSON:
When a name collides and the `versions` behaviour is enabled, mars_nwe uses
Samba `vfs_recycle` compatible names and the same selected name for the sidecar
JSON:
```text
SYS/.recycle/SUPERVISOR/PUBLIC/PMDFLTS.~1~.INI
SYS/.salvage/SUPERVISOR/PUBLIC/PMDFLTS.~1~.INI.json
SYS/.recycle/SUPERVISOR/PUBLIC/PMDFLTS.INI
SYS/.recycle/SUPERVISOR/PUBLIC/Copy #1 of PMDFLTS.INI
SYS/.salvage/SUPERVISOR/PUBLIC/PMDFLTS.INI.json
SYS/.salvage/SUPERVISOR/PUBLIC/Copy #1 of PMDFLTS.INI.json
```
If versioning is disabled, or a file matches the `noversions` list, the previous
recycled payload/metadata is replaced instead of creating a `Copy #x of ...`
entry.
The `.recycle` tree contains the actual deleted file. The `.salvage` tree
contains only mars_nwe metadata and indexes. This keeps the recycle area usable
for administrators and compatible with Samba-style recycle setups, while
@@ -94,7 +101,6 @@ Minimal example:
"ctime": 1700000000,
"backup_time": 1700000000,
"finder_info_hex": "0000000000000000000000000000000000000000000000000000000000000000",
"afp_entry_id": "0x2cc1243d",
"resource_fork_size": 0
}
@@ -102,6 +108,9 @@ Minimal example:
The JSON should store paths relative to the volume root wherever possible. That
makes the records easier to move with the volume and easier to read manually.
Optional AFP/Finder fields should only be present when the corresponding
mars_nwe-xattr metadata exists; missing FinderInfo must not be serialized as
synthetic all-zero data.
## Cleanup of stale metadata
@@ -196,27 +205,38 @@ Create a separate test group first:
tests/salvage/
```
Initial tests should cover the backend without AFP:
Initial tests cover the backend without AFP:
- create sample file,
- delete through the salvage helper path,
- local layout contract,
- create/delete through the normal NCP server path,
- verify the file is moved to `.recycle`,
- verify the JSON exists in `.salvage`,
- verify cleanup removes stale JSON when the recycle file is deleted,
- verify recover restores the file,
- verify purge removes both file and JSON.
- verify Samba-compatible `Copy #x of NAME` history naming.
Follow-up tests should cover:
- cleanup removes stale JSON when the recycle file is deleted,
- recover restores the file,
- purge removes both file and JSON,
- scan endpoints enumerate the JSON records in NetWare-compatible order.
Only after those tests are stable should `tests/afp` gain an AFP `0x13` smoke.
## Configuration direction
The first implementation may use fixed per-volume defaults:
The active low-numbered configuration block is:
```text
recycle repository: .recycle
salvage metadata: .salvage
51 1 # enable salvage
52 .recycle .salvage # payload repository, metadata repository
53 kv # k=keeptree, v=versions, t=touch, m=touch_mtime
54 0700 0700 # repository/subdirectory modes
55 0 0 # minsize maxsize; bytes or kb/mb/gb suffixes
56 - # exclude patterns
57 - # exclude_dir patterns
58 - # noversions patterns
59 - # cleanup/history policy, reserved
```
Later configuration can expose those names if needed. The default should remain
compatible with a Samba-style recycle layout and should keep `.salvage` as
metadata-only state.
The defaults should remain compatible with a Samba-style recycle layout and
should keep `.salvage` as metadata-only state.

View File

@@ -166,7 +166,16 @@ emit "first_metadata=$FIRST_META"
emit "second_metadata=$SECOND_META"
section "pre-clean test history artifacts"
rm -f -- "$FIRST_META" "$SECOND_META" "$FIRST_RECYCLE" "$SECOND_RECYCLE" 2>&1 | tee -a "$REPORT_TMP"
for artifact in "$FIRST_META" "$SECOND_META" "$FIRST_RECYCLE" "$SECOND_RECYCLE"; do
if [ -e "$artifact" ]; then
emit "rm '$artifact'"
if rm -f -- "$artifact" 2>>"$REPORT_TMP"; then
:
else
emit "warning: could not remove stale test artifact: $artifact"
fi
fi
done
run_delete "NCP create/delete #1"
run_delete "NCP create/delete #2"