diff --git a/tests/README.md b/tests/README.md index c6d03a0..c0c8f13 100644 --- a/tests/README.md +++ b/tests/README.md @@ -5,9 +5,9 @@ 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. +`tests/afp` contains the current AFP endpoint smoke suite and its single AFP +README. AFP deleted-file endpoint `0x13` is implemented as a thin adapter over +the shared salvage backend and is covered by the AFP smoke suite. ## Salvage diff --git a/tests/afp/AFP_DELETED_FILE_INFO.md b/tests/afp/AFP_DELETED_FILE_INFO.md deleted file mode 100644 index 394da83..0000000 --- a/tests/afp/AFP_DELETED_FILE_INFO.md +++ /dev/null @@ -1,57 +0,0 @@ -# AFP deleted-file Macintosh metadata - -`AFP Get Macintosh Info On Deleted Files` is NetWare AFP subfunction `0x13`. -Keep it documented as an unsupported, salvage-backend-dependent endpoint for the -current AFP compatibility slice. - -## Why this is not implemented directly - -The request is not a normal path, name, or file-handle metadata query. It is -keyed by a volume number and a DOS directory entry for a deleted Macintosh -directory entry. The reply shape is also salvage-specific: Finder information, -ProDOS information, resource fork size, and the deleted file name. - -That means the AFP call is an adapter on top of NetWare salvage/deleted-entry -state, not an independent AFP metadata lookup. Implementing it directly in the -AFP handler would create a parallel deleted-file path and would violate the -current rule that AFP endpoints reuse mars_nwe backend semantics. - -## Required mars_nwe backend first - -Before implementing AFP subfunction `0x13`, the normal NetWare salvage backend -must exist and be verified. The relevant non-AFP NCP family is: - -- `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) - -AFP `0x13` translates the WebSDK/NWAFP -wire request to the mars_nwe salvage entry and then append AFP-specific deleted -Macintosh metadata. - -## Intended future mapping - -AFP `0x13` behaves like this: - -1. Validate the request volume and DOS directory entry. -2. Look up the deleted entry through the mars_nwe salvage/deleted-entry backend. -3. Return FinderInfo from the AFP metadata store when available, otherwise zeroes. -4. Return ProDOS information from the Salvage JSON snapshot (`prodos_info_hex`), which is captured from the nwatalk ProDOSInfo xattr backend. -5. Return resource fork size as zero while resource forks remain unsupported. -6. Return the deleted file name from the salvage entry, not from a live path scan. - -## Current audit status - -Current status for the AFP inventory and final audit: - -```text -0x13 AFP Get Macintosh Info On Deleted Files -status: unsupported / final-audit item -reason: requires mars_nwe salvage/deleted-entry backend first -do not: implement a standalone AFP-local deleted-file scan -``` - -This keeps the AFP implementation aligned with the rest of the current design: -NetWare semantics come from mars_nwe core paths, while AFP-only metadata is kept -small and explicit. diff --git a/tests/afp/AFP_ENDPOINT_INVENTORY.md b/tests/afp/AFP_ENDPOINT_INVENTORY.md deleted file mode 100644 index 2f5295b..0000000 --- a/tests/afp/AFP_ENDPOINT_INVENTORY.md +++ /dev/null @@ -1,79 +0,0 @@ -# AFP endpoint inventory before final audit - -This file records the current mars_nwe AFP endpoint inventory before the final -WebSDK / Novell NWAFP header audit. The generated helper output should remain -the source of truth for fresh checks: - -```sh -tests/afp/afp_endpoint_inventory.py -``` - -## Current endpoint coverage - -| Subfn | Name | Status | Backend discipline | -| --- | --- | --- | --- | -| `0x01` | AFP Create Directory | implemented | mars_nwe path/namespace and object lifecycle | -| `0x02` | AFP Create File | implemented | mars_nwe path/namespace and object lifecycle; AFP xattr only for file entry-id metadata | -| `0x03` | AFP Delete | implemented | mars_nwe path/namespace and object lifecycle | -| `0x04` | AFP Get Entry ID From Name | implemented / validating | mars_nwe path/namespace; entry-id-relative bases are directory entry IDs only | -| `0x05` | AFP Get File Information | implemented | mars_nwe path/namespace plus mars_nwe attributes/rights; AFP xattrs only for FinderInfo / entry-id | -| `0x06` | AFP Get Entry ID From NetWare Handle | implemented | mars_nwe file-handle lookup, then AFP entry-id metadata | -| `0x07` | AFP Rename | implemented | mars_nwe path/namespace and object lifecycle; AFP entry-id metadata follows renamed files | -| `0x08` | AFP Open File Fork | implemented for data fork | mars_nwe file handles; resource fork remains unsupported | -| `0x09` | AFP Set File Information | implemented | mars_nwe attributes/archive/fileinfo/trustee; AFP xattrs only for FinderInfo / AFP-only bits | -| `0x0a` | AFP Scan File Information | implemented for path and directory entry-id starts | mars_nwe path/namespace; entry IDs must resolve to directories | -| `0x0b` | AFP Alloc Temporary Dir Handle | implemented | mars_nwe path/namespace and directory handles | -| `0x0c` | AFP Get Entry ID From Path Name | implemented / validating | mars_nwe path/namespace; AFP entry-id metadata for files | -| `0x0d` | AFP 2.0 Create Directory | implemented | mars_nwe path/namespace and object lifecycle | -| `0x0e` | AFP 2.0 Create File | implemented | mars_nwe path/namespace and object lifecycle; AFP xattr only for file entry-id metadata | -| `0x0f` | AFP 2.0 Get File Information | implemented | same backend discipline as `0x05` | -| `0x10` | AFP 2.0 Set File Information | implemented | same backend discipline as `0x09` | -| `0x11` | AFP 2.0 Scan File Information | implemented for path and directory entry-id starts | same backend discipline as `0x0a` | -| `0x12` | AFP Get DOS Name From Entry ID | implemented | mars_nwe path/namespace and AFP entry-id reverse lookup | -| `0x13` | AFP Get Macintosh Info On Deleted Files | implemented / salvage-backed | covered by afp_deleted_info_smoke | - -## Backend rules for the final audit - -AFP handlers must not grow independent filesystem semantics. The final audit -must verify that each endpoint continues to use the mars_nwe core backend: - -- Create, delete, rename, and directory operations go through mars_nwe - path/namespace and object lifecycle helpers. -- Data-fork open/read/write/close uses mars_nwe file handles and the normal - NetWare file I/O path. AFP `0x08` opens the data fork; subsequent I/O and - close use normal NetWare handle semantics. -- Rights checks use mars_nwe trustee/effective-rights logic. -- Hidden, system, archive, timestamps, creator/modifier, and backup/archive - metadata use mars_nwe attribute/archive/fileinfo paths. -- AFP-only xattrs are limited to: - - `user.org.mars-nwe.afp.entry-id` - - `user.org.mars-nwe.afp.finder-info` - - `user.org.mars-nwe.afp.attributes` only for future AFP-only bits -- Resource forks remain unsupported and should return the documented completion - code instead of inventing a parallel storage backend. -- Deleted-file Macintosh metadata (`0x13`) is implemented on the mars_nwe - salvage backend and must not perform an AFP-local deleted-file scan. - -## Resolved final-audit items - -Resolved WebSDK compatibility items: - -1. `0x13` is implemented as a salvage/deleted-entry backend adapter and covered - by `afp_deleted_info_smoke`. - -- `0x0a` / `0x11` entry-id-only scan requests are supported when the base entry - ID resolves to a directory through mars_nwe namespace/basehandle logic. - -## Final comparison checklist - -When doing the final WebSDK / Novell NWAFP header pass, compare: - -- AFP subfunction numbers and names. -- Request field order, sizes, and byte order. -- Reply field order, sizes, and byte order. -- Bitmap meanings for get/set/scan information calls. -- Legacy vs AFP 2.0 layout variants. -- Completion codes for unsupported resource forks, insufficient rights, bad - handles, missing entries, bad paths, and unsupported deleted-file metadata. -- Whether each implemented endpoint still routes NetWare semantics through - mars_nwe functions rather than AFP-local shortcuts. diff --git a/tests/afp/AFP_FINAL_AUDIT.md b/tests/afp/AFP_FINAL_AUDIT.md deleted file mode 100644 index 2be4f31..0000000 --- a/tests/afp/AFP_FINAL_AUDIT.md +++ /dev/null @@ -1,129 +0,0 @@ -# AFP final WebSDK / Novell header audit plan - -This document is the working checklist for the final AFP endpoint audit. It is -not a statement that the audit is complete. The goal is to make the final pass -repeatable and to keep the comparison focused on protocol compatibility and -mars_nwe backend discipline. - -## Required reference sources - -Before marking the audit complete, compare the current implementation against -all available authoritative local references: - -- WebSDK AFP definitions, especially the header or documentation equivalent to - `nwafp.h`. -- Novell/NWAFP include files from the SDK tree used for the compatibility work. -- Any WebSDK request/reply examples bundled with the uploaded SDK archive. -- mars_nwe current `src/nwconn.c`. -- mars_nwe AFP helper inventory: - -```sh -tests/afp/afp_endpoint_inventory.py -tests/afp/afp_endpoint_inventory.py --json -``` - -The final audit should record the exact source paths used, for example: - -```text -WebSDK source: /... -Novell headers: /... -mars_nwe source: src/nwconn.c -audit date: YYYY-MM-DD -``` - -## Source greps to run - -Run these in the mars_nwe tree and in the unpacked WebSDK / Novell header trees. - -```sh -grep -RIn "AFP.*Get Entry ID\|Get Macintosh Info On Deleted\|Scan File Information\|Open File Fork\|Set File Information" . -grep -RIn "0x2222.*35\|function 35\|NWAFP\|nwafp" . -grep -RIn "AFP.*0x0[0-9a-fA-F]\|AFP.*0x1[0-3]" . -grep -RIn "Scan Salvageable\|Recover Salvageable\|Purge Salvageable\|Deleted" . -``` - -Run these in mars_nwe to verify cleanup and backend discipline: - -```sh -grep -RIn "layout=websdk\|libatalk\|Netatalk\|HAVE_NETATALK\|ENABLE_NETATALK" src include tests/afp CMakeLists.txt cmake 2>/dev/null -grep -RIn "entry-id-only .*unsupported\|unsupported.*entry-id" src/nwconn.c tests/afp -grep -RIn "\bopen\s*(\|\bread\s*(\|\bwrite\s*(\|\brename\s*(\|\bunlink\s*(\|\brmdir\s*(\|\bmkdir\s*(" src/nwconn.c -``` - -The last grep is review material, not an automatic failure. Some direct POSIX -calls may be legitimate inside older mars_nwe core paths, but AFP endpoint code -should not bypass mars_nwe helpers for NetWare semantics. - -## Endpoint comparison table - -Fill this table during the final pass. `mars_nwe backend path` must name the -mars_nwe helper family used by the endpoint, not just the AFP handler. - -| Subfn | WebSDK / Novell name | mars_nwe handler | Request layout OK | Reply layout OK | Completion codes OK | mars_nwe backend path OK | Notes | -| --- | --- | --- | --- | --- | --- | --- | --- | -| `0x01` | AFP Create Directory | inline AFP create directory case | TODO | TODO | TODO | TODO | | -| `0x02` | AFP Create File | inline AFP create file case | TODO | TODO | TODO | TODO | | -| `0x03` | AFP Delete | inline AFP delete case | TODO | TODO | TODO | TODO | | -| `0x04` | AFP Get Entry ID From Name | `afp_get_entry_id_from_name` | TODO | TODO | TODO | TODO | | -| `0x05` | AFP Get File Information | `afp_get_file_information` | TODO | TODO | TODO | TODO | | -| `0x06` | AFP Get Entry ID From NetWare Handle | `afp_get_entry_id_from_netware_handle` | TODO | TODO | TODO | TODO | | -| `0x07` | AFP Rename | inline AFP rename case | TODO | TODO | TODO | TODO | | -| `0x08` | AFP Open File Fork | inline AFP open file fork case | TODO | TODO | TODO | TODO | data fork only; resource fork unsupported | -| `0x09` | AFP Set File Information | inline AFP set file information case | TODO | TODO | TODO | TODO | legacy variant | -| `0x0a` | AFP Scan File Information | `afp_scan_file_information` | TODO | TODO | TODO | TODO | entry-id-only directory scan supported | -| `0x0b` | AFP Alloc Temporary Dir Handle | inline AFP alloc temporary dir handle case | TODO | TODO | TODO | TODO | | -| `0x0c` | AFP Get Entry ID From Path Name | `afp_get_entry_id_from_path_name` | TODO | TODO | TODO | TODO | | -| `0x0d` | AFP 2.0 Create Directory | inline AFP 2.0 create directory case | TODO | TODO | TODO | TODO | | -| `0x0e` | AFP 2.0 Create File | inline AFP 2.0 create file case | TODO | TODO | TODO | TODO | | -| `0x0f` | AFP 2.0 Get File Information | `afp_get_file_information` | TODO | TODO | TODO | TODO | AFP 2.0 variant | -| `0x10` | AFP 2.0 Set File Information | inline AFP 2.0 set file information case | TODO | TODO | TODO | TODO | AFP 2.0 variant | -| `0x11` | AFP 2.0 Scan File Information | `afp_scan_file_information` | TODO | TODO | TODO | TODO | entry-id-only directory scan supported | -| `0x12` | AFP Get DOS Name From Entry ID | `afp_get_dos_name_from_entry_id` | TODO | TODO | TODO | TODO | | -| `0x13` | AFP Get Macintosh Info On Deleted Files | implemented | server | helper | suite | docs | salvage-backed | - -## Backend discipline checklist - -For every implemented endpoint, check these rules explicitly: - -- Path resolution uses mars_nwe path/namespace helpers. -- Directory identities use mars_nwe namespace/basehandle state. -- File entry-id reverse lookup uses the AFP entry-id xattr cache only for files. -- File open/read/write/close uses mars_nwe NetWare file handles. -- Rights use mars_nwe trustee/effective-rights checks. -- Hidden/system/archive attributes use mars_nwe attribute/archive paths. -- FinderInfo uses the AFP-only metadata store. -- Resource forks remain unsupported until a real backend exists. -- Deleted-file Macintosh metadata waits for mars_nwe salvage/deleted-entry state. -- AFP handlers do not introduce standalone POSIX filesystem semantics. - -## Known open decisions - -### Entry-id-only scan - -AFP `0x0a` and AFP 2.0 `0x11` support directory entry-id starts. The entry ID -must resolve through mars_nwe namespace/basehandle logic; file AFP entry-id xattrs -are not valid scan bases. - -### Deleted-file Macintosh metadata - -AFP `0x13` is now implemented as a thin adapter over the normal NetWare -salvage family, which is present and verified: - -- `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) - -Do not extend AFP `0x13` as an AFP-local deleted-file scan; keep it on the shared salvage backend. - -## Completion criteria - -The final audit is complete only when: - -1. The endpoint table above is filled with explicit OK/unsupported decisions. -2. The exact WebSDK and Novell header paths used for the comparison are listed. -3. `tests/afp/afp_endpoint_inventory.py` has no unexpected warnings. -4. The only accepted warnings are documented final-slice limitations. -5. The AFP smoke suite still reports `failures=0`. -6. Any unsupported endpoint has a documented backend dependency or deliberate - compatibility-slice reason. diff --git a/tests/afp/AFP_WEBSK_AUDIT_FINDINGS.md b/tests/afp/AFP_WEBSK_AUDIT_FINDINGS.md deleted file mode 100644 index 4176d96..0000000 --- a/tests/afp/AFP_WEBSK_AUDIT_FINDINGS.md +++ /dev/null @@ -1,141 +0,0 @@ -# AFP WebSDK audit findings - -This records the concrete WebSDK pass done against the uploaded SDK tree before -turning the final audit checklist into implementation decisions. - -## Sources inspected - -The available uploaded WebSDK tree contained AFP reference HTML pages rather -than a standalone `nwafp.h` header. The checked source paths were: - -```text -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/TOCMap.txt -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/projmap.txt -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R001.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R002.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R003.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R004.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R005.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R006.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R007.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R008.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R009.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R010.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R011.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R012.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R013.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R014.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R015.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R016.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R017.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R018.htm -``` - -The deleted-file dependency check also inspected the normal salvage reference -pages: - -```text -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0010R024.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0010R027.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0010R038.htm -/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0010R039.htm -``` - -## AFP subfunction numbers - -The WebSDK AFP reference pages confirm this NCP `0x2222 / function 35` subfunction -map: - -| Subfn | WebSDK page | Name | mars_nwe inventory status | -| --- | --- | --- | --- | -| `0x01` | `0002R006.htm` | AFP Create Directory | implemented | -| `0x02` | `0002R007.htm` | AFP Create File | implemented | -| `0x03` | `0002R008.htm` | AFP Delete | implemented | -| `0x04` | `0002R010.htm` | AFP Get Entry ID From Name | implemented / validating | -| `0x05` | `0002R013.htm` | AFP Get File Information | implemented | -| `0x06` | `0002R011.htm` | AFP Get Entry ID From NetWare Handle | implemented | -| `0x07` | `0002R016.htm` | AFP Rename | implemented | -| `0x08` | `0002R015.htm` | AFP Open File Fork | implemented for data fork | -| `0x09` | `0002R018.htm` | AFP Set File Information | implemented | -| `0x0a` | `0002R017.htm` | AFP Scan File Information | implemented for path and directory entry-id starts | -| `0x0b` | `0002R005.htm` | AFP Alloc Temporary Directory Handle | implemented | -| `0x0c` | `0002R012.htm` | AFP Get Entry ID From Path Name | implemented | -| `0x0d` | `0002R001.htm` | AFP 2.0 Create Directory | implemented | -| `0x0e` | `0002R002.htm` | AFP 2.0 Create File | implemented | -| `0x0f` | `0002R013.htm` | AFP 2.0 Get File Information | implemented through shared handler | -| `0x10` | `0002R004.htm` | AFP 2.0 Set File Information | implemented through shared semantics | -| `0x11` | `0002R003.htm` | AFP 2.0 Scan File Information | implemented for path and directory entry-id starts | -| `0x12` | `0002R009.htm` | AFP Get DOS Name From Entry ID | implemented | -| `0x13` | `0002R014.htm` | AFP Get Macintosh Info On Deleted Files | implemented / salvage-backend adapter | - -No extra AFP subfunction beyond `0x13` appeared in the WebSDK AFP reference TOC. - -## Layout findings - -The current mars_nwe request/reply layouts match the WebSDK direction for the -implemented compatibility slice: - -- `0x04` Get Entry ID From Name uses volume, AFP entry ID, path length, and an - AFP-style path relative to the entry ID, then returns a 32-bit AFP entry ID. -- `0x05` and `0x0f` Get File Information use volume, AFP entry ID, request - bitmap, path length, and AFP-style path, then return the AFP information - record. -- `0x09` and `0x10` Set File Information use volume, AFP entry ID, request - bitmap, attributes, date/time fields, FinderInfo, ProDOS info, and relative - AFP path. The server intentionally implements only the safe subset that maps - to mars_nwe metadata and AFP-only xattrs. -- `0x0a` and `0x11` Scan File Information use volume, AFP entry ID, last-seen - ID, desired response count, search bitmap, request bitmap, path length, and an - AFP-style path relative to the entry ID. -- `0x0b` Alloc Temporary Directory Handle uses volume, AFP entry ID, path length, - and an AFP-style path relative to the entry ID, then returns a NetWare - directory handle and access-rights byte. -- `0x0c` Get Entry ID From Path Name is the older NetWare-directory-handle based - lookup and returns a 32-bit AFP entry ID. -- `0x08` Open File Fork returns AFP entry ID, fork length, and a NetWare access - handle. The WebSDK page confirms the data/resource fork indicator and access - mode bits. mars_nwe currently supports the data fork and rejects resource - fork requests until a real backend exists. -- `0x13` Get Macintosh Info On Deleted Files is not a live path lookup. Its - request is volume number plus DOS directory entry, and its reply is FinderInfo, - ProDOS info, resource fork size, and deleted file name. - -## Important implementation decisions - -### Entry-id-only Scan File Information - -The WebSDK Scan File Information pages describe the path string as relative to -`AFP Entry ID`. mars_nwe supports the compatible directory case: a request with -a directory AFP entry ID and an empty path scans that directory. - -The implementation remains intentionally narrow: - -1. Entry-id scan bases must resolve to directories through mars_nwe - namespace/basehandle logic. -2. The existing mars_nwe directory scan path is reused after resolution. -3. File AFP xattr entry IDs are not valid scan bases. - -### AFP 0x13 is salvage-backed in this slice - -The WebSDK `0x13` page makes this call dependent on deleted-file state: the -request is keyed by volume and DOS directory entry, and the reply describes a -deleted Macintosh directory entry. The WebSDK file services chapter also -contains the normal salvage family: - -- Scan Salvageable Files -- Recover Salvageable File -- Purge Salvageable File -- Scan Salvageable File (old) - -Therefore `0x13` is implemented only after the verified mars_nwe -salvage/deleted-entry backend. It must remain a backend adapter, not an -AFP-local scan of live paths or filesystem trash. - -## Result - -The WebSDK pass does not require renaming the AFP endpoint map again. The -remaining concrete work before closing the current AFP slice is: - -1. Keep `0x13` documented as salvage-backed; do not add a second AFP deleted-file store. -2. Re-run `tests/afp/afp_endpoint_inventory.py` and the AFP smoke suite. -3. Fill the final audit table with these WebSDK source paths and results. diff --git a/tests/afp/README.md b/tests/afp/README.md index 89ebaae..4e20208 100644 --- a/tests/afp/README.md +++ b/tests/afp/README.md @@ -1,53 +1,128 @@ # AFP integration smoke tests -This directory contains optional AFP integration smoke tests for endpoints that -are easier to exercise from a Unix host than from the DOS test utilities. +Status: finished for the current AFP compatibility slice. -The tests use the ncpfs/libncp client library. They are not built by default -because they require the host ncpfs development headers/library and a running -NetWare-compatible server. +This directory contains the AFP endpoint smoke tests and the single remaining +AFP documentation file for this test group. Earlier endpoint-inventory, +WebSDK-audit, deleted-file-info, and TODO notes have been folded into this +README so the AFP test directory has one Markdown entry point. -The AFP endpoints are intentionally conservative but are now backed by mars_nwe -itself rather than Netatalk/libatalk. Directory Entry IDs prefer the existing -mars_nwe NetWare namespace/basehandle mapping. Regular-file Entry IDs use the -mars_nwe-owned `org.mars-nwe.afp.entry-id` xattr so entry-id-only AFP probes can -resolve back to files without treating file IDs as DOS namespace directory -numbers. FinderInfo uses `org.mars-nwe.afp.finder-info`; ProDOSInfo uses the -6-byte `org.mars-nwe.afp.prodos-info` xattr in the same nwatalk metadata layer; -only future AFP-only attribute bits belong in `org.mars-nwe.afp.attributes`. -NetWare semantics such as Hidden/System/Archive attributes, timestamps, trustee -rights, create, rename, delete, and data-fork open/write stay on the existing -mars_nwe NetWare paths. -Resource forks remain unsupported. On Linux the local xattr helper stores -source-level `org.mars-nwe.*` names through the portable `user.` namespace. +## Scope -Build with: +The AFP endpoints are backed by mars_nwe itself rather than Netatalk/libatalk. +AFP handlers must keep NetWare semantics on mars_nwe core paths: + +- path, namespace, create, delete, rename, and directory operations use the + existing mars_nwe path/namespace and object-lifecycle helpers; +- file I/O uses normal mars_nwe NetWare file handles; +- rights checks use mars_nwe trustee/effective-rights logic; +- Hidden, System, Archive, timestamps, creator/modifier, backup/archive, and + related metadata use mars_nwe attribute/archive/fileinfo paths; +- AFP-only xattrs are limited to `user.org.mars-nwe.afp.entry-id`, + `user.org.mars-nwe.afp.finder-info`, `user.org.mars-nwe.afp.prodos-info`, and + future AFP-only bits in `user.org.mars-nwe.afp.attributes`. + +Resource forks remain unsupported. AFP resource-fork requests should return +the documented completion code instead of inventing a parallel storage backend. + +## Endpoint inventory + +The generated helper remains the source of truth for fresh checks: + +```sh +tests/afp/afp_endpoint_inventory.py +tests/afp/afp_endpoint_inventory.py --json +``` + +Current coverage: + +| Subfn | Name | Status | Backend discipline | +| --- | --- | --- | --- | +| `0x01` | AFP Create Directory | implemented | mars_nwe namespace/object lifecycle | +| `0x02` | AFP Create File | implemented | mars_nwe namespace/object lifecycle; AFP xattr only for file entry-id metadata | +| `0x03` | AFP Delete | implemented | mars_nwe namespace/object lifecycle | +| `0x04` | AFP Get Entry ID From Name | implemented | mars_nwe namespace/basehandle; directory bases only | +| `0x05` | AFP Get File Information | implemented | mars_nwe attributes/rights plus AFP metadata xattrs | +| `0x06` | AFP Get Entry ID From NetWare Handle | implemented | mars_nwe file-handle lookup, then AFP entry-id metadata | +| `0x07` | AFP Rename | implemented | mars_nwe namespace/object lifecycle; AFP entry-id metadata follows renamed files | +| `0x08` | AFP Open File Fork | implemented for data fork | mars_nwe file handles; resource fork unsupported | +| `0x09` | AFP Set File Information | implemented | mars_nwe attributes/archive/fileinfo/trustee; AFP xattrs only for AFP metadata | +| `0x0a` | AFP Scan File Information | implemented | mars_nwe directory scan; entry-id-only bases must resolve to directories | +| `0x0b` | AFP Alloc Temporary Dir Handle | implemented | mars_nwe namespace and directory handles | +| `0x0c` | AFP Get Entry ID From Path Name | implemented | mars_nwe namespace plus AFP entry-id metadata | +| `0x0d` | AFP 2.0 Create Directory | implemented | same discipline as `0x01` | +| `0x0e` | AFP 2.0 Create File | implemented | same discipline as `0x02` | +| `0x0f` | AFP 2.0 Get File Information | implemented | same discipline as `0x05` | +| `0x10` | AFP 2.0 Set File Information | implemented | same discipline as `0x09` | +| `0x11` | AFP 2.0 Scan File Information | implemented | same discipline as `0x0a` | +| `0x12` | AFP Get DOS Name From Entry ID | implemented | mars_nwe namespace and AFP entry-id reverse lookup | +| `0x13` | AFP Get Macintosh Info On Deleted Files | implemented | adapter over the shared salvage backend | + +## WebSDK / Novell audit result + +The AFP WebSDK pass confirmed the `NCP 0x2222 / function 35` map above. The +uploaded reference tree contained AFP HTML reference pages rather than a single +`nwafp.h`; the checked pages were under: + +```text +/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R001.htm +... +/mnt/data/websdk_full/websdk/htmldoc/nwhtm/redleg14/a_apiref/0002R018.htm +``` + +The deleted-file dependency check also used the normal salvage reference pages +for Scan, Recover, Purge, and old Scan Salvageable File. No AFP subfunction +beyond `0x13` appeared in the WebSDK AFP reference TOC. + +Important audit decisions now considered closed for this slice: + +- entry-id-only `0x0a` / `0x11` scans are supported only when the AFP entry ID + resolves to a directory through mars_nwe namespace/basehandle logic; +- file AFP xattr entry IDs are not valid scan bases; +- `0x13` is salvage-backed and must not scan live paths, `.recycle`, or + `.salvage` directly from AFP code. + +## AFP `0x13` deleted-file metadata + +`AFP Get Macintosh Info On Deleted Files` is NCP `0x2222 / 35 / 19` and WebSDK +subfunction byte `0x13`. Its request is `VolumeNumber` plus +`DOSDirectoryNumber`; the reply contains FinderInfo[32], ProDOSInfo[6], resource +fork size, and the deleted file name. + +The implementation is finished for the current slice and remains a thin adapter +over the shared mars_nwe salvage/deleted-entry backend: + +1. resolve the deleted DOS directory entry through the salvage backend; +2. return FinderInfo from the salvage JSON snapshot; +3. return ProDOS information from the nwatalk xattr-backed salvage JSON snapshot + field `prodos_info_hex`; +4. return resource fork size from the salvage JSON snapshot; +5. return the deleted original filename from the salvage record. + +The AFP code must not create a second deleted-file store and must not expose the +backend `.recycle` / `.salvage` repositories as AFP-visible paths. + +## Build + +The helpers require ncpfs/libncp headers and library, so they are optional and +not built by default. Enable the test build and build individual helpers or the +suite target: ```sh cmake -DMARS_NWE_BUILD_TESTS=ON ... cmake --build . --target afp_entry_id_smoke cmake --build . --target afp_file_info_smoke cmake --build . --target afp_scan_info_smoke -cmake --build . --target afp_create_directory_smoke -cmake --build . --target afp_temp_dir_handle_smoke cmake --build . --target afp_set_file_info_smoke +cmake --build . --target afp_deleted_info_smoke +cmake --build . --target afp_smoke_suite ``` +## Smoke suite -## AFP smoke-suite report helper - -`afp_smoke_suite.sh` runs the currently verified AFP AFP smoke helpers as one -collectable report. It is meant for interactive runtime validation after a -server rebuild: the script prints each helper with the password masked, captures -new AFP lines appended to the mars_nwe server log while the suite runs, and -adds `getfattr -e hex` checks for the mars_nwe AFP xattrs on the tested Unix -file. - -When `MARS_NWE_BUILD_TESTS=ON` is enabled, CMake copies the helper into -the build `tests/afp` directory through the `afp_smoke_suite` build target. -This keeps the runtime copy in sync with source changes, and `cmake --build - --target clean` removes the copied script so stale suite helpers do -not survive clean rebuilds. +`afp_smoke_suite.sh` is the single AFP integration entry point. CMake copies it +into the build-tree `tests/afp` directory through the `afp_smoke_suite` target +so the runtime helper stays in sync with source changes. Example from the build `tests/afp` directory: @@ -60,13 +135,11 @@ Example from the build `tests/afp` directory: --out /tmp/mars-afp-smoke.txt ``` -The report includes AFP Entry ID, Entry ID From NetWare Handle, Get File -Information, Scan File Information, Alloc Temporary Directory Handle, AFP Create -Directory for both legacy (`0x01`) and AFP 2.0 (`0x0d`), Open File -Fork, FinderInfo Set File Information for both AFP 2.0 (`0x10`) and the legacy AFP -Set File Information (`0x09`), AFP 2.0 Hidden/System/Archive Set/Clear File -Information, legacy AFP `0x09` Hidden Set/Clear coverage, and the Linux xattr -checks for: +The suite covers Entry ID lookup, Entry ID from NetWare handle, Get File +Information, Scan File Information, Alloc Temporary Directory Handle, Create +Directory, Create File, Delete, Rename, Open File Fork, Set File Information, +DOS name lookup, and AFP `0x13` deleted-file metadata. It also checks the AFP +xattrs on the tested Unix file: ```text user.org.mars-nwe.afp.finder-info @@ -75,36 +148,13 @@ user.org.mars-nwe.afp.attributes user.org.mars-nwe.afp.entry-id ``` +Use `--no-log` when the server log is unavailable or 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. +## Rights-negative coverage -A verified AFP/ProDOS smoke run completed with `failures=0`. The suite set -FinderInfo and ProDOSInfo on the live smoke file, verified the live xattr, then -created and deleted a temporary AFP file and verified AFP `0x13 Get Macintosh -Info On Deleted File` returned the same ProDOSInfo from the Salvage snapshot: - -```text -AFP Set File Info ... finder_type=TEXT finder_creator=MARS ... prodos_info verified -AFP deleted prodos=010203040506 -user.org.mars-nwe.afp.prodos-info=0x010203040506 -``` - -The suite now also exercises the WebSDK AFP attribute bits that -`afp_set_file_info_smoke` supports: Hidden (`0x0200`), System (`0x0400`), and -Archive (`0x2000`). Those bits are verified through the NetWare attribute path, -not through a parallel AFP attribute store. The legacy AFP `0x09` path still -runs for FinderInfo and Hidden so both Set File Information entry points cover -the same WebSDK packet layout. Each attribute bit is cleared again before the -final xattr dump so repeated runs leave no AFP-only attribute payload unless a -future AFP-only bit is explicitly tested. - -Use `--no-log` when the log file is unavailable or when the server log is being -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/afp` directory: +The suite can optionally exercise Modify-rights failures with a second user: ```sh ./afp_smoke_suite.sh \ @@ -116,1307 +166,20 @@ user. For a no-password test user such as `NOPASSUSER`, run from the build --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.*` -xattrs, but those writes are still file metadata changes. The Set File -Information handler now resolves the target through the normal mars_nwe path and -checks the existing NetWare Modify trustee policy before updating FinderInfo or -AFP-only Hidden/System metadata. Archive uses the existing NetWare attribute -path, and Modify timestamp uses `nw_utime_node()`, so the smoke suite should -continue to pass for SUPERVISOR while non-supervisor negative coverage can later -exercise the same policy gate. - -A verified rights-negative smoke run with `--readonly-user NOPASSUSER`, -`--readonly-no-password`, and `--prepare-readonly-rights` completed with -`failures=0`. The setup used `nwgrant -r '[RF]'` for the no-password test -user so the file remained readable and searchable but lacked Modify rights. -The suite then verified that AFP Set File Information rejects FinderInfo, -Hidden, and System metadata writes with completion `0x8c` while the -SUPERVISOR positive path still succeeds: - -```text -AFP metadata Modify rights rejected: FinderInfo -AFP Set File Information returned expected completion 0x8c: subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0020 -AFP metadata Modify rights rejected: Invisible -AFP Set File Information returned expected completion 0x8c: subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 -AFP metadata Modify rights rejected: System -AFP Set File Information returned expected completion 0x8c: subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 -``` - -The server log for the same run showed the common policy gate for all three -probes: - -```text -AFP 2.0 Set File Information rejected: no Modify rights for AFP metadata path='SYS:PUBLIC/pmdflts.ini' -``` - -The final `nwrevoke` cleanup returned successfully, and the final xattr/stat -checks remained intact. An initial pre-cleanup `nwrevoke` may report that no -explicit assignment existed yet; that is harmless as long as `nwgrant` and the -final cleanup both succeed. - -A verified suite run after the FinderInfo payload-alignment fix completed with -`failures=0` for `SYS:PUBLIC/pmdflts.ini`. The report covered Entry ID by path, -Entry ID from NetWare handle, Get File Information, Scan File Information, -Alloc Temporary Directory Handle, Open File Fork, FinderInfo Set File -Information, and Finder Hidden set/clear. The relevant Linux xattr checks -from that run were: - -```text -user.org.mars-nwe.afp.finder-info=0x544558544d415253000000000000000000000000000000000000000000000000 -user.org.mars-nwe.afp.attributes=0x01000000 -user.org.mars-nwe.afp.entry-id=0x0100000033f9a1ed -``` - -The FinderInfo value starts with `TEXTMARS` without a leading padding byte, so -the smoke helper and server now agree on the WebSDK/NWAFP Set File Information -payload alignment. The server log excerpt for the same run showed all AFP -operations returning successfully, including `mask=0x0020` for FinderInfo and -`mask=0x0001` for the Hidden/System/Archive attribute probes. - - -A later full-suite run after the smoke-suite copy/sync fix also completed with -`failures=0` from the build-tree script and confirmed that the legacy AFP Set -File Information endpoint (`0x09`) is exercised in the same report as AFP 2.0 -`0x10`. The run covered legacy FinderInfo, legacy Hidden set/clear, AFP 2.0 -System set/clear, and AFP 2.0 Archive set/clear while leaving the final AFP -metadata attributes xattr clear: - -```text -AFP Set File Info subfunction=0x09 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0020 attrs=0x0000 finder_type=TEXT finder_creator=MARS entry_id=0x1ad06d3e verified -AFP Set File Info subfunction=0x09 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x0001 finder_type=TEXT finder_creator=MARS entry_id=0x1ad06d3e verified -AFP Set File Info subfunction=0x09 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x0000 finder_type=TEXT finder_creator=MARS entry_id=0x1ad06d3e verified -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x0004 finder_type=TEXT finder_creator=MARS entry_id=0x1ad06d3e verified -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x0000 finder_type=TEXT finder_creator=MARS entry_id=0x1ad06d3e verified -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x2000 finder_type=TEXT finder_creator=MARS entry_id=0x1ad06d3e verified -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x0000 finder_type=TEXT finder_creator=MARS entry_id=0x1ad06d3e verified -user.org.mars-nwe.afp.finder-info=0x544558544d415253000000000000000000000000000000000000000000000000 -user.org.mars-nwe.afp.attributes=0x01000000 -user.org.mars-nwe.afp.entry-id=0x010000001ad06d3e -``` - - -## AFP Create Directory smoke test - -`afp_create_directory_smoke` sends the WebSDK/nwafp.h AFP Create Directory -requests through libncp: - -```text -NCP 0x2222/35/01 AFP Create Directory -NCP 0x2222/35/0d AFP 2.0 Create Directory -``` - -The helper derives the parent Entry ID with the existing AFP Entry ID From Path -Name endpoint, sends only the new leaf name to Create Directory, and verifies -the returned directory ID by looking the created path up again. This exercises -the server-side path through the existing mars_nwe namespace/basehandle mapping -and `nw_mk_rd_dir()` rather than an AFP-only directory resolver. - -Example: - -```sh -./tests/afp/afp_create_directory_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpdirts -./tests/afp/afp_create_directory_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpdirts2 -``` - -The full smoke suite creates temporary directories under the tested parent using -fresh short DOS-compatible leaf names by default. It now uses the AFP Delete -endpoint for both pre-test cleanup and post-create cleanup, so the smoke no -longer depends on local Unix `rmdir` permissions when the server created the -directory under its own identity. Use `--create-dir-name NAME` to override the -default temporary leaf name. - - -## AFP Create File smoke test - -`afp_create_file_smoke` sends the WebSDK/nwafp.h AFP Create File requests -through libncp: - -```text -NCP 0x2222/35/02 AFP Create File -NCP 0x2222/35/0e AFP 2.0 Create File -``` - -The helper derives the parent Entry ID with AFP Entry ID From Path Name, sends -only the new leaf name to Create File, and verifies the returned file ID by -looking the created path up again. This exercises the server-side file create -path through the existing mars_nwe create helpers rather than direct local file -creation from the test. - -Example: - -```sh -./tests/afp/afp_create_file_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpfile -./tests/afp/afp_create_file_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpfil2 -``` - -The full smoke suite creates temporary files under the tested parent with fresh -short DOS-compatible leaf names by default. It now uses AFP Delete for both -pre-test cleanup and post-create cleanup, avoiding local Unix `rm` permission -assumptions when mars_nwe creates the file as the server-side identity. Use -`--create-file-name NAME` to override the default leaf name. - -Runtime status: the build-server smoke run verified both create-file variants -with AFP Delete cleanup enabled. Legacy AFP Create File (`0x02`) created -`SYS:PUBLIC/atst0` and returned `0x77ba6113`; AFP 2.0 Create File (`0x0e`) -created `SYS:PUBLIC/atst02` and returned `0x3cd55515`. Both IDs were re-read -through AFP Entry ID From Path Name and then removed through AFP Delete. - - -## AFP Delete smoke test - -`afp_delete_smoke` sends the WebSDK/nwafp.h AFP Delete request through -libncp: - -```text -NCP 0x2222/35/03 AFP Delete -``` - -The helper derives the parent Entry ID with AFP Entry ID From Path Name, sends -only the leaf name to AFP Delete, and verifies that a follow-up Entry ID lookup -for the deleted path no longer succeeds. The server implementation routes -directory removal through the existing mars_nwe directory remove path and file -removal through the existing NetWare delete-file path; it does not use local -Unix `unlink`/`rmdir` calls from the test process. - -Example: - -```sh -./tests/afp/afp_delete_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpfile -``` - -Runtime status: the smoke suite now uses AFP Delete for pre-test cleanup and -post-create cleanup around both Create Directory variants and both Create File -variants. The verified run deleted stale `afptst0`, `afptst02`, `atst0`, and -`atst02` paths, recreated the directory and file pairs, deleted all four -created objects again through AFP Delete, and ended with `failures=0`. - - -## AFP Rename smoke test - -`afp_rename_smoke` sends the WebSDK/nwafp.h AFP Rename request through -libncp: - -```text -NCP 0x2222/35/07 AFP Rename -``` - -The helper derives the source and destination parent Entry IDs with AFP Entry -ID From Path Name, sends leaf names to AFP Rename, and verifies that the source -path disappears while the destination path resolves to the expected Entry ID. -Directory renames use the existing mars_nwe directory-aware move path. File -renames use the existing NetWare file move path with file-only search -attributes, so regular files are not filtered out by the directory search bit. - -Example: - -```sh -./tests/afp/afp_rename_smoke -S MARS -U SUPERVISOR -P secret \ - SYS:PUBLIC/oldname SYS:PUBLIC/newname -``` - -Runtime status: the smoke suite now covers AFP Rename for both directories and -regular files. The verified run created and renamed `SYS:PUBLIC/d22240` to -`SYS:PUBLIC/d22240r`, preserving the namespace-derived directory Entry ID -`0x00000005`. It also created and renamed `SYS:PUBLIC/m22248` to -`SYS:PUBLIC/m22248r`, preserving the nwatalk/fallback AFP file Entry ID -`0x78da3728`. Both renamed objects were removed through AFP Delete, and the -full suite ended with `failures=0`. - -## AFP Entry ID smoke test - -`afp_entry_id_smoke` sends the WebSDK-documented NetWare AFP request: - -```text -NCP 0x2222/35/12 AFP Get Entry ID From Path Name -``` - -It uses libncp's `NWRequestSimple()` path, so it goes through the same client -transport stack as other ncpfs-based utilities. - -Example: - -```sh -./tests/afp/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -``` - -The test accepts NetWare-style `VOL:PATH` arguments. By default it sends the -supplied `SYS:`-style path directly with directory handle 0, matching the -verified mars_nwe smoke-test path. `--alloc-handle` is available only for -follow-up debugging of the separate directory-handle allocation path, and -`--dir-handle N` expects a handle that is valid in the current connection. - -Useful smoke cases for a standard MARS-NWE `SYS` volume are: - -```sh -./tests/afp/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS: -./tests/afp/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM -./tests/afp/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:BURST -``` - -A successful reply prints the request path, directory handle, and returned -32-bit AFP Entry ID. Current AFP Entry IDs are normally derived from the -existing NetWare namespace/basehandle mapping. A full-suite run after that -change returned `entry_id=0x00000004` for `SYS:PUBLIC/pmdflts.ini`. - -If the server was built without xattr support, AFP metadata-dependent endpoints -are expected to return invalid namespace. To treat that as a successful negative -smoke test, use: - -```sh -./tests/afp/afp_entry_id_smoke --allow-invalid-namespace -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -``` - -For path-resolution negative tests, use `--allow-invalid-path` to accept the -expected `0x9c` Invalid Path completion. - - -### AFP Get Entry ID From Name - -`afp_entry_id_smoke` can also exercise the WebSDK-documented NetWare AFP -request: - -```text -NCP 0x2222/35/04 AFP Get Entry ID From Name -``` - -Use `--from-name` to select this subfunction. mars_nwe supports both the -path-backed smoke mode and the documented entry-id-relative form. The -path-backed form passes a raw `SYS:`-style path with base Entry ID 0. The -entry-id-relative form first resolves a directory base with -`AFP Get Entry ID From Path Name`, then sends the leaf name with that base -Entry ID and no `VOL:` prefix. - -Useful smoke cases for a standard MARS-NWE `SYS` volume are: - -```sh -./tests/afp/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS: -./tests/afp/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_entry_id_smoke --from-name --base-path SYS:PUBLIC -S MARS -U SUPERVISOR -P secret pmdflts.ini -./tests/afp/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:SYSTEM -./tests/afp/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:BURST -``` - -A successful reply prints the same 32-bit AFP Entry ID format as the path-name -probe. Path-backed requests use the normal raw `VOL:PATH` resolver. -Entry-id-relative requests use the directory Entry ID only as a base directory, -resolve that base through the mars_nwe namespace/basehandle path, and then feed -the composed `VOL:BASE/NAME` path back into the normal mars_nwe resolver. -Regular file Entry IDs are not treated as directory bases. - -### AFP Get Entry ID From NetWare Handle - -`afp_entry_id_smoke` can also exercise the WebSDK-documented NetWare AFP -request: - -```text -NCP 0x2222/35/06 AFP Get Entry ID From NetWare Handle -``` - -Use `--from-handle` to select this subfunction. The smoke test opens the -requested file through libncp in the same connection, passes the returned -6-byte NetWare file handle to the AFP request, and closes the file after the -AFP reply. This is important because NetWare file handles are connection-local: -`--dir-handle N` and file-handle values copied from server logs or unrelated -helper processes are not stable inputs for this request. - -Useful smoke cases for a standard MARS-NWE `SYS:PUBLIC` directory are: - -```sh -./tests/afp/afp_entry_id_smoke --from-handle -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini -./tests/afp/afp_entry_id_smoke --from-handle -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/ohlogscr.bat -``` - -Successful replies print the resolved volume number, 32-bit AFP Entry ID, and -fork indicator. The current implementation reports the data fork (`fork=0`) -and derives the Entry ID from the same mars_nwe namespace/basehandle mapping as -path-backed lookups: - -```text -AFP Entry ID From NetWare Handle path=SYS:PUBLIC/pmdflts.ini volume=0 entry_id=0x00000004 (4) fork=0 -AFP Get Entry ID From NetWare Handle: handle=1 volume=0 unix='/var/mars_nwe/SYS/public/pmdflts.ini' entry=0x00000004 -``` - -Persistent Apple CNID/AppleDouble/mars_nwe AFP xattr-backed identity, parent Entry ID -derivation, and AFP resource-fork handle semantics remain future Mac-namespace -work; the current smoke coverage verifies the conservative read-only data-fork -mapping through the existing NetWare namespace identity path. - -## AFP Alloc Temporary Directory Handle smoke test - -`afp_temp_dir_handle_smoke` sends the WebSDK-documented NetWare AFP request. It can send either a path-backed request or `--entry-id-only`, where it first resolves the directory entry ID through AFP Get Entry ID From Path Name and then sends Alloc Temporary Directory Handle with `path_len=0`: - -```text -NCP 0x2222/35/11 AFP Alloc Temporary Directory Handle -``` - -The request layout is the AFP volume number, base AFP Entry ID, path length, -and AFP-style path. The current mars_nwe implementation supports both raw -`VOL:`-style path requests such as `SYS:` or `HOME:` and entry-id-only directory -requests where `path_len=0`. Path-backed requests resolve the effective -NetWare volume from the path prefix instead of assuming volume 0. Entry-ID-only -requests use the shared AFP object resolver and, for directories, remain on the -existing mars_nwe namespace/basehandle path rather than using file AFP xattrs. - -Useful smoke cases for a standard MARS-NWE `SYS` volume are: - -```sh -./tests/afp/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS: -./tests/afp/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_temp_dir_handle_smoke --entry-id-only -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM -./tests/afp/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:BURST -``` - -On installations with another exported volume, the same helper can be run -against that raw prefix, for example `HOME:`. The server log should then show -the resolved volume number for `HOME:` rather than hard-coded `vol=0`. - -A successful reply prints the allocated temporary NetWare directory handle and -the effective-rights mask returned by the server. The smoke helper immediately -deallocates the handle with the normal NetWare Deallocate Directory Handle call -before closing the connection, so the handle value is only useful inside that -client connection and must not be copied into later tests or server logs. - -Runtime-verified output and server diagnostic shape: - -```text -AFP Alloc Temporary Dir Handle path=SYS: dir_handle=2 rights=0xff -AFP Alloc Temporary Dir Handle path=SYS:PUBLIC entry_id=0x00000000 dir_handle=2 rights=0xff -AFP Alloc Temporary Dir Handle path=SYS:PUBLIC entry_id=0x00000004 dir_handle=2 rights=0xff entry-id-only -AFP Alloc Temporary Dir Handle path=SYS:SYSTEM dir_handle=2 rights=0xff -AFP Alloc Temporary Dir Handle path=SYS:BURST dir_handle=2 rights=0xff -AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:' dir_handle=2 rights=0x1ff -AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:PUBLIC' dir_handle=2 rights=0x1ff -AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000004 path='SYS:PUBLIC' dir_handle=2 rights=0x1ff entry-id -AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:SYSTEM' dir_handle=2 rights=0x1ff -AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:BURST' dir_handle=2 rights=0x1ff -``` - -The AFP reply carries the one-byte access-rights field consumed by the smoke -helper, so the client prints `0xff`. The server diagnostic logs the internal -NetWare effective-rights mask before that AFP reply narrowing, so a fully -privileged directory can appear as `0x1ff` in `mars_nwe.log`. - -If the server was built without xattr support, use -`--allow-invalid-namespace` for the expected negative test. Use -`--allow-invalid-path` for path-resolution negative tests. - -### AFP temporary directory handle entry-id smoke success - -Runtime status: the full AFP smoke suite is verified with `failures=0` after -adding entry-id-only coverage for AFP Alloc Temporary Directory Handle. The -verified run resolved `SYS:PUBLIC` to directory Entry ID `0x00000004`, sent the -allocation request with `path_len=0`, received a temporary directory handle, and -released it through the normal NetWare directory-handle cleanup path. - -Verified output: - -```text -AFP Alloc Temporary Directory Handle by Entry ID -AFP Alloc Temporary Dir Handle path=SYS:PUBLIC entry_id=0x00000004 dir_handle=2 rights=0xff entry-id-only - -Summary: failures=0 -``` - -The server log confirms that the entry-id-only request is routed back to the -normal directory path resolver: - -```text -AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000004 path='SYS:PUBLIC' dir_handle=2 rights=0x1ff entry-id -``` - - -## AFP Open File Fork smoke test - -`afp_open_file_fork_smoke` sends the WebSDK-documented NetWare AFP open fork -request: - -```text -NCP 0x2222/35/08 AFP Open File Fork -``` - -mars_nwe supports raw `VOL:`-style path requests such as `SYS:` or -`HOME:` with base Entry ID zero and opens only the AFP data fork. It also -accepts entry-id-only data-fork opens for files whose AFP entry id is already -persisted in the `org.mars-nwe.afp.entry-id` nwatalk metadata. Read and write -data-fork opens are routed through the existing NetWare file open/share path -(`nw_creat_open_file()`/`file_creat_open()`), so trustee rights, read-only -attributes, and share-deny checks stay shared with regular NCP file opens. For -path-backed requests, mars_nwe resolves the effective NetWare volume from the -raw path prefix instead of assuming volume 0. Entry-id-only file opens reverse -resolve through the AFP/nwatalk metadata cache, not through DOS namespace -directory numbers. On success the server returns the normal six-byte NetWare -file handle shape used by AFP handle APIs plus the current data-fork length. -The smoke helper immediately closes the returned NetWare file handle in the -same connection. - -Useful smoke cases for a standard MARS-NWE `SYS` volume are: - -```sh -./tests/afp/afp_open_file_fork_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini -./tests/afp/afp_open_file_fork_smoke --entry-id-only -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini -./tests/afp/afp_open_file_fork_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/ohlogscr.bat -``` - -A file on another exported volume should be tested with its raw volume prefix -(for example `HOME:...`). The matching server log should report that resolved -volume number, while still showing the request volume byte separately. - -A successful reply prints the returned NetWare handle, the requested fork, the -requested access mode, and the data-fork length. A verified runtime smoke run -against the standard DOS utility files produced: - -```text -AFP Open File Fork path=SYS:PUBLIC/pmdflts.ini handle=1 fork=0 access=0x01 fork_len=8161 -AFP Open File Fork path=SYS:PUBLIC/pmdflts.ini entry_id=0x32d8e6b2 handle=1 fork=0 access=0x01 fork_len=8161 entry-id-only -AFP Open File Fork path=SYS:PUBLIC/ohlogscr.bat handle=1 fork=0 access=0x01 fork_len=1296 -``` - -The matching server log records the path-backed open and the same data-fork -lengths: - -```text -AFP Open File Fork: vol=0 request_vol=0 entry=0x00000000 fork=0 access=0x01 path='SYS:PUBLIC/pmdflts.ini' handle=1 fork_len=8161 -AFP Open File Fork: vol=0 request_vol=0 entry=0x00000000 fork=0 access=0x01 path='SYS:PUBLIC/ohlogscr.bat' handle=1 fork_len=1296 -``` - -The exact handle number is connection-local and must not be reused across -processes. The exact `fork_len` depends on the backing file contents. -Write access is tested on a temporary file created through AFP Create File and -then removed with AFP Delete: - -```sh -./tests/afp/afp_create_file_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit -./tests/afp/afp_open_file_fork_smoke --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit -./tests/afp/afp_open_file_fork_smoke --entry-id-only --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit -./tests/afp/afp_delete_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit -``` - -Resource-fork opens (`--fork 1`) remain unsupported until AppleDouble/resource -fork support exists. The smoke helper can assert the resource-fork rejection -explicitly: - -```sh -./tests/afp/afp_open_file_fork_smoke --expect-completion 0x9c --fork 1 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini -``` - -A rejected resource-fork probe should print: - -```text -AFP Open File Fork returned expected completion 0x9c: path=SYS:PUBLIC/pmdflts.ini fork=1 access=0x01 -``` - -If the server was built without xattr support, use -`--allow-invalid-namespace` for the expected negative test. Use -`--allow-invalid-path` for path-resolution or Entry-ID-only negative tests. - -## AFP File Information smoke test - -`afp_file_info_smoke` sends the WebSDK-documented NetWare AFP file -information requests: - -```text -NCP 0x2222/35/05 AFP Get File Information -NCP 0x2222/35/15 AFP 2.0 Get File Information -``` - -It uses the same libncp `NWRequestSimple()` transport path as the Entry ID -smoke test and sends raw `SYS:`-style path requests with directory handle 0. -The server replies with the read-only AFP file information record currently -implemented by mars_nwe: Entry ID, Parent ID, attributes, data/resource fork -lengths, offspring count, fixed long/short names, and access rights. - - -### AFP Get/Scan AccessPrivileges smoke - -AFP Get File Information and AFP Scan File Information now derive the -`AccessPrivileges` word from mars_nwe trustee/effective-rights state instead -of returning the old static compatibility masks. The WebSDK AccessPrivileges -bits exposed by the smoke helpers are: - -```text -0x0100 Read -0x0200 Write -0x0400 Open -0x0800 Create -0x1000 Delete -0x2000 Parental -0x4000 Search -0x8000 Modify File Status Flags -``` - -The AFP smoke suite exercises this with the same readonly trustee setup used by -the metadata negative tests. With `--readonly-user NOPASSUSER`, -`--readonly-no-password`, and `--prepare-readonly-rights`, the suite grants -`[RF]` on `SYS:PUBLIC/pmdflts.ini` and verifies that Get File Information -reports readable/openable rights while write and modify-status rights are not -set: - -```sh -./afp_file_info_smoke \ - --expect-rights-set 0x0100 \ - --expect-rights-clear 0x9200 \ - -S MARS -U NOPASSUSER -n \ - SYS:PUBLIC/pmdflts.ini -``` - -Runtime status: the trustee-derived AccessPrivileges smoke run is verified -with `failures=0`. The report showed the Supervisor Get/Scan replies returning -`rights=0x9f00`, while the readonly `NOPASSUSER` probe returned `rights=0x0500` -under the temporary `[RF]` trustee assignment and satisfied the set/clear mask -checks. - -Useful smoke cases for a standard MARS-NWE `SYS` volume are: - -```sh -./tests/afp/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS: -./tests/afp/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM -./tests/afp/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:BURST - -# AFP 2.0 variant using the same path-backed read-only reply -./tests/afp/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS: -./tests/afp/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:SYSTEM -./tests/afp/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:BURST -``` - -The AFP 2.0 mode is selected with `--afp20`. It has been verified against -the same `SYS:`, `SYS:PUBLIC`, `SYS:SYSTEM`, and `SYS:BURST` paths and -currently exercises the same path-backed read-only reply as the older call. -The current implementation fills fields from the existing mars_nwe sources of -truth where possible: NetWare attributes, namespace/basehandle Entry IDs, -trustee-derived AccessPrivileges, Unix file sizes and timestamps, and optional -AFP metadata such as FinderInfo. Parent ID refinements and fuller resource-fork semantics remain future -Mac-namespace work. - -If the server was built without xattr support, use -`--allow-invalid-namespace` for the expected negative test. Use -`--allow-invalid-path` for path-resolution negative tests. - -## AFP Scan File Information smoke test - -`afp_scan_info_smoke` sends the WebSDK-documented NetWare AFP scan requests: - -```text -NCP 0x2222/35/10 AFP Scan File Information -NCP 0x2222/35/17 AFP 2.0 Scan File Information -``` - -The helper defaults to the AFP 2.0 subfunction (`0x11`) and uses `--afp10` -to exercise the older `0x0a` endpoint. Both variants include the documented -DesiredResponseCount word; mars_nwe currently returns one read-only entry per -request, using the same AFP file information record as `afp_file_info_smoke`. -The test sends raw `SYS:`-style path requests with directory handle 0, and -`--entry-id-only` first resolves the directory path to an AFP directory entry ID -and then sends the scan request with `path_len=0`. Both forms use the returned -`next_last_seen` AFP Entry ID as the continuation token for the next call. - -Useful smoke sequence for a standard MARS-NWE `SYS:PUBLIC` directory: - -```sh -./tests/afp/afp_scan_info_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_scan_info_smoke --afp10 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_scan_info_smoke --entry-id-only -S MARS -U SUPERVISOR -P secret SYS:PUBLIC -./tests/afp/afp_scan_info_smoke -S MARS -U SUPERVISOR -P secret --last-seen 0x260437f6 SYS:PUBLIC -./tests/afp/afp_scan_info_smoke -S MARS -U SUPERVISOR -P secret --last-seen 0x6686342b SYS:PUBLIC -``` - -Verified runtime output from the sample `SYS:PUBLIC` tree after Entry IDs were -moved onto the existing mars_nwe namespace/basehandle path: - -```text -AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x00000000 desired=1 next_last_seen=0x00000004 entry_id=0x00000004 parent_id=0x00000000 attrs=0x2000 data_len=44424 resource_len=0 offspring=0 long_name=debug.exe short_name=debug.exe rights=0x9f00 -AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x00000000 desired=1 next_last_seen=0x00000004 entry_id=0x00000004 parent_id=0x00000000 attrs=0x2000 data_len=44424 resource_len=0 offspring=0 long_name=debug.exe short_name=debug.exe rights=0x9f00 entry-id-only -``` - -The AFP entry-id xattr remains a compatibility/cache location rather than the -source of truth. A final xattr dump from the same run showed the cached value -matching the namespace-derived ID: - -```text -user.org.mars-nwe.afp.entry-id=0x0100000000000004 -``` - -If that cache write is rejected, the server logs it and continues because the -namespace/basehandle mapping can still resolve the object. - -The scan continuation order is deliberately documented as server directory -iteration order, not numeric Entry-ID order. `last_seen` identifies the Entry -ID that was returned by the previous scan call so mars_nwe can skip entries -until that object is seen and then return the next directory entry. Therefore -the next returned Entry ID is not guaranteed to be numerically greater than the -`last_seen` value; in the verified run, `last_seen=0x6686342b` returns -`pmail.bat` with `entry_id=0x2d12d99c`. The older `0x0a` path intentionally -shares the same conservative scan implementation so older AFP callers can probe -the same read-only directory listing semantics before fuller multi-response and -CNID-backed scans are implemented. - -If the server was built without xattr support, use -`--allow-invalid-namespace` for the expected negative test. Use -`--allow-invalid-path` for path-resolution negative tests, and `--allow-empty` -when a scan continuation is expected to reach the end of the directory. - - -### AFP Get/Scan WebSDK layout smoke - -The AFP file-information helpers now verify the WebSDK/NWAFP wire layouts for -Get File Information and Scan File Information. The legacy Get File -Information reply (`0x05`) is a single 114-byte AFP file-information record. -The AFP 2.0 Get File Information reply (`0x0f`) is a 120-byte record that -includes the trailing ProDOSInfo field. - -Scan replies use the documented WebSDK shape rather than the old mars_nwe -smoke-only layout: a two-byte ActualResponseCount field at offset 0 followed by -the first AFP file-information record at offset 2. The legacy Scan File -Information reply therefore has a 116-byte payload for one result, while the AFP -2.0 Scan File Information reply has a 122-byte payload for one result. The -helper rejects the old layout where the Entry ID appeared directly at offset 0. - -Verified runtime output from the WebSDK-layout smoke run: - -```text -AFP File Info subfunction=0x05 path=SYS:PUBLIC/pmdflts.ini entry_id=0x3003df97 parent_id=0x00000000 attrs=0x2000 data_len=8161 resource_len=0 offspring=0 long_name=pmdflts.ini short_name=pmdflts.ini rights=0x9f00 reply_len=114 -AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x00000000 desired=1 actual_count=1 next_last_seen=0x00000004 entry_id=0x00000004 parent_id=0x00000000 attrs=0x2000 data_len=44424 resource_len=0 offspring=0 long_name=debug.exe short_name=debug.exe rights=0x9f00 reply_len=122 -``` - -Server diagnostics from the same run show the normalized scan response count in -addition to the first returned Entry ID: - -```text -AFP 2.0 Scan File Information: vol=0 entry=0x00000000 last=0x00000000 desired=1 mask=0xffff req=0xffff path='SYS:PUBLIC' count=1 reply_entry=0x00000004 -``` - -Runtime status: the full AFP smoke suite verified WebSDK Get/Scan layouts, -including `reply_len=114` for legacy Get File Information and `actual_count=1` -plus `reply_len=122` for AFP 2.0 Scan File Information, then ended with -`failures=0`. - - -## AFP Set File Information metadata smoke test - -`afp_set_file_info_smoke` sends the WebSDK-documented NetWare AFP Set File -Information requests. It defaults to the AFP 2.0 subfunction and can exercise -the older AFP subfunction with `--afp09`: - -```text -NCP 0x2222/35/09 AFP Set File Information -NCP 0x2222/35/16 AFP 2.0 Set File Information -``` - -The helper now always sends the fixed WebSDK/NWAFP Set File Information packet -layout; the earlier compact smoke-test layout is intentionally gone. Legacy -`0x09` requests put `path_len` at offset 54. AFP 2.0 `0x10` requests include -ProDOSInfo at offsets 54..59 and put `path_len` at offset 60. The smoke covers -the deliberately narrow write-safe metadata subsets currently implemented by -mars_nwe: FinderInfo (`0x4000`), Attributes (`0x0100`) for Hidden/System/Archive, -Access Date/Time (`0x0400`), Create Date/Time (`0x0800`), Modify Date/Time -(`0x1000`), and Backup Date/Time (`0x2000`). It sends path-backed raw -`VOL:`-style requests, writes the 32-byte FinderInfo block to mars_nwe's private -`org.mars-nwe.afp.finder-info` metadata key, writes AFP-only Hidden/System bits -to `org.mars-nwe.afp.attributes`, maps Archive through the existing NetWare -attribute store, routes Create and Backup Date/Time through the existing -mars_nwe NetWare metadata helpers, routes Access through the normal file -`atime` path, routes modification timestamp writes through the existing NetWare -timestamp helper, and immediately verifies the updates through AFP 2.0 Get File -Information. On Linux the source-level `org.mars-nwe.afp.*` name is stored via -the portable `user.` xattr namespace by mars_nwe's local xattr wrapper, the same -same extended-attribute naming pattern used by Apple metadata backends. - -Example: - -```sh -./tests/afp/afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --type TEXT --creator MARS \ - SYS:PUBLIC/pmdflts.ini -``` - - - -The same FinderInfo payload can be sent through the older Set File Information -subfunction: - -```sh -./tests/afp/afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --afp09 --finder-info-only --type TEXT --creator MARS \ - SYS:PUBLIC/pmdflts.ini -``` - -Verified runtime output from the WebSDK-layout-only smoke run: - -```text -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x4000 attrs=0x2000 create=0x5cbe access=0x5cbe modify=0x5cbeb52a backup=0x00000000 finder_type=TEXT finder_creator=MARS entry_id=0x42ede1c8 verified -AFP Set File Info subfunction=0x09 path=SYS:PUBLIC/pmdflts.ini bitmap=0x4000 attrs=0x2000 create=0x5cbe access=0x5cbe modify=0x5cbeb52a backup=0x00000000 finder_type=TEXT finder_creator=MARS entry_id=0x42ede1c8 verified -``` - -Server diagnostics show the effective resolved volume, the request volume byte, -the WebSDK layout, the FinderInfo bitmap, and follow-up Get File Information -verification with the same AFP file Entry ID: - -```text -AFP 2.0 Set File Information: vol=0 request_vol=0 entry=0x00000000 mask=0x4000 path='SYS:PUBLIC/pmdflts.ini' finder_info attrs=0x0000 atime=-1 mtime=-1 -AFP Set File Information: vol=0 request_vol=0 entry=0x00000000 mask=0x4000 path='SYS:PUBLIC/pmdflts.ini' finder_info attrs=0x0000 atime=-1 mtime=-1 -AFP 2.0 Get File Information: vol=0 entry=0x00000000 mask=0xffff path='SYS:PUBLIC/pmdflts.ini' reply_entry=0x42ede1c8 -``` - -Linux xattr checks for the FinderInfo and cached Entry ID look like this: - -```sh -getfattr -n user.org.mars-nwe.afp.finder-info -e hex /var/mars_nwe/SYS/public/pmdflts.ini -getfattr -n user.org.mars-nwe.afp.entry-id -e hex /var/mars_nwe/SYS/public/pmdflts.ini -``` - -For the verified FinderInfo smoke run, the FinderInfo xattr starts with -`TEXTMARS`: - -```text -user.org.mars-nwe.afp.finder-info=0x544558544d415253000000000000000000000000000000000000000000000000 -``` - -AFP Hidden is tested through the same NetWare attribute path as System and -Archive: - -```sh -./tests/afp/afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --attributes-only --hidden \ - SYS:PUBLIC/pmdflts.ini -``` - -The helper also exercises the two additional file attribute bits. Hidden, -System, and Archive are routed through the existing NetWare file attribute store -rather than the AFP metadata xattr: - -```sh -./tests/afp/afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --attributes-only --system \ - SYS:PUBLIC/pmdflts.ini - -./tests/afp/afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --attributes-only --archive \ - SYS:PUBLIC/pmdflts.ini -``` - -System and Archive are verified through AFP Get File Information after updating -the NetWare attribute store; they do not live in -`user.org.mars-nwe.afp.attributes`. Use `--clear-system` -and `--clear-archive` to remove those bits. - - -Runtime status: the latest full AFP smoke suite verified that every Set File -Information probe uses the WebSDK request shape and that the compact mars_nwe-only packet -shape is no longer accepted by the helper. The verified run covered AFP 2.0 -and legacy FinderInfo, Hidden set/clear, System set/clear, Archive set/clear, -Access Date/Time, Create Date/Time, Modify Date/Time, Backup Date/Time, and the -NOPASSUSER Modify-rights negative probes, then ended with `failures=0`. -The older helper spellings `--backup` and `--clear-backup` remain accepted -as compatibility aliases, but the suite and documentation use Archive because -this bit is the AFP file attribute, not the separate AFP backup date/time field. -The helper verifies only the bit that a single probe sets or clears. Other -stored metadata bits are intentionally preserved, so a run can legitimately -report a combined attribute word while verifying only the targeted bit. - -Verified runtime probes for the additional bits showed the expected AFP-visible -attribute words and server diagnostics. A later full-suite run also confirmed -that the helper must mask the targeted bit rather than compare the whole -attribute word: previously stored metadata bits can remain visible, so Hidden set/clear may -produce a combined attribute word while still being correct for the Invisible -bit. After Archive was mapped to the existing NetWare `FILE_ATTR_A` store, the -suite was rerun from the build-tree helper and completed with `failures=0`: -Archive set reported AFP-visible `attrs=0x2000`, Clear Archive reported -`attrs=0x0000`, and the final AFP metadata xattr stayed at `0x01000000`, proving -that Archive no longer lives in `user.org.mars-nwe.afp.attributes`. - -```text -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x0004 finder_type=TEXT finder_creator=MARS entry_id=0x62ecb463 verified -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x0000 finder_type=TEXT finder_creator=MARS entry_id=0x62ecb463 verified -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x2000 finder_type=TEXT finder_creator=MARS entry_id=0x62ecb463 verified -AFP 2.0 Set File Information: vol=0 request_vol=0 entry=0x00000000 mask=0x0001 path='SYS:PUBLIC/pmdflts.ini' attributes attrs=0x8004 -AFP 2.0 Set File Information: vol=0 request_vol=0 entry=0x00000000 mask=0x0001 path='SYS:PUBLIC/pmdflts.ini' attributes attrs=0x0004 -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x2000 modify=0x576eb9aa finder_type=TEXT finder_creator=MARS entry_id=0x399193ed verified -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0001 attrs=0x0000 modify=0x576eb9aa finder_type=TEXT finder_creator=MARS entry_id=0x399193ed verified -user.org.mars-nwe.afp.attributes=0x01000000 -AFP 2.0 Set File Information: vol=0 request_vol=0 entry=0x00000000 mask=0x0001 path='SYS:PUBLIC/pmdflts.ini' attributes attrs=0x8040 -``` - -Modification timestamp writes are deliberately routed through the existing -NetWare `nw_utime_node()` path so trustee Modify rights and the established -`utime(2)` fallback behavior stay shared with classic NCP timestamp updates. -The first timestamp smoke uses a fixed Unix epoch that the helper converts into -the AFP/NW DOS date+time fields and verifies through the follow-up Get File -Information response: - -```sh -./tests/afp/afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --timestamp-only --mtime-epoch 1700000000 \ - SYS:PUBLIC/pmdflts.ini - -stat -c 'mtime_epoch=%Y mtime=%y' /var/mars_nwe/SYS/public/pmdflts.ini -``` - -This currently remains file-only and path-backed, just like the FinderInfo and -metadata-attribute Set File Information probes; directory timestamps and -Entry-ID-only Set File Information are left for later resolver work. - -The legacy `0x09` endpoint is deliberately routed through the same narrow -implementation as AFP 2.0 `0x10`; it does not add create, rename, delete, -directory timestamp, or fork-write semantics. - -All other Set File Information bitmap bits and AFP attribute bits, including -NoWrite, NoRename, NoDelete, NoCopy, and the computed data/resource-fork-open -flags, are intentionally rejected for now. That keeps unsupported AFP-only -flags, resource-fork semantics, and unimplemented write modes out of this -conservative smoke path. - -If the server was built without xattr support, use -`--allow-invalid-namespace` for the expected negative test. Use -`--allow-invalid-path` for path-resolution negative tests. - -## AFP Get DOS Name From Entry ID smoke test - -`afp_dos_name_smoke` exercises the WebSDK-documented NetWare AFP reverse -lookup: - -```text -NCP 0x2222/35/18 AFP Get DOS Name From Entry ID -``` - -The request carries the AFP volume number and a 32-bit Macintosh directory -entry ID. The reply is a one-byte DOS path length followed by the DOS path -string for the matching entry. The smoke helper first resolves the supplied -`VOL:PATH` through AFP Get Entry ID From Path Name when `--entry-id` is not -provided, then calls AFP Get DOS Name From Entry ID and verifies that the -returned path matches the existing mars_nwe DOS namespace spelling without the -volume prefix. - -Example: - -```sh -./afp_dos_name_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini -``` - -Expected output shape: - -```text -AFP Get DOS Name From Entry ID volume=0 entry_id=0x00000004 path=PUBLIC/PMDFLTS.INI verified -``` - -A successful post-fix smoke-suite run confirmed the `namedos.c` alias path via -the existing mars_nwe namespace/basehandle resolver: - -```text -AFP Get DOS Name From Entry ID volume=0 entry_id=0x00000004 path=PUBLIC/PMDFLTS.INI verified -AFP Get DOS Name From Entry ID: vol=0 entry=0x00000004 path='PUBLIC/PMDFLTS.INI' -``` - -This intentionally returns DOS namespace spelling rather than the raw Unix -realcase path (`public/pmdflts.ini`). - -The server implementation deliberately reuses existing mars_nwe namespace -machinery. It first resolves the AFP Entry ID through -`map_directory_number_to_path()` and falls back to the old AFP metadata volume -walk only for legacy cached IDs. It does not create new AFP IDs while walking -the volume, keeping the reverse lookup read-only and avoiding a parallel AFP -path database. - -### AFP attribute bit alignment with WebSDK - -AFP Set/Get File Information now uses the documented WebSDK bitmaps for the -SetInfo request and for the returned attribute word. SetInfo request bits are -`0x0100` for Attributes, `0x1000` for Modify Date/Time, and `0x4000` for -FinderInfo. The attribute word maps `Hidden` (`0x0200`) to NetWare -`FILE_ATTR_H`, `System` (`0x0400`) to `FILE_ATTR_S`, and `Archive` (`0x2000`) -to `FILE_ATTR_A`. The smoke helper keeps `--hidden` as a compatibility -alias, but the suite uses `--hidden` / `--clear-hidden` because the value is the -NetWare/AFP Hidden attribute, not a separate Finder-only xattr. - -Runtime status: the WebSDK attribute-bit smoke run is verified with -`failures=0`. The suite confirmed Hidden set/clear as `0x0200`/`0x0000`, -System set/clear as `0x0400`/`0x0000`, and Archive set/clear as -`0x2000`/`0x0000`. It also confirms the SetInfo request bitmaps `0x0100` -Attributes, `0x1000` Modify Date/Time, and `0x4000` FinderInfo. Since these -attributes now use the existing NetWare attribute path, the final -`user.org.mars-nwe.afp.attributes` dump is optional and may report `ENODATA`; -that is expected when no AFP-only attribute bits remain set. - -### AFP Backup Date/Time smoke - -`afp_set_file_info_smoke` supports the WebSDK Backup Date/Time request bitmap -`0x2000` via: - -```sh -./afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --backup-time-only --backup-time-epoch 1700000000 \ - SYS:PUBLIC/pmdflts.ini -``` - -This is intentionally separate from the Archive attribute bit in the Attributes -word. The server stores Backup Date/Time through `nwarchive.c` and the Linux -suite dumps the corresponding xattr as: - -```sh -getfattr -n user.org.mars-nwe.netware.archive -e hex /var/mars_nwe/SYS/public/pmdflts.ini -``` - -The expected AFP reply shows the same Backup Date/Time at offsets 28/30 of the -120-byte file information record, while Archive/Hidden/System attributes remain -mapped through the normal NetWare attribute store. - -Runtime status: the Backup Date/Time smoke run is verified with `failures=0`. -The report showed `bitmap=0x2000`, `backup=0x576eb9aa`, and the backing xattr -`user.org.mars-nwe.netware.archive=0x01036e57aab900000000` after setting epoch -`1700000000`. - - -### AFP Access Date/Time smoke - -`afp_set_file_info_smoke` supports the WebSDK Access Date/Time request bitmap -`0x0400` via: - -```sh -./afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --access-time-only --access-time-epoch 1700000000 \ - SYS:PUBLIC/pmdflts.ini -``` - -The server stores Access Date/Time through the existing POSIX `st_atime` path, -preserving `st_mtime` with `utime()` and enforcing trustee Modify rights before -changing the timestamp. The AFP file-information record exposes the Access -Date at offset 22; no AFP-specific xattr is added for this NetWare-semantic -timestamp. - -Runtime status: the Access Date/Time smoke run is verified with `failures=0`. -The report showed `bitmap=0x0400`, `access=0x576e`, and no AFP-only xattr for -this timestamp after setting epoch `1700000000`. - -### AFP Create Date/Time smoke - -`afp_set_file_info_smoke` supports the WebSDK Create Date/Time request bitmap -`0x0800` via: - -```sh -./afp_set_file_info_smoke \ - -S MARS -U SUPERVISOR -P secret \ - --create-time-only --create-time-epoch 1700000000 \ - SYS:PUBLIC/pmdflts.ini -``` - -The server stores Create Date/Time through `nwarchive.c` file-info metadata -(`user.org.mars-nwe.netware.fileinfo`) using `mars_nwe_set_file_info()`. The -AFP file-information record exposes the Create Date at offset 20; the matching -Create Time is still persisted in the shared NetWare file-info metadata so -classic NetWare file-info paths can return it. No AFP-specific xattr is added -for this NetWare-semantic timestamp. - -Runtime status: the Create Date/Time smoke run is verified with `failures=0`. -The report showed `bitmap=0x0800`, `create=0x576e`, and the backing xattr -`user.org.mars-nwe.netware.fileinfo=0x01036e57aab90000000000000000` after -setting epoch `1700000000`. - -## AFP Open File Fork write smoke success - -AFP Open File Fork now routes data-fork write opens through the existing -NetWare open/share path instead of rejecting write access at the AFP wrapper -layer. - -Verified smoke run: - -```text -AFP Open File Fork write create file -AFP Create File subfunction=0x02 path=SYS:PUBLIC/atst2 parent=SYS:PUBLIC leaf=atst2 parent_entry_id=0x00000004 entry_id=0x1a81505a verified - -AFP Open File Fork write access -AFP Open File Fork path=SYS:PUBLIC/atst2 handle=1 fork=0 access=0x02 fork_len=0 - -AFP Open File Fork write cleanup -AFP Delete path=SYS:PUBLIC/atst2 parent=SYS:PUBLIC leaf=atst2 parent_entry_id=0x00000004 verified - -AFP Open File Fork resource fork rejected -AFP Open File Fork returned expected completion 0x9c: path=SYS:PUBLIC/pmdflts.ini fork=1 access=0x01 - -Summary: failures=0 -``` - -The positive write-open smoke only covers the AFP data fork. AFP resource -fork opens are still intentionally rejected with completion 0x9c. - -## AFP Open File Fork entry-id smoke success - -AFP Open File Fork now accepts entry-id-only data-fork opens for regular files -whose AFP ID is present in the nwatalk AFP entry-id metadata. The server -reverse-resolves file AFP IDs through the AFP/nwatalk metadata cache instead of -using DOS namespace directory numbers, then opens the resolved file through the -same NetWare open/share path used by path-backed AFP Open File Fork. - -Verified smoke run after the WebSDK Get Entry ID From Path Name helper-layout -fix: - -```text -AFP Open File Fork by Entry ID -AFP Open File Fork path=SYS:PUBLIC/pmdflts.ini entry_id=0x59f0e25b handle=1 fork=0 access=0x01 fork_len=8161 entry-id-only - -AFP Open File Fork write access by Entry ID -AFP Open File Fork path=SYS:PUBLIC/atst2 entry_id=0x262fd43e handle=1 fork=0 access=0x02 fork_len=0 entry-id-only - -AFP Open File Fork resource fork rejected -AFP Open File Fork returned expected completion 0x9c: path=SYS:PUBLIC/pmdflts.ini entry_id=0x00000000 fork=1 access=0x01 - -Summary: failures=0 -``` - -This keeps the current policy split intact: directory Entry IDs may use the -existing namespace/basehandle mapping, while regular file Entry IDs are resolved -through AFP metadata only. Resource forks remain unsupported and continue to -return completion 0x9c. - -## Standalone AFP xattr backend smoke success - -The AFP metadata backend no longer depends on Netatalk/libatalk for the -currently implemented metadata. After the standalone mars_nwe xattr backend -change, the full AFP smoke suite was rerun against `SYS:PUBLIC/pmdflts.ini` -and completed successfully. - -Verified smoke run: - -```text -AFP Entry ID From Path Name -AFP Entry ID path=SYS:PUBLIC/pmdflts.ini request_path=SYS:PUBLIC/pmdflts.ini dir_handle=0 entry_id=0x05811b2a (92347178) - -AFP Open File Fork by Entry ID -AFP Open File Fork path=SYS:PUBLIC/pmdflts.ini entry_id=0x05811b2a handle=1 fork=0 access=0x01 fork_len=8161 entry-id-only - -AFP Open File Fork write access by Entry ID -AFP Open File Fork path=SYS:PUBLIC/atst2 entry_id=0x145f0c8a handle=1 fork=0 access=0x02 fork_len=0 entry-id-only - -Linux xattr: AFP FinderInfo -user.org.mars-nwe.afp.finder-info=0x544558544d415253000000000000000000000000000000000000000000000000 - -Linux xattr: AFP Entry ID -user.org.mars-nwe.afp.entry-id=0x0100000005811b2a - -AFP-only attributes xattr is absent; this is expected when the tested Hidden/System/Archive bits are stored through the NetWare attribute path. - -Summary: failures=0 -``` - -This confirms that Entry ID and FinderInfo metadata are served from the -mars_nwe-owned AFP xattrs, while Hidden/System/Archive attributes and the -timestamp fields continue to use the existing NetWare metadata paths. AFP -remains behind the existing build configuration; this smoke only records that -the implemented AFP endpoints no longer need a Netatalk/libatalk metadata -backend when xattr support is enabled. - -## AFP metadata entry-id resolver smoke success - -AFP metadata endpoints now share the same file Entry ID resolver that was first -used by AFP Open File Fork. Regular file Entry IDs are reverse-resolved through -the mars_nwe AFP entry-id xattr metadata and then routed back into the normal -NetWare path-based helpers. They are not interpreted as DOS namespace directory -numbers. - -Verified smoke run: - -```text -AFP Get File Information by Entry ID -AFP File Info subfunction=0x05 path=SYS:PUBLIC/pmdflts.ini entry_id=0x05aaace0 parent_id=0x00000000 attrs=0x2000 data_len=8161 resource_len=0 offspring=0 long_name=PMDFLTS.INI short_name=PMDFLTS.INI rights=0x9f00 reply_len=114 entry-id-only - -AFP Set File Information FinderInfo by Entry ID -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x4000 attrs=0x2000 create=0x5cbf access=0x5cbf modify=0x5cbf35da backup=0x00000000 finder_type=TEXT finder_creator=MARS entry_id=0x05aaace0 verified entry-id-only - -AFP Set File Information Hidden by Entry ID -AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0100 attrs=0x2200 create=0x5cbf access=0x5cbf modify=0x5cbf35da backup=0x00000000 finder_type=TEXT finder_creator=MARS entry_id=0x05aaace0 verified entry-id-only - -Summary: failures=0 -``` - -The server log for the same run shows the shared resolver feeding normal -metadata operations with a resolved AFP/NetWare path: - -```text -AFP Get File Information: vol=0 entry=0x05aaace0 mask=0xffff path='SYS:PUBLIC/PMDFLTS.INI' reply_entry=0x05aaace0 -AFP 2.0 Set File Information: vol=0 request_vol=0 entry=0x05aaace0 mask=0x4000 path='SYS:PUBLIC/PMDFLTS.INI' finder_info attrs=0x0000 atime=-1 mtime=-1 -AFP 2.0 Set File Information: vol=0 request_vol=0 entry=0x05aaace0 mask=0x0100 path='SYS:PUBLIC/PMDFLTS.INI' attributes attrs=0x8200 atime=-1 mtime=-1 -``` - -Directory Entry IDs continue to use the existing namespace/basehandle path. -Regular file Entry IDs continue to be AFP metadata IDs stored in -`user.org.mars-nwe.afp.entry-id`. - -## AFP File Fork I/O smoke success - -AFP Open File Fork returns a normal mars_nwe/NetWare file handle for the data -fork. The AFP smoke suite now verifies that the returned handle works with the -existing NetWare read/write paths rather than introducing any AFP-private POSIX -I/O path. - -Verified smoke run: - -```text -AFP File Fork Read via NetWare handle -AFP File Fork I/O read path=SYS:PUBLIC/pmdflts.ini entry_id=0x00000000 offset=0 requested=16 read=16 fork_len=8161 - -AFP File Fork Read by Entry ID via NetWare handle -AFP File Fork I/O read path=SYS:PUBLIC/pmdflts.ini entry_id=0x05aaace0 offset=0 requested=16 read=16 fork_len=8161 entry-id-only - -AFP File Fork Write via NetWare handle -AFP File Fork I/O write path=SYS:PUBLIC/atst3 entry_id=0x00000000 offset=0 size=8 verified - -AFP File Fork Write by Entry ID via NetWare handle -AFP File Fork I/O write path=SYS:PUBLIC/atst3 entry_id=0x0edc380e offset=0 size=11 verified entry-id-only - -AFP File Fork Close rejects old read handle -AFP File Fork I/O read path=SYS:PUBLIC/pmdflts.ini entry_id=0x00000000 offset=0 requested=16 read=16 fork_len=8161 close-verified - -AFP File Fork Close rejects old write handle -AFP File Fork I/O write path=SYS:PUBLIC/atst3 entry_id=0x00000000 offset=0 size=14 verified close-verified - -Summary: failures=0 -``` - -The write probes create the test file through AFP Create File, open the data -fork through AFP Open File Fork, perform I/O through the existing NetWare file -handle read/write operations, and clean up through AFP Delete. The close -lifecycle probes close the returned handle through the normal NetWare Close File -NCP and verify that later reads or writes on the old handle fail. Resource Fork -opens remain covered by the separate negative smoke and continue to return the -unsupported completion. - - -## AFP File Fork Close lifecycle smoke success - -AFP does not need a separate close-fork endpoint in the current compatibility -slice. AFP Open File Fork returns a normal mars_nwe/NetWare file handle, and -that handle is closed through the existing `NCP 0x42 Close File` implementation -which routes to `nw_close_file()`. - -Verified smoke run: - -```text -AFP File Fork Close rejects old read handle -AFP File Fork I/O read path=SYS:PUBLIC/pmdflts.ini entry_id=0x00000000 offset=0 requested=16 read=16 fork_len=8161 close-verified - -AFP File Fork Close rejects old write handle -AFP File Fork I/O write path=SYS:PUBLIC/atst3 entry_id=0x00000000 offset=0 size=14 verified close-verified - -Summary: failures=0 -``` - -The helper verifies the close by attempting I/O with the old handle after the -close call and requiring that operation to fail. This confirms that the AFP -fork handle participates in the normal mars_nwe handle lifecycle instead of -requiring an AFP-specific close path. - -## AFP Entry ID From Name entry-id-relative smoke success - -AFP Get Entry ID From Name supports both the path-backed smoke form and the -WebSDK entry-id-relative form. In the entry-id-relative form the client sends a -directory Entry ID as the base and a leaf name without a `VOL:` prefix. The -server resolves the base through the mars_nwe directory namespace/basehandle -path, builds a normal NetWare path, and then uses the existing mars_nwe path and -AFP metadata helpers to return the target Entry ID. - -Verified smoke run: - -```text -AFP Entry ID From Path Name -AFP Entry ID path=SYS:PUBLIC/pmdflts.ini request_path=SYS:PUBLIC/pmdflts.ini dir_handle=0 base_entry_id=0x00000000 entry_id=0x76474c35 (1984384053) - -AFP Entry ID From NetWare Handle -AFP Entry ID From NetWare Handle path=SYS:PUBLIC/pmdflts.ini volume=0 entry_id=0x76474c35 (1984384053) fork=0 - -AFP Entry ID From Name relative to Entry ID -AFP Entry ID path=pmdflts.ini request_path=pmdflts.ini dir_handle=0 base_entry_id=0x00000004 entry_id=0x76474c35 (1984384053) entry-id-relative - -Summary: failures=0 -``` - -The server log for the same run confirms that base directory Entry ID -`0x00000004` is resolved to `SYS:PUBLIC`, then the relative name is resolved to -the file AFP Entry ID: - -```text -AFP Get Entry ID From Path Name: dh=0 path='SYS:PUBLIC' entry=0x00000004 -AFP Get Entry ID From Name: vol=0 entry=0x00000004 path='pmdflts.ini' reply_entry=0x76474c35 entry-id -``` - -Directory Entry IDs remain namespace/basehandle IDs. Regular file Entry IDs -remain AFP metadata IDs stored in `user.org.mars-nwe.afp.entry-id` and are not -accepted as directory bases. - - -## AFP timestamp plausibility smoke success - -The Set File Information smoke now verifies the AFP timestamp fields that are -actually selected by the request bitmap. Access, create, modify, and backup -timestamp updates are checked for plausible encoded date/time values, while -non-selected timestamp fields may still appear as `-1` in server debug output -because those fields were intentionally not supplied by that request. - -Verified smoke run: - -```text -AFP Set File Information Access Timestamp -AFP Set File Information Create Timestamp -AFP Set File Information Modify Timestamp -AFP Set File Information Backup Timestamp - -Linux stat: AFP Modify Timestamp -mtime_epoch=1700000000 mtime=2023-11-14 23:13:20.000000000 +0100 - -Linux stat: AFP Modify Timestamp exact epoch -mtime_epoch=1700000000 expected=1700000000 - -Summary: failures=0 -``` - -This keeps the debug-log convention intact: `atime=-1` or `mtime=-1` is allowed -when the corresponding timestamp bitmap bit is not present, but the smoke fails -if a timestamp request returns an implausible encoded value or if the final -modify-time epoch no longer matches the requested test value. - -## Final AFP endpoint audit TODO - -Before the AFP compatibility slice is marked complete, do one last audit of all -implemented AFP endpoints against the WebSDK archive and Novell NWAFP headers. -That final check should compare call numbers, request/reply layouts, legacy vs. -AFP 2.0 variants, bitmaps, record lengths, and unsupported completion codes for -the complete smoke-covered endpoint set. It should also audit every AFP handler -implementation and confirm that NetWare semantics still go through existing -mars_nwe functions/wrappers rather than AFP-local shortcuts. - -## AFP Get Macintosh Info On Deleted File smoke test - -`afp_deleted_info_smoke` covers the WebSDK / Novell AFP deleted-file call: - -```text -NCP 0x2222/35/19 AFP Get Macintosh Info On Deleted File -``` - -The request carries `VolumeNumber` and `DOSDirectoryNumber`. The server maps -that deleted directory number through the shared mars_nwe salvage/deleted-entry -backend and returns the FinderInfo snapshot, ProDOSInfo snapshot, the stored -resource fork size, and the deleted original filename. It does not open or -expose `.recycle` or `.salvage` as AFP-visible paths. - -The full AFP smoke suite pre-cleans deleted entries in the tested directory, -creates a temporary AFP file, writes FinderInfo through AFP Set File -Information, deletes the file through AFP Delete, verifies `0x13`, and purges -the tested salvage entry afterwards. +`--prepare-readonly-rights` uses `nwrevoke` and `nwgrant -r '[RF]'` to grant read +and file-scan rights without Modify. The negative probes verify that FinderInfo, +Hidden, and System metadata writes are rejected while the SUPERVISOR positive +path still succeeds. Use this only on smoke files or paths where removing an +explicit trustee assignment for the readonly test user is acceptable. + +## Completion status + +The AFP test group is complete for the current compatibility slice. Verified +runs have completed with `failures=0`, including FinderInfo/ProDOSInfo xattr +checks, AFP 35/19 deleted-file metadata, legacy and AFP 2.0 Set File Information, +Hidden/System/Archive mapping through the NetWare attribute path, and +Modify-rights negative coverage. + +Future AFP work should keep using this README as the single documentation entry +point for the test directory and should add new runtime checks to +`afp_smoke_suite.sh` rather than creating unrelated top-level smoke scripts. diff --git a/tests/afp/TODO.md b/tests/afp/TODO.md deleted file mode 100644 index 27f687d..0000000 --- a/tests/afp/TODO.md +++ /dev/null @@ -1,31 +0,0 @@ -# AFP TODO - -This file contains only AFP follow-up work that is still open. Smoke results, -endpoint inventory, WebSDK audit notes, and implementation history belong in the -neighboring AFP documentation files, not in the root `TODO.md`. - -## Remaining AFP work - -### `0x13 AFP Get Macintosh Info On Deleted File` - -Current status: - -- Implemented as a conservative adapter over the mars_nwe salvage/deleted-entry - backend. -- Covered by `afp_deleted_info_smoke` and the full `afp_smoke_suite.sh`. -- Does not scan or expose `.recycle` / `.salvage` as AFP-visible paths. - -Current AFP mapping: - -1. Resolve the deleted DOS directory entry through the mars_nwe salvage backend. -2. Return FinderInfo from the salvage JSON snapshot. -3. Return ProDOS information from the nwatalk xattr-backed Salvage JSON snapshot (`prodos_info_hex`). -4. Return resource fork size from the salvage JSON snapshot. -5. Return the deleted filename from the salvage/deleted-entry record. - -References: - -- `tests/afp/AFP_DELETED_FILE_INFO.md` -- `tests/afp/AFP_ENDPOINT_INVENTORY.md` -- `tests/afp/AFP_FINAL_AUDIT.md` -- `tests/afp/AFP_WEBSK_AUDIT_FINDINGS.md` diff --git a/tests/salvage/README.md b/tests/salvage/README.md index 818cc79..efe6bad 100644 --- a/tests/salvage/README.md +++ b/tests/salvage/README.md @@ -1,9 +1,15 @@ # Salvage smoke tests -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. +Status: finished for the current NetWare salvage compatibility slice. + +This directory contains the shared mars_nwe NetWare salvage/deleted-entry tests +and the single remaining salvage documentation file for this test group. The +previous backend-design notes have been folded into this README so the salvage +test directory has one Markdown entry point. + +The backend is shared by the NetWare NCP salvage calls and AFP `0x13` `Get +Macintosh Info On Deleted Files`. AFP must remain a thin adapter over this +backend and must not grow a parallel deleted-file scanner. ## Configuration covered by the tests @@ -24,27 +30,26 @@ The active salvage configuration block is intentionally in a low `nwserv.conf` / Section meanings: -- `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: +- `51` enables or 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`. + `kb`, `mb`, or `gb` units. - `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 +## Repository layout and metadata -With `k`/keeptree enabled, a deleted `SYS:PUBLIC/PMDFLTS.INI` owned by -`SUPERVISOR` is expected to produce: +With keeptree enabled, a deleted `SYS:PUBLIC/PMDFLTS.INI` owned by `SUPERVISOR` +is expected to produce: ```text SYS/.recycle/SUPERVISOR/PUBLIC/PMDFLTS.INI @@ -55,17 +60,13 @@ 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. -The sidecar JSON must preserve mars_nwe server metadata needed for exact -recover, including: - -- 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. - -`finder_info_hex` is serialized as a fixed 32-byte AFP FinderInfo block encoded -as 64 hex characters. If mars_nwe has no FinderInfo xattr for the deleted -object, the JSON stores the all-zero 32-byte FinderInfo value. +The sidecar JSON preserves the mars_nwe server metadata needed for exact +recover, including NetWare archive/fileinfo xattrs, AFP metadata from nwatalk +when present, inherited rights, explicit trustee object/right pairs, and the +selected recycle/salvage relative paths. `finder_info_hex` is serialized as a +fixed 32-byte AFP FinderInfo block encoded as 64 hex characters. If mars_nwe +has no FinderInfo xattr for the deleted object, the JSON stores the all-zero +32-byte FinderInfo value. ## Samba-compatible version naming @@ -85,27 +86,47 @@ 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. The `Copy #n of ...` prefix is intentionally not localized; it follows -Samba `vfs_recycle` literally and should be stable across server locales. +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 ...` +history 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 +## NCP endpoint scope -`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. +The current NetWare 3.x salvage slice is implemented and covered by the smoke +suite: -```sh -./tests/salvage/salvage_layout_smoke.sh +```text +NCP 0x2222 / 87 / 16 Scan Salvageable Files +NCP 0x2222 / 87 / 17 Recover Salvageable File +NCP 0x2222 / 87 / 18 Purge Salvageable File +NCP 0x2222 / 22 / 27 Scan Salvageable Files (old bridge) +NCP 0x2222 / 22 / 28 Recover Salvageable File (old bridge) +NCP 0x2222 / 22 / 29 Purge Salvageable File (old bridge) ``` -It verifies the basic `.recycle`/`.salvage` directory contract and stale JSON -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. -The combined smoke suite includes a manual pause that asks the tester to remove -the backend `.recycle` payload, then scans again and greps the server log for -`WARN SALVAGE 87/16 STALE`. +The old `22/*` calls remain thin adapters over the same shared backend. Later +NetWare 4.11+ list variants and UTF-8 extensions remain outside this 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 +``` + +## Stale metadata cleanup + +Samba or an administrator may delete files from `.recycle` while `.salvage` +metadata is still present. That is a real backend state, not a protocol error. +Runtime scans treat each `.salvage` JSON file as a sidecar for the matching +`.recycle` payload: if the payload is gone, `87/16` must not return the stale +entry and should remove the JSON. The server log should contain a greppable +line like: + +```text +WARN SALVAGE 87/16 STALE ... +``` ## NCP path visibility @@ -116,40 +137,37 @@ leading-dot path components as special `.`/`..` syntax and rejects names such as are also hidden by default in the NetWare attribute layer. Smoke tests therefore must not validate backend payloads by opening -`SYS:.recycle/...` or metadata by opening `SYS:.salvage/...` through normal NCP -file calls. The correct NCP-level checks are: +`SYS:.recycle/...` or `SYS:.salvage/...` through normal NCP file calls. The +correct NCP-level checks are: -- create/write/read the live file through NCP, -- delete the live file through NCP, -- use salvage scan `87/16` to confirm that a salvageable entry exists, -- use recover `87/17` or purge `87/18` on the scan sequence, +- create/write/read the live file through NCP; +- delete the live file through NCP; +- use salvage scan `87/16` to confirm that a salvageable entry exists; +- use recover `87/17` or purge `87/18` on the scan sequence; - read the restored live file through NCP to verify payload content. Local filesystem layout tests may still inspect `.recycle` and `.salvage` directly when they intentionally test backend layout and run with suitable Unix permissions. +## Local 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 smoke suite -`salvage_smoke_suite.sh` is the single integration check for the server-side -delete hook and Samba-compatible history behaviour. It uses `ncp_delete_smoke`, -a small libncp client, to create and delete the same NetWare path twice through -classic NetWare NCP file functions. - -The suite appends all current integration checks into one report: - -- first delete: verifies normal `.recycle` payload and `.salvage` JSON capture, -- second delete: verifies version naming with `Copy #1 of NAME` when option `53` - contains the `v` flag, -- NCP `0x2222 / 87 / 16`: scans the same directory through the official - `ncpfs` salvage scan API. The NCP reply reports the original deleted file - name, so the suite expects at least two entries named `SLVGCHK.TXT`; the - `Copy #1 of ...` history name is verified through the `.salvage` JSON path - above. - -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 payloads and JSON sidecars. +`salvage_smoke_suite.sh` is the single integration entry point for the +server-side delete hook, Samba-compatible history behaviour, scan, recover, +purge, hidden repository behaviour, stale sidecar cleanup, and report-file +generation. Example: @@ -162,28 +180,30 @@ Example: --out /tmp/mars-salvage-report.txt ``` -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. For -cleanest results, use a fresh test filename or run cleanup with the same Unix -account that owns the mars_nwe volume files. +The suite creates multiple versions of the same file through NCP, deletes them +through NCP, scans salvageable entries, verifies duplicate sequence entries for +the original deleted name, purges selected entries, recovers the oldest sequence, +and checks the restored payload through NCP read. Backend history paths remain +hidden behind the salvage endpoints. -The old split scripts `salvage_ncp_delete_smoke.sh` and -`salvage_ncp_history_smoke.sh` were replaced by this single suite so the salvage -flow is exercised like the AFP smoke suite: one entry point, one report, and one -summary. New salvage checks should be appended to this suite instead of growing -separate top-level smoke scripts. +The suite includes a manual stale-payload pause: it prints a `sudo rm -f` +command for the backend `.recycle` payload, then waits for Enter. Remove that +payload in a second shell and continue; the next scan should clean the stale +sidecar and the log grep should find `WARN SALVAGE 87/16 STALE`. -## Still to implement +Best-effort pre-clean of stale test artifacts may warn when existing `.recycle` +or `.salvage` files are owned by the server user or root. Those warnings do not +fail the smoke by themselves. For cleanest results, use a fresh test filename +or run cleanup with the same Unix account that owns the mars_nwe volume files. -Runtime endpoint tests should be appended as the NCP salvage calls are -implemented. The suite already covers the initial `NCP 0x2222 / 87 / 16` scan -endpoint after the delete/history phases have created salvageable entries. -For duplicate original names, the scan endpoint exposes multiple sequence -entries with the same original name; the server-side `.salvage` metadata keeps -the selected recycle/history path. +## Completion status -- optional `NCP 0x2222 / 22 / 27` Scan Salvageable Files (old), -- `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 salvage test group is complete for the current compatibility slice. The +combined suite now covers NCP write/read payloads, delete capture, versioned +history naming, `87/16` scan, `87/17` recover, `87/18` purge, old `22/27` to +`22/29` bridges, hidden backend repository behaviour, stale sidecar cleanup, and +report-file generation. + +Future salvage checks should be appended to `salvage_smoke_suite.sh` rather than +creating unrelated top-level smoke scripts. New helper binaries are acceptable +when the suite starts them directly. diff --git a/tests/salvage/SALVAGE_BACKEND_DESIGN.md b/tests/salvage/SALVAGE_BACKEND_DESIGN.md deleted file mode 100644 index 7ee6900..0000000 --- a/tests/salvage/SALVAGE_BACKEND_DESIGN.md +++ /dev/null @@ -1,244 +0,0 @@ -# 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 -/ - .recycle/ - / - / - - - .salvage/ - / - / - .json -``` - -Example: - -```text -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: - -```text -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: - -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, - - "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: - -```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 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: - -```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; the JSON stores a fixed 32-byte - FinderInfo block, using all zeroes when mars_nwe has no FinderInfo xattr, -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 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 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 active low-numbered configuration block is: - -```text -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.