Files
mars-nwe/tests/linux
ChatGPT cfd036e54c
All checks were successful
Source release / source-package (push) Successful in 49s
nwconn: persist additional AFP metadata attributes
Extend the conservative AFP 2.0 Set File Information smoke path to accept the metadata-only System and Backup file attribute bits alongside the already-supported Finder Invisible bit.

The WebSDK/NWAFP Set File Information request uses the file Attributes bitmap to pass a set/clear attribute word. Netatalk stores several AFP file attributes in AppleDouble metadata while computing open-fork state dynamically and leaving enforcement-sensitive bits to the file/fork paths. Mirror only the low-risk mars_nwe subset here: persist Invisible, System, and Backup in the private org.mars-nwe.afp.attributes xattr, and keep NoWrite, NoRename, NoDelete, NoCopy, data-fork-open, resource-fork-open, timestamps, resource forks, and Entry-ID-only write semantics rejected until they have deliberate enforcement and backend design.

The xattr payload remains versioned and unchanged. Reads now expose the three supported metadata bits through Get/Scan File Information, and writes preserve the existing set/clear semantics over the supported mask.

Update the Linux Set File Information smoke helper with --system/--clear-system and --backup/--clear-backup options, while keeping the smoke-suite default unchanged. Document the expected Linux xattr forms 0x01000004 and 0x01000040 for those optional probes.

Tests: git diff --check; gcc -Iinclude -I/mnt/data/stubs -fsyntax-only tests/linux/afp_set_file_info_smoke.c.
2026-05-30 12:55:25 +02:00
..

Linux NCP smoke tests

This directory contains optional Linux-side integration tests for endpoints that are easier to exercise from a Unix host than from the DOS test utilities.

The tests use the ncpfs/libncp client library. They are not built by default because they require the host ncpfs development headers/library and a running NetWare-compatible server.

The AFP endpoints are intentionally conservative. mars_nwe-owned ids are read from the versioned org.mars-nwe.afp.entry-id xattr before falling back to Netatalk/libatalk AppleDouble/CNID metadata. When neither source has an id yet, the existing stat-derived compatibility id is cached in that xattr so subsequent AFP probes can reuse the same mars_nwe-owned id instead of re-entering the temporary fallback path. The first AFP write smoke path is deliberately limited to the FinderInfo bitmap of AFP 2.0 Set File Information; CNID allocation, DOS attribute mapping, resource fork writes, and data-fork writes remain separate write-safety work. mars_nwe source uses Netatalk-style org.mars-nwe.<domain>.* xattr names; AFP metadata stays under org.mars-nwe.afp.* while NetWare-core metadata uses org.mars-nwe.netware.*. On Linux the local xattr helper stores those through the portable user. namespace, matching Netatalk's org.netatalk.* EA abstraction style.

Build with:

cmake -DMARS_NWE_BUILD_LINUX_TESTS=ON ...
cmake --build . --target afp_entry_id_smoke
cmake --build . --target afp_file_info_smoke
cmake --build . --target afp_scan_info_smoke
cmake --build . --target afp_temp_dir_handle_smoke
cmake --build . --target afp_set_file_info_smoke

AFP smoke-suite report helper

afp_smoke_suite.sh runs the currently verified AFP Linux smoke helpers as one collectable report. It is meant for interactive runtime validation after a server rebuild: the script prints each helper with the password masked, captures new AFP lines appended to the mars_nwe server log while the suite runs, and adds getfattr -e hex checks for the mars_nwe AFP xattrs on the tested Unix file.

Example from the build tests/linux 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, Open File Fork, FinderInfo Set File Information, Invisible Set/Clear File Information, 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

Use --no-log when the log file is unavailable or when the server log is being collected separately. Use --stop-on-failure for strict bisect-style runs; by default the script keeps going so one failing endpoint does not hide later AFP output from the report.

A verified suite run after the FinderInfo payload-alignment fix completed with failures=0 for SYS:PUBLIC/pmdflts.ini. The report covered Entry ID by path, Entry ID from NetWare handle, Get File Information, Scan File Information, Alloc Temporary Directory Handle, Open File Fork, FinderInfo Set File Information, and Finder Invisible set/clear. The relevant Linux xattr checks from that run were:

