Files
mars-nwe/tests/linux/README.md
OpenAI 6bcf93fc5d
All checks were successful
Source release / source-package (push) Successful in 50s
tests: record legacy AFP set file information smoke success
Document the green Linux AFP smoke-suite run that exercises the legacy WebSDK/NWAFP Set File Information entry point alongside the AFP 2.0 path.

The report now comes from the build-tree copy of afp_smoke_suite.sh after the CMake sync fix, which matters because the tests are normally executed from the build directory. It confirms that subfunction 0x09 routes through the same deliberately narrow metadata-only implementation as subfunction 0x10: FinderInfo writes and Finder Invisible set/clear are verified through AFP Get File Information.

Record the relevant AFP-visible results and the Linux xattr state. FinderInfo remains aligned as TEXT/MARS in org.mars-nwe.afp.finder-info, the metadata attribute payload returns to the clean versioned value after the clear probes, and the cached entry id is visible through org.mars-nwe.afp.entry-id.

Tests:

- ./afp_smoke_suite.sh ... SYS:PUBLIC/pmdflts.ini

- Reported failures=0

- Verified legacy 0x09 FinderInfo and Invisible set/clear probes

TODO:

- Keep Set File Information restricted to metadata-only xattr writes until timestamp, enforcement, DOS/NetWare attribute mapping, fork write, and Entry-ID-only semantics are designed.
2026-05-30 13:43:14 +02:00

630 lines
30 KiB
Markdown

