Files
mars-nwe/tests/linux/README.md

1177 lines
52 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 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`; 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.
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_create_directory_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, 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:
```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 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/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 \
--readonly-user NOPASSUSER --readonly-no-password \
--prepare-readonly-rights \
--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/linux/afp_create_directory_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpdirts
./tests/linux/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/linux/afp_create_file_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpfile
./tests/linux/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/linux/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/linux/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 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. 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/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 and uses the same NetWare namespace/basehandle identity source for
path-backed requests.
### 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 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:
```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 xattr support, 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
```
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/linux/afp_open_file_fork_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini
./tests/linux/afp_open_file_fork_smoke --entry-id-only -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
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/linux/afp_create_file_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit
./tests/linux/afp_open_file_fork_smoke --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit
./tests/linux/afp_open_file_fork_smoke --entry-id-only --access 0x02 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/afpwrit
./tests/linux/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/linux/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/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 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 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 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
```
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/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 from the WebSDK-layout-only smoke run:
```text
AFP Set File Info subfunction=0x10 layout=websdk 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 layout=websdk 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' layout=websdk 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' layout=websdk 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/linux/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/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 --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 `layout=websdk` 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/linux/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.