user.org.mars-nwe.afp.finder-info=0x544558544d415253000000000000000000000000000000000000000000000000
user.org.mars-nwe.afp.attributes=0x01000000
user.org.mars-nwe.afp.entry-id=0x0100000033f9a1ed

The FinderInfo value starts with TEXTMARS without a leading padding byte, so the smoke helper and server now agree on the WebSDK/NWAFP Set File Information payload alignment. The server log excerpt for the same run showed all AFP operations returning successfully, including mask=0x0020 for FinderInfo and mask=0x0001 for the Invisible set/clear probes.

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 Linux ncpfs utilities.

Example:

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

./tests/linux/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/afp_entry_id_smoke -S MARS -U SUPERVISOR -P secret SYS:BURST

A successful reply prints the request path, directory handle, and returned 32-bit AFP Entry ID. Server-side diagnostics currently mark stat-derived temporary IDs with fallback; that means the endpoint is reachable, but persistent CNID/AppleDouble entry-id storage is still future Mac-namespace work.

If the server was built without the optional Netatalk/libatalk backend, the endpoint is expected to return invalid namespace. To treat that as a successful negative smoke test, use:

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

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/linux/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/afp_entry_id_smoke --from-name -S MARS -U SUPERVISOR -P secret SYS:BURST

A successful reply prints the same 32-bit AFP Entry ID format as the path-name probe. Server-side diagnostics currently mark stat-derived temporary IDs with fallback; real base-entry-id-relative lookup still depends on persistent CNID/AppleDouble mapping.

AFP Get Entry ID From NetWare Handle

afp_entry_id_smoke can also exercise the WebSDK-documented NetWare AFP request:

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/linux/afp_entry_id_smoke --from-handle -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini
./tests/linux/afp_entry_id_smoke --from-handle -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/ohlogscr.bat

Successful replies print the resolved volume number, 32-bit AFP Entry ID, and fork indicator. The current implementation reports the data fork (fork=0) and server diagnostics mark the returned Entry ID as fallback when it is derived from Unix stat(2) data rather than persistent CNID, AppleDouble, or libatalk metadata:

AFP Entry ID From NetWare Handle path=SYS:PUBLIC/pmdflts.ini volume=0 entry_id=0x23c8787d (600340605) fork=0
AFP Entry ID From NetWare Handle path=SYS:PUBLIC/ohlogscr.bat volume=0 entry_id=0x260437f6 (637810678) fork=0

AFP Get Entry ID From NetWare Handle: handle=1 volume=0 unix='/var/mars_nwe/SYS/public/pmdflts.ini' entry=0x23c8787d fallback
AFP Get Entry ID From NetWare Handle: handle=1 volume=0 unix='/var/mars_nwe/SYS/public/ohlogscr.bat' entry=0x260437f6 fallback

The concrete fallback Entry IDs vary with filesystem metadata. Persistent CNID/AppleDouble/libatalk-backed identity, parent Entry ID derivation, and AFP resource-fork handle semantics remain future Mac-namespace work; the current smoke coverage only verifies the conservative read-only data-fork mapping.

AFP Alloc Temporary Directory Handle smoke test

afp_temp_dir_handle_smoke sends the WebSDK-documented NetWare AFP request:

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:

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

AFP Alloc Temporary Dir Handle path=SYS: dir_handle=2 rights=0xff
AFP Alloc Temporary Dir Handle path=SYS:PUBLIC dir_handle=2 rights=0xff
AFP Alloc Temporary Dir Handle path=SYS:SYSTEM dir_handle=2 rights=0xff
AFP Alloc Temporary Dir Handle path=SYS:BURST dir_handle=2 rights=0xff
AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:' dir_handle=2 rights=0x1ff
AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:PUBLIC' dir_handle=2 rights=0x1ff
AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:SYSTEM' dir_handle=2 rights=0x1ff
AFP Alloc Temporary Dir Handle: vol=0 request_vol=0 entry=0x00000000 path='SYS:BURST' dir_handle=2 rights=0x1ff

