docs: design recycle-backed salvage metadata store

This commit is contained in:
OpenAI
2026-05-31 08:57:37 +00:00
committed by Mario Fetka
parent e85f26846e
commit e0ab095c05

View File

@@ -0,0 +1,222 @@
# mars_nwe salvage backend design
This document describes the planned mars_nwe salvage backend for NetWare 3.x
compatibility. The backend should live in `src/nwsalvage.c` with public
interfaces in `include/nwsalvage.h`.
The goal is to implement NetWare-style salvage without relying on filesystem
undelete features. mars_nwe sees the delete while the file still exists, so it
can move the file to a recycle repository and write the metadata required for
NetWare scan/recover/purge calls.
## Directory layout
The file payload and the mars_nwe metadata are deliberately separated.
```text
<SYS>/
.recycle/
<user>/
<original tree>/
<deleted file>
.salvage/
<user>/
<original tree>/
<deleted file>.json
```
Example:
```text
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:
```text
SYS/.recycle/SUPERVISOR/PUBLIC/PMDFLTS.~1~.INI
SYS/.salvage/SUPERVISOR/PUBLIC/PMDFLTS.~1~.INI.json
```
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
allowing mars_nwe to keep NetWare-specific salvage state.
## Why not filesystem undelete
Linux and FreeBSD do not provide a portable, online, filesystem-independent
undelete API that matches NetWare salvage semantics. ext-style recovery tools,
snapshot filesystems, and forensic recovery tools do not provide the stable
per-directory scan/recover/purge model required by the NetWare NCPs.
mars_nwe should therefore implement salvage as controlled server-side
"trash-on-delete" behavior:
1. Delete request reaches mars_nwe.
2. mars_nwe captures the required metadata.
3. mars_nwe moves the file to `.recycle`.
4. mars_nwe writes a JSON metadata record to `.salvage`.
5. NetWare salvage NCPs operate on those metadata records.
## JSON metadata
Use one JSON file per deleted object. Do not use a single large JSON file per
directory; per-object JSON is easier to inspect, easier to clean up, and safer
when one record is damaged.
Minimal example:
```json
{
"version": 1,
"source": "mars_nwe",
"volume": "SYS",
"deleted_by": "SUPERVISOR",
"deleted_at": 1780215322,
"original_path": "SYS:PUBLIC/PMDFLTS.INI",
"original_parent_entry_id": 4,
"original_name": "PMDFLTS.INI",
"dos_name": "PMDFLTS.INI",
"long_name": "pmdflts.ini",
"recycle_relative_path": ".recycle/SUPERVISOR/PUBLIC/PMDFLTS.INI",
"salvage_relative_path": ".salvage/SUPERVISOR/PUBLIC/PMDFLTS.INI.json",
"attributes": 8192,
"size": 8161,
"atime": 1700000000,
"mtime": 1700000000,
"ctime": 1700000000,
"backup_time": 1700000000,
"finder_info_hex": "0000000000000000000000000000000000000000000000000000000000000000",
"afp_entry_id": "0x2cc1243d",
"resource_fork_size": 0
}
```
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.
## Cleanup of stale metadata
Samba or an administrator may delete files from `.recycle`. mars_nwe must not
leave stale salvage entries forever.
The salvage backend should include an opportunistic cleanup pass:
```text
scan .salvage/**/*.json
read recycle_relative_path
if the .recycle file no longer exists:
remove the JSON record
remove empty .salvage directories where safe
```
Cleanup should run at least:
- on server startup,
- before `Scan Salvageable Files`,
- before AFP `0x13` deleted-file metadata lookup,
- optionally on a low-frequency timer later.
The first implementation can keep this simple and synchronous.
## Backend module
Implement the backend in:
```text
src/nwsalvage.c
include/nwsalvage.h
```
Planned responsibilities:
- build `.recycle` and `.salvage` paths for a volume,
- generate versioned recycle names on collision,
- move a deleted file into `.recycle`,
- write/read JSON records,
- remove stale JSON when the recycle file is gone,
- scan records by original parent directory entry,
- recover a record by moving the recycle file back,
- purge a record by deleting both recycle file and JSON metadata.
The AFP code must not implement salvage directly. AFP `0x13` should be only an
adapter on top of `nwsalvage`.
## NetWare 3.x NCP scope
mars_nwe targets NetWare 3.x-style compatibility for this slice. The salvage
backend should initially support the 3.x-era NCPs:
```text
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
```
Later NetWare 4.11+ list variants and UTF-8 extensions are out of scope for the
initial mars_nwe 3.x salvage slice:
```text
NCP 0x2222 / 87 / 41 Scan Salvageable File List
NCP 0x2222 / 87 / 42 Purge Salvageable File List
NCP 0x2222 / 89 / 16 UTF-8 / extended Scan Salvageable Files
```
## AFP integration
AFP `0x13 Get Macintosh Info On Deleted Files` remains unsupported until the
mars_nwe salvage backend exists.
After the backend is available, AFP `0x13` should:
1. validate the request volume and deleted DOS directory entry,
2. resolve the deleted entry through `nwsalvage`,
3. return FinderInfo from the JSON record when available,
4. return ProDOS information as zeroes unless mars_nwe later gains a ProDOS
metadata store,
5. return resource fork size as zero while resource forks remain unsupported,
6. return the deleted filename from the salvage record.
AFP must not scan `.recycle` or `.salvage` itself.
## Test plan
Create a separate test group first:
```text
tests/salvage/
```
Initial tests should cover the backend without AFP:
- create sample file,
- delete through the salvage helper 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.
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:
```text
recycle repository: .recycle
salvage metadata: .salvage
```
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.