# Linux NCP smoke tests
This directory contains optional Linux-side integration tests for endpoints that
are easier to exercise from a Unix host than from the DOS test utilities.
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.
The AFP endpoints are intentionally conservative. mars_nwe-owned ids are read
from the versioned `org.mars-nwe.afp.entry-id` xattr before falling back to
Netatalk/libatalk AppleDouble/CNID metadata. When neither source has an id yet,
the existing stat-derived compatibility id is cached in that xattr so subsequent
AFP probes can reuse the same mars_nwe-owned id instead of re-entering the
temporary fallback path. The first AFP write smoke path is deliberately limited
to the FinderInfo bitmap of
AFP 2.0 Set File Information; CNID allocation, DOS attribute mapping, resource
fork writes, and data-fork writes remain separate write-safety work. mars_nwe
source uses Netatalk-style `org.mars-nwe.<domain>.*` xattr names; AFP
metadata stays under `org.mars-nwe.afp.*` while NetWare-core metadata uses
`org.mars-nwe.netware.*`. On Linux the local xattr helper stores those through
the portable `user.` namespace, matching Netatalk's `org.netatalk.*` EA
abstraction style.
Build with:
```sh
cmake -DMARS_NWE_BUILD_LINUX_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_temp_dir_handle_smoke
cmake --build . --target afp_set_file_info_smoke
```
## AFP smoke-suite report helper
`afp_smoke_suite.sh` runs the currently verified AFP Linux 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_LINUX_TESTS=ON` is enabled, CMake copies the helper into
the build `tests/linux` directory through the `afp_smoke_suite` build target.
This keeps the runtime copy in sync with source changes, and `cmake --build
<build-dir> --target clean` removes the copied script so stale suite helpers do
not survive clean rebuilds.
Example from the build `tests/linux` directory:
```sh
./afp_smoke_suite.sh \
-S MARS -U SUPERVISOR -P secret \
--path SYS:PUBLIC/pmdflts.ini \
--unix-path /var/mars_nwe/SYS/public/pmdflts.ini \
--log /var/log/mars_nwe/nw.log \
--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, Open File
Fork, FinderInfo Set File Information for both AFP 2.0 (`0x10`) and the legacy AFP
Set File Information (`0x09`), AFP 2.0 Invisible/System/Backup Set/Clear File
Information, legacy AFP `0x09` Invisible Set/Clear coverage, and the Linux xattr
checks for:
```text
user.org.mars-nwe.afp.finder-info
user.org.mars-nwe.afp.attributes
user.org.mars-nwe.afp.entry-id
```
The suite now also exercises the additional metadata-only AFP attribute bits that
`afp_set_file_info_smoke` supports: System (`0x0004`) and Backup (`0x0040`).
It additionally runs the legacy AFP `0x09` path for FinderInfo and the Invisible
attribute so both Set File Information entry points cover the metadata write
path. It clears each attribute bit again before the final xattr dump so repeated
runs leave the attribute payload in the clean `0x01000000` state unless a
previous command fails.
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.
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 Invisible 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 Invisible/System/Backup 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 Invisible set/clear, AFP 2.0
System set/clear, and AFP 2.0 Backup set/clear while leaving the final attribute
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=0x0040 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 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 Linux ncpfs utilities.
Example:
```sh
./tests/linux/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/linux/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/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. Server-side diagnostics currently mark stat-derived
temporary IDs with `fallback`; that means the endpoint is reachable, but
persistent CNID/AppleDouble entry-id storage is still future Mac-namespace
work.
If the server was built without the optional Netatalk/libatalk backend, the
endpoint is expected to return invalid namespace. To treat that as a successful
negative smoke test, use:
```sh
./tests/linux/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. The current mars_nwe
implementation supports the same verified path-backed smoke mode as
`AFP Get Entry ID From Path Name`: pass a raw `SYS:`-style path with directory
handle 0 and base Entry ID 0.
Useful smoke cases for a standard MARS-NWE `SYS` volume are:
```sh
./tests/linux/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/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. Server-side diagnostics currently mark stat-derived temporary IDs with
`fallback`; real base-entry-id-relative lookup still depends on persistent
CNID/AppleDouble mapping.
### 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/linux/afp_entry_id_smoke --from-handle -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini
./tests/linux/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 server diagnostics mark the returned Entry ID as `fallback` when it is
derived from Unix `stat(2)` data rather than persistent CNID, AppleDouble, or
libatalk metadata:
```text
AFP Entry ID From NetWare Handle path=SYS:PUBLIC/pmdflts.ini volume=0 entry_id=0x23c8787d (600340605) fork=0
AFP Entry ID From NetWare Handle path=SYS:PUBLIC/ohlogscr.bat volume=0 entry_id=0x260437f6 (637810678) fork=0
AFP Get Entry ID From NetWare Handle: handle=1 volume=0 unix='/var/mars_nwe/SYS/public/pmdflts.ini' entry=0x23c8787d fallback
AFP Get Entry ID From NetWare Handle: handle=1 volume=0 unix='/var/mars_nwe/SYS/public/ohlogscr.bat' entry=0x260437f6 fallback
```
The concrete fallback Entry IDs vary with filesystem metadata. Persistent
CNID/AppleDouble/libatalk-backed identity, parent Entry ID derivation, and AFP
resource-fork handle semantics remain future Mac-namespace work; the current
smoke coverage only verifies the conservative read-only data-fork mapping.
## AFP Alloc Temporary Directory Handle smoke test
`afp_temp_dir_handle_smoke` sends the WebSDK-documented NetWare AFP request:
```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 the same
conservative path-backed subset as the Entry ID and File Information probes:
pass a raw `VOL:`-style path such as `SYS:` or `HOME:` and keep the
base Entry ID at zero. The compatibility server resolves the effective
NetWare volume from that path prefix instead of assuming volume 0; the request
volume byte is retained for WebSDK/header shape and for later Entry-ID-relative
lookup work. Pure Entry-ID-relative allocation is still rejected with Invalid
Path until persistent CNID/base-ID lookup exists.
Useful smoke cases for a standard MARS-NWE `SYS` volume are:
```sh
./tests/linux/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_temp_dir_handle_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/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 dir_handle=2 rights=0xff
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=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 the optional Netatalk/libatalk backend, use
`--allow-invalid-namespace` for the expected negative test. Use
`--allow-invalid-path` for path-resolution negative tests.
## 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
```
The first mars_nwe implementation is deliberately conservative. It supports
raw `VOL:`-style path requests such as `SYS:` or `HOME:` with base Entry ID
zero, opens only the AFP data fork, and only for read access. For path-backed
requests, mars_nwe resolves the effective NetWare volume from the raw path
prefix instead of assuming volume 0. 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/linux/afp_open_file_fork_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini
./tests/linux/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
read 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/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. Resource fork
opens (`--fork 1`), write access (`--access 2`), and Entry-ID-only open remain
negative/TODO coverage until persistent CNID/base-ID lookup and AppleDouble
resource-fork semantics are available.
If the server was built without the optional Netatalk/libatalk backend, use
`--allow-invalid-namespace` for the expected negative test. Use
`--allow-invalid-path` for path-resolution, resource-fork, 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.
Useful smoke cases for a standard MARS-NWE `SYS` volume are:
```sh
./tests/linux/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/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/linux/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/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 that can be derived from Unix `stat(2)` and the optional libatalk
helper wrappers. Server-side diagnostics mark
stat-derived temporary Entry IDs with `fallback`; Parent ID, persistent
CNID/AppleDouble IDs, and fuller Finder Info/resource-fork semantics remain
future Mac-namespace work.
If the server was built without the optional Netatalk/libatalk backend, 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 path-backed 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 uses 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/linux/afp_scan_info_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_scan_info_smoke --afp10 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_scan_info_smoke -S MARS -U SUPERVISOR -P secret --last-seen 0x260437f6 SYS:PUBLIC
./tests/linux/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 mars_nwe had
cached persistent AFP entry ids in `org.mars-nwe.afp.entry-id` xattrs:
```text
AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x00000000 desired=1 next_last_seen=0x260437f6 entry_id=0x260437f6 parent_id=0x00000000 attrs=0x0000 data_len=1296 resource_len=0 offspring=0 long_name=ohlogscr.bat short_name=ohlogscr.bat rights=0x9700
AFP Scan File Info subfunction=0x0a path=SYS:PUBLIC last_seen=0x00000000 desired=1 next_last_seen=0x23c8787d entry_id=0x23c8787d parent_id=0x00000000 attrs=0x0000 data_len=8161 resource_len=0 offspring=0 long_name=pmdflts.ini short_name=pmdflts.ini rights=0x9700
AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x260437f6 desired=1 next_last_seen=0x6686342b entry_id=0x6686342b parent_id=0x00000000 attrs=0x0000 data_len=1024 resource_len=0 offspring=0 long_name=pmgate.sys short_name=pmgate.sys rights=0x9700
AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x6686342b desired=1 next_last_seen=0x2d12d99c entry_id=0x2d12d99c parent_id=0x00000000 attrs=0x0000 data_len=1954 resource_len=0 offspring=0 long_name=pmail.bat short_name=pmail.bat rights=0x9700
```
The concrete Entry IDs vary by filesystem metadata. On first contact mars_nwe
can derive a compatibility id from `stat(2)` and cache it in the versioned
`org.mars-nwe.afp.entry-id` xattr; later probes of that object return the
cached id. The verified xattr payloads are versioned as one version byte,
three reserved bytes, and a big-endian 32-bit AFP Entry ID:
```sh
getfattr -n user.org.mars-nwe.afp.entry-id -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/pmgate.sys
```
```text
user.org.mars-nwe.afp.entry-id=0x010000007b9c42e1
user.org.mars-nwe.afp.entry-id=0x010000006686342b
```
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 the optional Netatalk/libatalk backend, 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 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 exercises two deliberately narrow write-safe AFP metadata subsets:
the file FinderInfo bitmap (`0x0020`) and the file Attributes bitmap (`0x0001`)
restricted to metadata-only file flags: Finder Invisible, System, and Backup.
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 the narrow AFP attribute word
to `org.mars-nwe.afp.attributes`, 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
pattern Netatalk uses for its `org.netatalk.*` metadata names.
Example:
```sh
./tests/linux/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/linux/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:
```text
AFP Set File Info subfunction=0x10 path=SYS:PUBLIC/pmdflts.ini bitmap=0x0020 attrs=0x0000 finder_type=TEXT finder_creator=MARS entry_id=0x23c8787d verified
```
Server diagnostics show the effective resolved volume, the request volume byte,
the FinderInfo bitmap, and the first eight FinderInfo bytes:
```text
AFP 2.0 Set File Information: vol=0 request_vol=0 entry=0x00000000 mask=0x0020 path='SYS:PUBLIC/pmdflts.ini' finder_type='TEXT' finder_creator='MARS'
AFP 2.0 Get File Information: vol=0 entry=0x00000000 mask=0xffff path='SYS:PUBLIC/pmdflts.ini' reply_entry=0x23c8787d fallback
```
The `fallback` marker on the first verification Get File Information diagnostic
still refers to the entry-id source: the returned entry id was derived from the
stat-backed compatibility path because no CNID or mars_nwe entry-id xattr existed
yet. The server now caches that derived id in `org.mars-nwe.afp.entry-id`, so a
second probe of the same file should reuse the xattr-backed id and normally omit
the `fallback` marker. It does not mean the FinderInfo write was ignored; the
helper verifies the written FinderInfo through the follow-up Get File Information
reply.
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
```
Finder Invisible can be tested without mutating DOS/NetWare mode bits:
```sh
./tests/linux/afp_set_file_info_smoke \
-S MARS -U SUPERVISOR -P secret \
--attributes-only --invisible \
SYS:PUBLIC/pmdflts.ini
getfattr -n user.org.mars-nwe.afp.attributes -e hex /var/mars_nwe/SYS/public/pmdflts.ini
```
The xattr payload is versioned. For an invisible file the expected Linux form
is:
```text
user.org.mars-nwe.afp.attributes=0x01000001
```
Use `--clear-invisible --attributes-only` to clear that bit; the same xattr then
stores `0x01000000`. The same helper can exercise the two additional
metadata-only file attribute bits that Netatalk treats as stored AFP file
attributes and that do not require immediate Unix mode-bit or DOS/NetWare
attribute mutation:
```sh
./tests/linux/afp_set_file_info_smoke \
-S MARS -U SUPERVISOR -P secret \
--attributes-only --system \
SYS:PUBLIC/pmdflts.ini
./tests/linux/afp_set_file_info_smoke \
-S MARS -U SUPERVISOR -P secret \
--attributes-only --backup \
SYS:PUBLIC/pmdflts.ini
```
For System the xattr value stores `0x01000004`; for Backup it stores
`0x01000040`. Use `--clear-system` and `--clear-backup` to remove those bits.
The helper verifies only the bit that a single probe sets or clears. Other
stored AFP metadata bits are intentionally preserved, so a run that starts with
Backup already set can legitimately report `attrs=0x0041` while verifying that
Invisible was set.
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: Backup persisted as `0x0040`, so Invisible set/clear produced
`0x0041` and `0x0040` while still being correct for the Invisible bit.
```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=0x0040 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 2.0 Set File Information: vol=0 request_vol=0 entry=0x00000000 mask=0x0001 path='SYS:PUBLIC/pmdflts.ini' attributes attrs=0x8040
```
The legacy `0x09` endpoint is deliberately routed through the same narrow
metadata-only implementation as AFP 2.0 `0x10`; it does not add create, rename,
delete, 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 timestamp,
DOS/NetWare mode-bit mapping, enforcement, resource-fork, and Entry-ID-only
write semantics out of this metadata-only smoke path.
If the server was built without the optional Netatalk/libatalk backend, use
`--allow-invalid-namespace` for the expected negative test. Use
`--allow-invalid-path` for path-resolution negative tests.