diff --git a/AI.md b/AI.md index 5ff004d..64e74a0 100644 --- a/AI.md +++ b/AI.md @@ -71,9 +71,16 @@ use, the current project status that the user pasted into the chat. `0x12`. - Keep `0x57` subfunction dispatch in `handle_func_0x57()` / namespace code, not as a second subfunction switch in `nwconn.c`. +- Versioned backend payload names follow Samba `vfs_recycle` literally: + `Copy #1 of NAME`, `Copy #2 of NAME`, ... . Do not localize this string and + do not run it through gettext; the NCP scan reply still reports the original + deleted filename for every version. - Versioned salvage entries may have different `.recycle`/`.salvage` names but `87/16` returns the original deleted filename for every version. Do not match recover/purge by display name alone. +- Scan must treat `.salvage` JSON as a sidecar for the matching `.recycle` + payload. If an external tool such as Samba or an administrator removes the + payload, `87/16` must not return the stale sidecar and should remove the JSON. - Scan, recover, and purge should share the same scan/sequence/basehandle view so that a sequence returned by scan identifies the exact sidecar used later. - Append salvage endpoint tests to `tests/salvage/salvage_smoke_suite.sh` rather diff --git a/TODO.md b/TODO.md index 08437fd..62738e1 100644 --- a/TODO.md +++ b/TODO.md @@ -247,20 +247,21 @@ Current status: - 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, Samba-compatible history naming, and the initial `87/16` salvage - scan are covered by the combined smoke suite in `tests/salvage/`. +- NCP delete, Samba-compatible history naming, `87/16` scan, `87/17` recover, + and `87/18` purge are covered by the combined smoke suite in `tests/salvage/`. +- The legacy old salvage calls `22/27`, `22/28`, and `22/29` are adapters over + the same shared backend, not a second deleted-entry implementation. +- Samba-compatible history naming is the literal English `Copy #n of NAME` + format used by `vfs_recycle`; it is not localized. - `NCP 0x2222 / 87 / 16` has a backend-backed scanner that reads `.salvage` - JSON entries and returns one salvageable entry per call. + JSON entries and returns one salvageable entry per call. Stale JSON sidecars + whose `.recycle` payload has disappeared are not returned and are removed by + the scan path. Follow-up: -- Continue the normal NetWare salvage family on top of the shared backend: - - `NCP 0x2222 / 87 / 17` - Recover Salvageable File - - `NCP 0x2222 / 87 / 18` - Purge Salvageable File - - optional legacy `NCP 0x2222 / 22 / 27` - Scan Salvageable File (old) -- Append endpoint-level recover/purge checks to `tests/salvage/salvage_smoke_suite.sh`. -- Add cleanup coverage for stale `.salvage` JSON when the matching `.recycle` - payload has been manually removed. +- Add explicit endpoint coverage for stale `.salvage` cleanup after manually or + externally removing the matching `.recycle` payload. ### AFP / Mac namespace backend diff --git a/src/nwsalvage.c b/src/nwsalvage.c index 6c7d95f..f276580 100644 --- a/src/nwsalvage.c +++ b/src/nwsalvage.c @@ -1853,6 +1853,7 @@ static int nwsalvage_scan_metadata_dir(const char *metadata_dir, char metadata_path[NWSALVAGE_PATH_MAX]; char recycle_path[NWSALVAGE_PATH_MAX]; struct nwsalvage_metadata_entry entry; + struct stat st; size_t len; if (de->d_name[0] == '.') @@ -1879,6 +1880,20 @@ static int nwsalvage_scan_metadata_dir(const char *metadata_dir, entry.recycle_relative_path) < 0) continue; + /* + * The .salvage JSON is only a sidecar for the real payload below + * .recycle. Samba, an administrator, or filesystem cleanup may remove + * the payload behind our back. Do not return such stale metadata from + * NCP 87/16 scans; remove the orphaned sidecar and keep scanning. + */ + if (stat(recycle_path, &st) < 0) { + if (errno == ENOENT) + (void)unlink(metadata_path); + continue; + } + if (!S_ISREG(st.st_mode)) + continue; + memset(result, 0, sizeof(*result)); result->scan_sequence = *match_index; result->scan_volume = (unsigned long)atoi(volume_name); /* overwritten below */ diff --git a/tests/README.md b/tests/README.md index dda164f..c6d03a0 100644 --- a/tests/README.md +++ b/tests/README.md @@ -19,15 +19,19 @@ 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`, -- NCP `87/16` Scan Salvageable Files through the official `ncpfs` helper API, +- Samba-compatible, non-localized history naming with `Copy #1 of NAME`, +- NCP `87/16` scan, `87/17` recover, and `87/18` purge through the official + `ncpfs` helper API, +- stale `.salvage` sidecars are ignored and cleaned when their `.recycle` + payload is externally removed, - report-file generation with `--out FILE`. `tests/salvage/salvage_smoke_suite.sh` is the single NCP integration entry -point. It creates and deletes the same file twice, then appends the `87/16` scan check, -so one report covers delete capture, history/versioning, and scan visibility. -The scan check expects duplicate sequence entries for the original deleted name; -the `Copy #1 of ...` history path is validated through the JSON sidecars. +point. It creates multiple versions of the same file through NCP, deletes them +through NCP, scans salvageable entries, recovers the oldest sequence, and checks +the restored payload through NCP read. The scan check expects duplicate +sequence entries for the original deleted name; backend history paths stay +hidden behind the salvage endpoints. The NCP smoke suite is intended to run as the same Unix user that normally runs the test client, not necessarily as root. Pre-clean of old `.recycle` or diff --git a/tests/salvage/README.md b/tests/salvage/README.md index 48938b9..07e2166 100644 --- a/tests/salvage/README.md +++ b/tests/salvage/README.md @@ -87,7 +87,8 @@ 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. +entry. The `Copy #n of ...` prefix is intentionally not localized; it follows +Samba `vfs_recycle` literally and should be stable across server locales. ## Layout smoke @@ -99,7 +100,9 @@ require a running mars_nwe server and does not use NCP. ``` It verifies the basic `.recycle`/`.salvage` directory contract and stale JSON -detection. +detection. Runtime scans also treat `.salvage` JSON as a sidecar for the +matching `.recycle` payload: if the payload is removed externally, the entry +must disappear from `87/16` scan results and the stale JSON should be cleaned. ## NCP path visibility