The AFP reply carries the one-byte access-rights field consumed by the smoke helper, so the client prints 0xff. The server diagnostic logs the internal NetWare effective-rights mask before that AFP reply narrowing, so a fully privileged directory can appear as 0x1ff in mars_nwe.log.

If the server was built without the optional Netatalk/libatalk backend, use --allow-invalid-namespace for the expected negative test. Use --allow-invalid-path for path-resolution negative tests.

AFP Open File Fork smoke test

afp_open_file_fork_smoke sends the WebSDK-documented NetWare AFP open fork request:

NCP 0x2222/35/08  AFP Open File Fork

The first mars_nwe implementation is deliberately conservative. It supports raw VOL:-style path requests such as SYS: or HOME: with base Entry ID zero, opens only the AFP data fork, and only for read access. For path-backed requests, mars_nwe resolves the effective NetWare volume from the raw path prefix instead of assuming volume 0. On success the server returns the normal six-byte NetWare file handle shape used by AFP handle APIs plus the current data-fork length. The smoke helper immediately closes the returned NetWare file handle in the same connection.

Useful smoke cases for a standard MARS-NWE SYS volume are:

./tests/linux/afp_open_file_fork_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini
./tests/linux/afp_open_file_fork_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/ohlogscr.bat

A file on another exported volume should be tested with its raw volume prefix (for example HOME:...). The matching server log should report that resolved volume number, while still showing the request volume byte separately.

A successful reply prints the returned NetWare handle, the requested fork, the read access mode, and the data-fork length. A verified runtime smoke run against the standard DOS utility files produced:

AFP Open File Fork path=SYS:PUBLIC/pmdflts.ini handle=1 fork=0 access=0x01 fork_len=8161
AFP Open File Fork path=SYS:PUBLIC/ohlogscr.bat handle=1 fork=0 access=0x01 fork_len=1296

The matching server log records the path-backed open and the same data-fork lengths:

AFP Open File Fork: vol=0 request_vol=0 entry=0x00000000 fork=0 access=0x01 path='SYS:PUBLIC/pmdflts.ini' handle=1 fork_len=8161
AFP Open File Fork: vol=0 request_vol=0 entry=0x00000000 fork=0 access=0x01 path='SYS:PUBLIC/ohlogscr.bat' handle=1 fork_len=1296

The exact handle number is connection-local and must not be reused across processes. The exact fork_len depends on the backing file contents. Resource fork opens (--fork 1), write access (--access 2), and Entry-ID-only open remain negative/TODO coverage until persistent CNID/base-ID lookup and AppleDouble resource-fork semantics are available.

If the server was built without the optional Netatalk/libatalk backend, use --allow-invalid-namespace for the expected negative test. Use --allow-invalid-path for path-resolution, resource-fork, or Entry-ID-only negative tests.

AFP File Information smoke test

afp_file_info_smoke sends the WebSDK-documented NetWare AFP file information requests:

NCP 0x2222/35/05  AFP Get File Information
NCP 0x2222/35/15  AFP 2.0 Get File Information

It uses the same libncp NWRequestSimple() transport path as the Entry ID smoke test and sends raw SYS:-style path requests with directory handle 0. The server replies with the read-only AFP file information record currently implemented by mars_nwe: Entry ID, Parent ID, attributes, data/resource fork lengths, offspring count, fixed long/short names, and access rights.

Useful smoke cases for a standard MARS-NWE SYS volume are:

./tests/linux/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/afp_file_info_smoke -S MARS -U SUPERVISOR -P secret SYS:BURST

# AFP 2.0 variant using the same path-backed read-only reply
./tests/linux/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:
./tests/linux/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:SYSTEM
./tests/linux/afp_file_info_smoke --afp20 -S MARS -U SUPERVISOR -P secret SYS:BURST

The AFP 2.0 mode is selected with --afp20. It has been verified against the same SYS:, SYS:PUBLIC, SYS:SYSTEM, and SYS:BURST paths and currently exercises the same path-backed read-only reply as the older call. The current implementation fills fields that can be derived from Unix stat(2) and the optional libatalk helper wrappers. Server-side diagnostics mark stat-derived temporary Entry IDs with fallback; Parent ID, persistent CNID/AppleDouble IDs, and fuller Finder Info/resource-fork semantics remain future Mac-namespace work.

