Files
mars-nwe/tests/afp
..

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.

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:

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

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 <build-dir> --target clean removes the copied script so stale suite helpers do not survive clean rebuilds.

Example from the build tests/afp directory:

./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:

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/afp directory:

./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:

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:

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:

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:

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:

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:

./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:

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:

./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:

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:

./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:

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:

./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:

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:

./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:

./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:

./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:

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:

./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 -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 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:

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:

./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:

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:

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:

./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:

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:

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:

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:

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:

./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:

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:

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:

./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:

./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:

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:

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:

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:

./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:

./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:

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:

./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 -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:

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:

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:

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:

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:

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:

./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:

./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:

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:

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:

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:

user.org.mars-nwe.afp.finder-info=0x544558544d415253000000000000000000000000000000000000000000000000

AFP Hidden is tested through the same NetWare attribute path as System and Archive:

./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:

./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 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.

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:

./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:

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:

./afp_dos_name_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini

Expected output shape:

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:

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:

./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:

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:

./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:

./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:

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:

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:

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:

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 layout=websdk 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 layout=websdk 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:

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' layout=websdk 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' layout=websdk 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:

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:

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.

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.