nwconn: persist additional AFP metadata attributes
All checks were successful
Source release / source-package (push) Successful in 49s

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.
This commit is contained in:
ChatGPT
2026-05-30 10:53:34 +00:00
committed by Mario Fetka
parent c57af8c7dc
commit cfd036e54c
5 changed files with 74 additions and 20 deletions

View File

@@ -449,8 +449,9 @@ 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 the Finder Invisible bit. It sends path-backed raw `VOL:`-style
requests, writes the 32-byte FinderInfo block to mars_nwe's private
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
@@ -522,10 +523,30 @@ user.org.mars-nwe.afp.attributes=0x01000001
```
Use `--clear-invisible --attributes-only` to clear that bit; the same xattr then
stores `0x01000000`. All other Set File Information bitmap bits and all other
AFP attribute bits are intentionally rejected for now. That keeps timestamp,
DOS/NetWare mode-bit mapping, resource-fork, and Entry-ID-only write semantics
out of this metadata-only smoke path.
stores `0x01000000`. The same helper can exercise the two additional
metadata-only file attribute bits that Netatalk treats as stored AFP file
attributes and that do not require immediate Unix mode-bit or DOS/NetWare
attribute mutation:
```sh
./tests/linux/afp_set_file_info_smoke \
-S MARS -U SUPERVISOR -P secret \
--attributes-only --system \
SYS:PUBLIC/pmdflts.ini
./tests/linux/afp_set_file_info_smoke \
-S MARS -U SUPERVISOR -P secret \
--attributes-only --backup \
SYS:PUBLIC/pmdflts.ini
```
For System the xattr value stores `0x01000004`; for Backup it stores
`0x01000040`. Use `--clear-system` and `--clear-backup` to remove those bits.
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

View File

@@ -24,7 +24,10 @@
#define AFP_ATTRIBUTES_MASK 0x0001
#define AFP_FINDER_INFO_MASK 0x0020
#define AFP_ATTR_INVISIBLE 0x0001
#define AFP_ATTR_SYSTEM 0x0004
#define AFP_ATTR_BACKUP 0x0040
#define AFP_ATTR_SETCLR 0x8000
#define AFP_ATTR_STORED_MASK (AFP_ATTR_INVISIBLE | AFP_ATTR_SYSTEM | AFP_ATTR_BACKUP)
#define AFP_REPLY_LEN 120
#define NWE_INVALID_NAMESPACE 0xbf
#define NWE_INVALID_PATH 0x9c
@@ -34,7 +37,8 @@ static void usage(const char *prog)
fprintf(stderr,
"Usage: %s [--allow-invalid-namespace] [--allow-invalid-path] "
"[--volume N] [--entry-id ID] [--type FOUR] [--creator FOUR] "
"[--invisible|--clear-invisible] [--finder-info-only|--attributes-only] "
"[--invisible|--clear-invisible|--system|--clear-system|--backup|--clear-backup] "
"[--finder-info-only|--attributes-only] "
"[ncpfs options] PATH\n"
"\n"
"ncpfs options are parsed by ncp_initialize(), for example:\n"
@@ -43,8 +47,9 @@ static void usage(const char *prog)
"Examples:\n"
" %s -S MARS -U SUPERVISOR -P secret --type TEXT --creator MARS SYS:PUBLIC/pmdflts.ini\n"
" %s -S MARS -U SUPERVISOR -P secret --invisible --attributes-only SYS:PUBLIC/pmdflts.ini\n"
" %s -S MARS -U SUPERVISOR -P secret --backup --attributes-only SYS:PUBLIC/pmdflts.ini\n"
" %s --allow-invalid-namespace -S MARS SYS:PUBLIC/pmdflts.ini\n",
prog, prog, prog, prog);
prog, prog, prog, prog, prog);
}
static int parse_u32(const char *text, uint32_t *value)
@@ -185,6 +190,22 @@ int main(int argc, char **argv)
request_mask |= AFP_ATTRIBUTES_MASK;
attr_request = AFP_ATTR_INVISIBLE;
attr_expected = 0;
} else if (!strcmp(argv[i], "--system")) {
request_mask |= AFP_ATTRIBUTES_MASK;
attr_request = AFP_ATTR_SETCLR | AFP_ATTR_SYSTEM;
attr_expected = AFP_ATTR_SYSTEM;
} else if (!strcmp(argv[i], "--clear-system")) {
request_mask |= AFP_ATTRIBUTES_MASK;
attr_request = AFP_ATTR_SYSTEM;
attr_expected = 0;
} else if (!strcmp(argv[i], "--backup")) {
request_mask |= AFP_ATTRIBUTES_MASK;
attr_request = AFP_ATTR_SETCLR | AFP_ATTR_BACKUP;
attr_expected = AFP_ATTR_BACKUP;
} else if (!strcmp(argv[i], "--clear-backup")) {
request_mask |= AFP_ATTRIBUTES_MASK;
attr_request = AFP_ATTR_BACKUP;
attr_expected = 0;
} else if (!strcmp(argv[i], "--attributes-only")) {
request_mask &= ~AFP_FINDER_INFO_MASK;
} else if (!strcmp(argv[i], "--finder-info-only")) {
@@ -301,7 +322,7 @@ int main(int argc, char **argv)
}
if ((request_mask & AFP_ATTRIBUTES_MASK) &&
((be16_to_cpu(verify_buf + 8) & AFP_ATTR_INVISIBLE) != attr_expected)) {
((be16_to_cpu(verify_buf + 8) & AFP_ATTR_STORED_MASK) != attr_expected)) {
fprintf(stderr,
"AFP Set File Information attr verify mismatch: path=%s attrs=0x%04x expected_invisible=0x%04x\n",
path, be16_to_cpu(verify_buf + 8), attr_expected);