If the server was built without the optional Netatalk/libatalk backend, use --allow-invalid-namespace for the expected negative test. Use --allow-invalid-path for path-resolution negative tests.

AFP Scan File Information smoke test

afp_scan_info_smoke sends the WebSDK-documented NetWare AFP scan requests:

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/linux/afp_scan_info_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_scan_info_smoke --afp10 -S MARS -U SUPERVISOR -P secret SYS:PUBLIC
./tests/linux/afp_scan_info_smoke -S MARS -U SUPERVISOR -P secret --last-seen 0x260437f6 SYS:PUBLIC
./tests/linux/afp_scan_info_smoke -S MARS -U SUPERVISOR -P secret --last-seen 0x6686342b SYS:PUBLIC

Verified runtime output from the sample SYS:PUBLIC tree after mars_nwe had cached persistent AFP entry ids in org.mars-nwe.afp.entry-id xattrs:

AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x00000000 desired=1 next_last_seen=0x260437f6 entry_id=0x260437f6 parent_id=0x00000000 attrs=0x0000 data_len=1296 resource_len=0 offspring=0 long_name=ohlogscr.bat short_name=ohlogscr.bat rights=0x9700
AFP Scan File Info subfunction=0x0a path=SYS:PUBLIC last_seen=0x00000000 desired=1 next_last_seen=0x23c8787d entry_id=0x23c8787d parent_id=0x00000000 attrs=0x0000 data_len=8161 resource_len=0 offspring=0 long_name=pmdflts.ini short_name=pmdflts.ini rights=0x9700
AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x260437f6 desired=1 next_last_seen=0x6686342b entry_id=0x6686342b parent_id=0x00000000 attrs=0x0000 data_len=1024 resource_len=0 offspring=0 long_name=pmgate.sys short_name=pmgate.sys rights=0x9700
AFP Scan File Info subfunction=0x11 path=SYS:PUBLIC last_seen=0x6686342b desired=1 next_last_seen=0x2d12d99c entry_id=0x2d12d99c parent_id=0x00000000 attrs=0x0000 data_len=1954 resource_len=0 offspring=0 long_name=pmail.bat short_name=pmail.bat rights=0x9700

The concrete Entry IDs vary by filesystem metadata. On first contact mars_nwe can derive a compatibility id from stat(2) and cache it in the versioned org.mars-nwe.afp.entry-id xattr; later probes of that object return the cached id. The verified xattr payloads are versioned as one version byte, three reserved bytes, and a big-endian 32-bit AFP Entry ID:

getfattr -n user.org.mars-nwe.afp.entry-id -e hex /var/mars_nwe/SYS/public/pmdflts.ini
getfattr -n user.org.mars-nwe.afp.entry-id -e hex /var/mars_nwe/SYS/public/pmgate.sys
user.org.mars-nwe.afp.entry-id=0x010000007b9c42e1
user.org.mars-nwe.afp.entry-id=0x010000006686342b

The scan continuation order is deliberately documented as server directory iteration order, not numeric Entry-ID order. last_seen identifies the Entry ID that was returned by the previous scan call so mars_nwe can skip entries until that object is seen and then return the next directory entry. Therefore the next returned Entry ID is not guaranteed to be numerically greater than the last_seen value; in the verified run, last_seen=0x6686342b returns pmail.bat with entry_id=0x2d12d99c. The older 0x0a path intentionally shares the same conservative scan implementation so older AFP callers can probe the same read-only directory listing semantics before fuller multi-response and CNID-backed scans are implemented.

If the server was built without the optional Netatalk/libatalk backend, use --allow-invalid-namespace for the expected negative test. Use --allow-invalid-path for path-resolution negative tests, and --allow-empty when a scan continuation is expected to reach the end of the directory.

AFP Set File Information metadata smoke test

afp_set_file_info_smoke sends the WebSDK-documented NetWare AFP 2.0 request:

