7.5 KiB
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.
<SYS>/
.recycle/
<user>/
<original tree>/
<deleted file>
.salvage/
<user>/
<original tree>/
<deleted file>.json
Example:
SYS/.recycle/SUPERVISOR/PUBLIC/PMDFLTS.INI
SYS/.salvage/SUPERVISOR/PUBLIC/PMDFLTS.INI.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:
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
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:
- Delete request reaches mars_nwe.
- mars_nwe captures the required metadata.
- mars_nwe moves the file to
.recycle. - mars_nwe writes a JSON metadata record to
.salvage. - 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:
{
"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,
"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. 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
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:
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
0x13deleted-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:
src/nwsalvage.c
include/nwsalvage.h
Planned responsibilities:
- build
.recycleand.salvagepaths 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:
NCP 0x2222 / 22 / 27 Scan Salvageable Files (old)
NCP 0x2222 / 87 / 16 Scan Salvageable Files initial scanner implemented
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. The 87/16 scanner walks the existing
.salvage sidecar files and does not create a second index or trustee store:
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:
- validate the request volume and deleted DOS directory entry,
- resolve the deleted entry through
nwsalvage, - return FinderInfo from the JSON record; the JSON stores a fixed 32-byte FinderInfo block, using all zeroes when mars_nwe has no FinderInfo xattr,
- return ProDOS information as zeroes unless mars_nwe later gains a ProDOS metadata store,
- return resource fork size as zero while resource forks remain unsupported,
- return the deleted filename from the salvage record.
AFP must not scan .recycle or .salvage itself.
Test plan
Create a separate test group first:
tests/salvage/
Initial tests cover the backend without AFP:
- 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 Samba-compatible
Copy #x of NAMEhistory 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 active low-numbered configuration block is:
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
The defaults should remain compatible with a Samba-style recycle layout and
should keep .salvage as metadata-only state.