NCP 0x2222/35/16  AFP 2.0 Set File Information

The helper exercises two deliberately narrow write-safe AFP metadata subsets: the file FinderInfo bitmap (0x0020) and the file Attributes bitmap (0x0001) restricted to metadata-only file flags: Finder Invisible, System, and Backup. It sends path-backed raw VOL:-style requests, writes the 32-byte FinderInfo block to mars_nwe's private org.mars-nwe.afp.finder-info metadata key, writes the narrow AFP attribute word to org.mars-nwe.afp.attributes, and immediately verifies the updates through AFP 2.0 Get File Information. On Linux the source-level org.mars-nwe.afp.* name is stored via the portable user. xattr namespace by mars_nwe's local xattr wrapper, the same pattern Netatalk uses for its org.netatalk.* metadata names.

Example:

./tests/linux/afp_set_file_info_smoke \
  -S MARS -U SUPERVISOR -P secret \
  --type TEXT --creator MARS \
  SYS:PUBLIC/pmdflts.ini

Verified runtime output:

AFP Set File Info path=SYS:PUBLIC/pmdflts.ini bitmap=0x0020 finder_type=TEXT finder_creator=MARS entry_id=0x23c8787d verified

Server diagnostics show the effective resolved volume, the request volume byte, the FinderInfo bitmap, and the first eight FinderInfo bytes:

AFP 2.0 Set File Information: vol=0 request_vol=0 entry=0x00000000 mask=0x0020 path='SYS:PUBLIC/pmdflts.ini' finder_type='TEXT' finder_creator='MARS'
AFP 2.0 Get File Information: vol=0 entry=0x00000000 mask=0xffff path='SYS:PUBLIC/pmdflts.ini' reply_entry=0x23c8787d fallback

The fallback marker on the first verification Get File Information diagnostic still refers to the entry-id source: the returned entry id was derived from the stat-backed compatibility path because no CNID or mars_nwe entry-id xattr existed yet. The server now caches that derived id in org.mars-nwe.afp.entry-id, so a second probe of the same file should reuse the xattr-backed id and normally omit the fallback marker. It does not mean the FinderInfo write was ignored; the helper verifies the written FinderInfo through the follow-up Get File Information reply.

Linux xattr checks for the FinderInfo and cached Entry ID look like this:

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

Finder Invisible can be tested without mutating DOS/NetWare mode bits:

./tests/linux/afp_set_file_info_smoke \
  -S MARS -U SUPERVISOR -P secret \
  --attributes-only --invisible \
  SYS:PUBLIC/pmdflts.ini

getfattr -n user.org.mars-nwe.afp.attributes -e hex /var/mars_nwe/SYS/public/pmdflts.ini

The xattr payload is versioned. For an invisible file the expected Linux form is:

user.org.mars-nwe.afp.attributes=0x01000001

Use --clear-invisible --attributes-only to clear that bit; the same xattr then stores 0x01000000. The same helper can exercise the two additional metadata-only file attribute bits that Netatalk treats as stored AFP file attributes and that do not require immediate Unix mode-bit or DOS/NetWare attribute mutation:

./tests/linux/afp_set_file_info_smoke \
  -S MARS -U SUPERVISOR -P secret \
  --attributes-only --system \
  SYS:PUBLIC/pmdflts.ini

./tests/linux/afp_set_file_info_smoke \
  -S MARS -U SUPERVISOR -P secret \
  --attributes-only --backup \
  SYS:PUBLIC/pmdflts.ini

For System the xattr value stores 0x01000004; for Backup it stores 0x01000040. Use --clear-system and --clear-backup to remove those bits. All other Set File Information bitmap bits and AFP attribute bits, including NoWrite, NoRename, NoDelete, NoCopy, and the computed data/resource-fork-open flags, are intentionally rejected for now. That keeps timestamp, DOS/NetWare mode-bit mapping, enforcement, resource-fork, and Entry-ID-only write semantics out of this metadata-only smoke path.

If the server was built without the optional Netatalk/libatalk backend, use --allow-invalid-namespace for the expected negative test. Use --allow-invalid-path for path-resolution negative tests.