From ec2b645994feb8da75faf5e4fb28594db0dd7dfd Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Mon, 22 Jun 2026 08:56:04 +0200 Subject: [PATCH] afp: store metadata in Netatalk xattrs --- REDESIGN.md | 25 ++- src/nwatalk.c | 325 ++++++++++++++++++++-------- src/nwconn.c | 14 +- tests/afp/README.md | 14 +- tests/afp/afp_endpoint_inventory.py | 12 +- tests/afp/afp_smoke_suite.sh | 22 +- 6 files changed, 276 insertions(+), 136 deletions(-) diff --git a/REDESIGN.md b/REDESIGN.md index dcf4df3..d24a278 100644 --- a/REDESIGN.md +++ b/REDESIGN.md @@ -2289,14 +2289,15 @@ MAC_METADATA_LAYOUT packed layout value 1 MAC_RF data-stream name for the Mac resource fork ``` -Therefore the post-pl27 mars-nwe AFP target is to replace -`org.mars-nwe.afp.*` xattrs with NSS-style metadata/data-stream storage. Since -this code has not been released to users since the pl27 line, there is no need -for fallback, migration, or mirror support for those private xattrs. If Mac -metadata packing requires changes to `netware.metadata` writer logic, trustee -variable-length handling, or root-variable-data packing, improve those pieces; do -not create a parallel `netware.macmetadata` convenience store unless later full -NSS evidence proves that is the actual Linux xattr boundary. +Therefore mars-nwe AFP metadata must not use the old private +`org.mars-nwe.afp.*` xattr family. On real NSS, AFP/Mac state belongs in +NSS-style `zMacInfo_s`/`PackedMacInfo_s` metadata and `MAC_RF` streams. On the +OtherFS/xattr backend, store the same AFP-facing state through Netatalk-family +xattrs: `org.netatalk.Metadata` for AppleDouble `adouble:ea` FinderInfo and +CNID/entry-id metadata, `org.netatalk.ProDOS` for AFP 2.0 ProDOSInfo, and +`org.netatalk.ResourceFork` for resource-fork data. Since the private xattr +code has not been released to users since the pl27 line, there is no need for +fallback, migration, or mirror support for those private xattrs. Salvage must preserve and restore the same NSS-style Mac metadata and later the `MAC_RF` resource fork stream. The current AFP NCP handlers may remain the @@ -2403,8 +2404,9 @@ nss/public_core/comn/namespace/ nss/public_core/comn/namespace/macNSpace.c and shared/sdk/internal/macNSpace.h Mac metadata packing/unpacking and namespace rules. Use the NSS - zMacInfo_s/PackedMacInfo_s/RVD_MAC_META_DATA model; replace private - org.mars-nwe.afp.* xattrs instead of migrating them. + zMacInfo_s/PackedMacInfo_s/RVD_MAC_META_DATA model for real NSS volumes; + OtherFS/xattr AFP metadata uses Netatalk-family xattrs instead of private + org.mars-nwe.afp.* xattrs. nss/public_core/comn/common/comnMacintosh.c Mac resource fork data-stream lookup/creation and the `MAC_RF` stream name. @@ -2621,7 +2623,8 @@ release: 5. nwattrib.c -> read/write fileAttributes through libnwfs metadata 6. trustee.c -> store trustees and inherited-rights-mask in netware.metadata 7. nwatalk/AFP -> replace private org.mars-nwe.afp.* xattrs with NSS-style - zMacInfo_s/PackedMacInfo_s metadata and MAC_RF streams + zMacInfo_s/PackedMacInfo_s metadata on NSS and + Netatalk-family xattrs on OtherFS 8. nwarchive.c -> merge private mars-nwe archive/fileinfo xattrs into metadata 9. salvage -> snapshot/restore the same metadata, including AFP/archive state 10. MARS_NWE_4 -> keep NetWare 4.x namespace-aware variants, compression diff --git a/src/nwatalk.c b/src/nwatalk.c index aa30aa4..2a0ebfe 100644 --- a/src/nwatalk.c +++ b/src/nwatalk.c @@ -14,7 +14,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ -/* mars_nwe AFP xattr metadata backend helpers. */ +/* Netatalk adouble:ea AFP xattr metadata backend helpers. */ #include "net.h" #include "config.h" #include "nwatalk.h" @@ -22,17 +22,56 @@ #include "tools.h" #include +#include #if XATTR_SUPPORT #include #endif -#define MARS_NWE_AFP_ENTRY_ID_XATTR "org.mars-nwe.afp.entry-id" -#define MARS_NWE_AFP_FINDER_INFO_XATTR "org.mars-nwe.afp.finder-info" -#define MARS_NWE_AFP_PRODOS_INFO_XATTR "org.mars-nwe.afp.prodos-info" -#define MARS_NWE_AFP_ATTRIBUTES_XATTR "org.mars-nwe.afp.attributes" -#define MARS_NWE_AFP_ENTRY_ID_VERSION 1 -#define MARS_NWE_AFP_ATTRIBUTES_VERSION 1 +#define NWATALK_AD_EA_META_XATTR "org.netatalk.Metadata" +#define NWATALK_AD_EA_RESO_XATTR "org.netatalk.ResourceFork" +#define NWATALK_AD_EA_PRODOS_XATTR "org.netatalk.ProDOS" + +#define NWATALK_AD_MAGIC 0x00051607U +#define NWATALK_AD_VERSION_EA 0x00020002U +#define NWATALK_AD_HEADER_LEN 26 +#define NWATALK_AD_ENTRY_LEN 12 +#define NWATALK_AD_ENTRY_COUNT 8 +#define NWATALK_AD_DATASZ_EA 402 + +#define NWATALK_ADEID_FINDERI 9U +#define NWATALK_ADEID_COMMENT 4U +#define NWATALK_ADEID_FILEDATESI 8U +#define NWATALK_ADEID_AFPFILEI 14U +#define NWATALK_ADEID_PRIVDEV 0x80444556U +#define NWATALK_ADEID_PRIVINO 0x80494e4fU +#define NWATALK_ADEID_PRIVSYN 0x8053594eU +#define NWATALK_ADEID_PRIVID 0x8053567eU + +#define NWATALK_ADEDLEN_COMMENT 200 +#define NWATALK_ADEDLEN_FILEDATESI 16 +#define NWATALK_ADEDLEN_AFPFILEI 4 +#define NWATALK_ADEDLEN_PRIVDEV 8 +#define NWATALK_ADEDLEN_PRIVINO 8 +#define NWATALK_ADEDLEN_PRIVSYN 8 +#define NWATALK_ADEDLEN_PRIVID 4 + +#define NWATALK_ADEDOFF_FINDERI \ + (NWATALK_AD_HEADER_LEN + NWATALK_AD_ENTRY_COUNT * NWATALK_AD_ENTRY_LEN) +#define NWATALK_ADEDOFF_COMMENT \ + (NWATALK_ADEDOFF_FINDERI + NWATALK_FINDER_INFO_LEN) +#define NWATALK_ADEDOFF_FILEDATESI \ + (NWATALK_ADEDOFF_COMMENT + NWATALK_ADEDLEN_COMMENT) +#define NWATALK_ADEDOFF_AFPFILEI \ + (NWATALK_ADEDOFF_FILEDATESI + NWATALK_ADEDLEN_FILEDATESI) +#define NWATALK_ADEDOFF_PRIVDEV \ + (NWATALK_ADEDOFF_AFPFILEI + NWATALK_ADEDLEN_AFPFILEI) +#define NWATALK_ADEDOFF_PRIVINO \ + (NWATALK_ADEDOFF_PRIVDEV + NWATALK_ADEDLEN_PRIVDEV) +#define NWATALK_ADEDOFF_PRIVSYN \ + (NWATALK_ADEDOFF_PRIVINO + NWATALK_ADEDLEN_PRIVINO) +#define NWATALK_ADEDOFF_PRIVID \ + (NWATALK_ADEDOFF_PRIVSYN + NWATALK_ADEDLEN_PRIVSYN) #define NWATALK_AFP_ATTR_HIDDEN 0x0200 #define NWATALK_AFP_ATTR_SYSTEM 0x0400 @@ -40,18 +79,6 @@ #define NWATALK_AFP_ATTR_STORED_MASK \ (NWATALK_AFP_ATTR_HIDDEN | NWATALK_AFP_ATTR_SYSTEM) -typedef struct { - uint8 version; - uint8 reserved[3]; - uint8 entry_id[4]; -} MARS_NWE_AFP_ENTRY_ID_XATTR_DATA; - -typedef struct { - uint8 version; - uint8 reserved; - uint8 attributes[2]; -} MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA; - int nwatalk_backend_available(void) { #if XATTR_SUPPORT @@ -62,22 +89,162 @@ int nwatalk_backend_available(void) } #if XATTR_SUPPORT -static int nwatalk_get_mars_entry_id_xattr(const char *path, uint32 *entry_id) +typedef struct { + uint32 id; + uint32 off; + uint32 len; +} NWATALK_AD_ENTRY; + +static const NWATALK_AD_ENTRY nwatalk_ad_entries[NWATALK_AD_ENTRY_COUNT] = { + { NWATALK_ADEID_FINDERI, NWATALK_ADEDOFF_FINDERI, NWATALK_FINDER_INFO_LEN }, + { NWATALK_ADEID_COMMENT, NWATALK_ADEDOFF_COMMENT, NWATALK_ADEDLEN_COMMENT }, + { NWATALK_ADEID_FILEDATESI, NWATALK_ADEDOFF_FILEDATESI, NWATALK_ADEDLEN_FILEDATESI }, + { NWATALK_ADEID_AFPFILEI, NWATALK_ADEDOFF_AFPFILEI, NWATALK_ADEDLEN_AFPFILEI }, + { NWATALK_ADEID_PRIVDEV, NWATALK_ADEDOFF_PRIVDEV, NWATALK_ADEDLEN_PRIVDEV }, + { NWATALK_ADEID_PRIVINO, NWATALK_ADEDOFF_PRIVINO, NWATALK_ADEDLEN_PRIVINO }, + { NWATALK_ADEID_PRIVSYN, NWATALK_ADEDOFF_PRIVSYN, NWATALK_ADEDLEN_PRIVSYN }, + { NWATALK_ADEID_PRIVID, NWATALK_ADEDOFF_PRIVID, NWATALK_ADEDLEN_PRIVID } +}; + +static void nwatalk_put_be16(uint8 *p, uint16 value) +{ + p[0] = (uint8)((value >> 8) & 0xff); + p[1] = (uint8)(value & 0xff); +} + +static void nwatalk_put_be32(uint8 *p, uint32 value) +{ + p[0] = (uint8)((value >> 24) & 0xff); + p[1] = (uint8)((value >> 16) & 0xff); + p[2] = (uint8)((value >> 8) & 0xff); + p[3] = (uint8)(value & 0xff); +} + +static uint16 nwatalk_get_be16(const uint8 *p) +{ + return((uint16)(((uint16)p[0] << 8) | p[1])); +} + +static uint32 nwatalk_get_be32(const uint8 *p) +{ + return(((uint32)p[0] << 24) | ((uint32)p[1] << 16) | + ((uint32)p[2] << 8) | (uint32)p[3]); +} + +static void nwatalk_init_ad_metadata(uint8 data[NWATALK_AD_DATASZ_EA]) +{ + size_t i; + uint8 *p; + + memset(data, 0, NWATALK_AD_DATASZ_EA); + nwatalk_put_be32(data, NWATALK_AD_MAGIC); + nwatalk_put_be32(data + 4, NWATALK_AD_VERSION_EA); + nwatalk_put_be16(data + 24, NWATALK_AD_ENTRY_COUNT); + + p = data + NWATALK_AD_HEADER_LEN; + for (i = 0; i < NWATALK_AD_ENTRY_COUNT; i++) { + nwatalk_put_be32(p, nwatalk_ad_entries[i].id); + nwatalk_put_be32(p + 4, nwatalk_ad_entries[i].off); + nwatalk_put_be32(p + 8, nwatalk_ad_entries[i].len); + p += NWATALK_AD_ENTRY_LEN; + } +} + +static int nwatalk_valid_ad_metadata(const uint8 data[NWATALK_AD_DATASZ_EA]) +{ + uint16 nentries; + uint16 i; + + if (nwatalk_get_be32(data) != NWATALK_AD_MAGIC || + nwatalk_get_be32(data + 4) != NWATALK_AD_VERSION_EA) + return(0); + + nentries = nwatalk_get_be16(data + 24); + if (nentries > NWATALK_AD_ENTRY_COUNT) + return(0); + + for (i = 0; i < nentries; i++) { + const uint8 *p = data + NWATALK_AD_HEADER_LEN + i * NWATALK_AD_ENTRY_LEN; + uint32 off = nwatalk_get_be32(p + 4); + uint32 len = nwatalk_get_be32(p + 8); + if (off > NWATALK_AD_DATASZ_EA || len > NWATALK_AD_DATASZ_EA || + off + len > NWATALK_AD_DATASZ_EA) + return(0); + } + + return(1); +} + +static int nwatalk_read_ad_metadata(const char *path, + uint8 data[NWATALK_AD_DATASZ_EA], + int create_if_missing) { - MARS_NWE_AFP_ENTRY_ID_XATTR_DATA d; ssize_t len; + + if (!path || !*path) return(-0x9c); + + memset(data, 0, NWATALK_AD_DATASZ_EA); + len = mars_nwe_getxattr(path, NWATALK_AD_EA_META_XATTR, + data, NWATALK_AD_DATASZ_EA); + if (len == NWATALK_AD_DATASZ_EA && nwatalk_valid_ad_metadata(data)) + return(0); + + if (create_if_missing) { + nwatalk_init_ad_metadata(data); + return(0); + } + + return(-0x9c); +} + +static int nwatalk_write_ad_metadata(const char *path, + const uint8 data[NWATALK_AD_DATASZ_EA]) +{ + if (mars_nwe_setxattr(path, NWATALK_AD_EA_META_XATTR, + data, NWATALK_AD_DATASZ_EA, 0)) + return(-1); + return(0); +} + +static void nwatalk_store_host_value(uint8 *dst, size_t dst_len, + const void *src, size_t src_len) +{ + memset(dst, 0, dst_len); + if (src_len > dst_len) src_len = dst_len; + memcpy(dst, src, src_len); +} + +static void nwatalk_set_file_identity(const char *path, + uint8 data[NWATALK_AD_DATASZ_EA], + uint32 entry_id) +{ + struct stat st; + + if (stat(path, &st) == 0) { + nwatalk_store_host_value(data + NWATALK_ADEDOFF_PRIVDEV, + NWATALK_ADEDLEN_PRIVDEV, + &st.st_dev, sizeof(st.st_dev)); + nwatalk_store_host_value(data + NWATALK_ADEDOFF_PRIVINO, + NWATALK_ADEDLEN_PRIVINO, + &st.st_ino, sizeof(st.st_ino)); + } + memset(data + NWATALK_ADEDOFF_PRIVSYN, 0, NWATALK_ADEDLEN_PRIVSYN); + nwatalk_put_be32(data + NWATALK_ADEDOFF_PRIVID, entry_id); +} + +static int nwatalk_get_netatalk_entry_id_xattr(const char *path, uint32 *entry_id) +{ + uint8 data[NWATALK_AD_DATASZ_EA]; uint32 id; if (!entry_id) return(-0x9c); *entry_id = 0; if (!path || !*path) return(-0x9c); - memset(&d, 0, sizeof(d)); - len = mars_nwe_getxattr(path, MARS_NWE_AFP_ENTRY_ID_XATTR, &d, sizeof(d)); - if (len != sizeof(d) || d.version != MARS_NWE_AFP_ENTRY_ID_VERSION) + if (nwatalk_read_ad_metadata(path, data, 0) < 0) return(-0x9c); - id = GET_BE32(d.entry_id); + id = nwatalk_get_be32(data + NWATALK_ADEDOFF_PRIVID); id &= 0x7fffffffU; if (!id) return(-0x9c); @@ -85,7 +252,7 @@ static int nwatalk_get_mars_entry_id_xattr(const char *path, uint32 *entry_id) return(0); } #else -static int nwatalk_get_mars_entry_id_xattr(const char *path, uint32 *entry_id) +static int nwatalk_get_netatalk_entry_id_xattr(const char *path, uint32 *entry_id) { (void)path; if (entry_id) *entry_id = 0; @@ -97,7 +264,7 @@ int nwatalk_get_finder_info(const char *path, uint8 *finder_info, int finder_info_len) { #if XATTR_SUPPORT - ssize_t len; + uint8 data[NWATALK_AD_DATASZ_EA]; #endif if (!finder_info || finder_info_len < NWATALK_FINDER_INFO_LEN) { @@ -107,13 +274,10 @@ int nwatalk_get_finder_info(const char *path, uint8 *finder_info, memset(finder_info, 0, finder_info_len); #if XATTR_SUPPORT - if (path && *path) { - len = mars_nwe_getxattr(path, MARS_NWE_AFP_FINDER_INFO_XATTR, - finder_info, NWATALK_FINDER_INFO_LEN); - if (len == NWATALK_FINDER_INFO_LEN) - return(0); - memset(finder_info, 0, finder_info_len); - } + if (path && *path && + nwatalk_read_ad_metadata(path, data, 0) == 0) + memcpy(finder_info, data + NWATALK_ADEDOFF_FINDERI, + NWATALK_FINDER_INFO_LEN); #else (void)path; #endif @@ -126,11 +290,18 @@ int nwatalk_set_finder_info(const char *path, const uint8 *finder_info, int finder_info_len) { #if XATTR_SUPPORT + uint8 data[NWATALK_AD_DATASZ_EA]; + if (!path || !*path || !finder_info || finder_info_len < NWATALK_FINDER_INFO_LEN) return(-0x9c); - if (mars_nwe_setxattr(path, MARS_NWE_AFP_FINDER_INFO_XATTR, - finder_info, NWATALK_FINDER_INFO_LEN, 0)) { + if (nwatalk_read_ad_metadata(path, data, 1) < 0) + return(-0x9c); + + memcpy(data + NWATALK_ADEDOFF_FINDERI, finder_info, + NWATALK_FINDER_INFO_LEN); + + if (nwatalk_write_ad_metadata(path, data)) { int err = errno; XDPRINTF((3,0,"AFP FinderInfo xattr write failed for %s errno=%d", path, err)); return(-0x8c); /* no write privileges / cannot persist metadata */ @@ -161,7 +332,7 @@ int nwatalk_get_prodos_info(const char *path, uint8 *prodos_info, #if XATTR_SUPPORT if (path && *path) { - len = mars_nwe_getxattr(path, MARS_NWE_AFP_PRODOS_INFO_XATTR, + len = mars_nwe_getxattr(path, NWATALK_AD_EA_PRODOS_XATTR, prodos_info, NWATALK_PRODOS_INFO_LEN); if (len == NWATALK_PRODOS_INFO_LEN) return(0); @@ -182,7 +353,7 @@ int nwatalk_set_prodos_info(const char *path, const uint8 *prodos_info, if (!path || !*path || !prodos_info || prodos_info_len < NWATALK_PRODOS_INFO_LEN) return(-0x9c); - if (mars_nwe_setxattr(path, MARS_NWE_AFP_PRODOS_INFO_XATTR, + if (mars_nwe_setxattr(path, NWATALK_AD_EA_PRODOS_XATTR, prodos_info, NWATALK_PRODOS_INFO_LEN, 0)) { int err = errno; XDPRINTF((3,0,"AFP ProDOSInfo xattr write failed for %s errno=%d", path, err)); @@ -200,37 +371,16 @@ int nwatalk_set_prodos_info(const char *path, const uint8 *prodos_info, int nwatalk_get_afp_attributes(const char *path, uint16 *attributes) { -#if XATTR_SUPPORT - MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA d; - ssize_t len; -#endif + (void)path; if (!attributes) return(-0x9c); *attributes = 0; - if (!path || !*path) return(-0x9c); - -#if XATTR_SUPPORT - memset(&d, 0, sizeof(d)); - len = mars_nwe_getxattr(path, MARS_NWE_AFP_ATTRIBUTES_XATTR, - &d, sizeof(d)); - if (len == sizeof(d) && d.version == MARS_NWE_AFP_ATTRIBUTES_VERSION) { - *attributes = GET_BE16(d.attributes) & NWATALK_AFP_ATTR_STORED_MASK; - return(0); - } -#else - (void)path; -#endif - - return(-0x9c); + return(0); } int nwatalk_set_afp_attributes(const char *path, uint16 attributes) { -#if XATTR_SUPPORT - MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA d; - uint16 stored = 0; uint16 requested = attributes & ~NWATALK_AFP_ATTR_SETCLR; - uint16 current = 0; if (!path || !*path) return(-0x9c); @@ -239,46 +389,24 @@ int nwatalk_set_afp_attributes(const char *path, uint16 attributes) return(-0x9c); } - (void)nwatalk_get_afp_attributes(path, ¤t); - if (attributes & NWATALK_AFP_ATTR_SETCLR) - stored = current | requested; - else - stored = current & ~requested; - - memset(&d, 0, sizeof(d)); - d.version = MARS_NWE_AFP_ATTRIBUTES_VERSION; - U16_TO_BE16(stored, d.attributes); - - if (mars_nwe_setxattr(path, MARS_NWE_AFP_ATTRIBUTES_XATTR, - &d, sizeof(d), 0)) { - int err = errno; - XDPRINTF((3,0,"AFP attributes xattr write failed for %s attrs=0x%04x errno=%d", path, stored, err)); - return(-0x8c); - } - return(0); -#else - (void)path; - (void)attributes; - return(-0xbf); -#endif } int nwatalk_set_entry_id(const char *path, uint32 entry_id) { #if XATTR_SUPPORT - MARS_NWE_AFP_ENTRY_ID_XATTR_DATA d; + uint8 data[NWATALK_AD_DATASZ_EA]; if (!path || !*path) return(-0x9c); entry_id &= 0x7fffffffU; if (!entry_id) return(-0x9c); - memset(&d, 0, sizeof(d)); - d.version = MARS_NWE_AFP_ENTRY_ID_VERSION; - U32_TO_BE32(entry_id, d.entry_id); + if (nwatalk_read_ad_metadata(path, data, 1) < 0) + return(-0x9c); - if (mars_nwe_setxattr(path, MARS_NWE_AFP_ENTRY_ID_XATTR, - &d, sizeof(d), 0)) { + nwatalk_set_file_identity(path, data, entry_id); + + if (nwatalk_write_ad_metadata(path, data)) { int err = errno; XDPRINTF((5,0,"AFP entry-id xattr write ignored for %s entry=0x%08x errno=%d", path, entry_id, err)); @@ -295,9 +423,24 @@ int nwatalk_set_entry_id(const char *path, uint32 entry_id) int nwatalk_get_resource_fork_size(const char *path, uint32 *resource_size) { - (void)path; +#if XATTR_SUPPORT + ssize_t len; +#endif + if (!resource_size) return(-0x9c); *resource_size = 0; + +#if XATTR_SUPPORT + if (!path || !*path) return(-0x9c); + len = mars_nwe_getxattr(path, NWATALK_AD_EA_RESO_XATTR, NULL, 0); + if (len > 0) { + if (len > 0xffffffffL) len = 0xffffffffL; + *resource_size = (uint32)len; + } +#else + (void)path; +#endif + return(0); } @@ -307,5 +450,5 @@ int nwatalk_get_entry_id(const char *path, uint32 *entry_id) *entry_id = 0; if (!path || !*path) return(-0x9c); - return(nwatalk_get_mars_entry_id_xattr(path, entry_id)); + return(nwatalk_get_netatalk_entry_id_xattr(path, entry_id)); } diff --git a/src/nwconn.c b/src/nwconn.c index 72895a6..207868f 100644 --- a/src/nwconn.c +++ b/src/nwconn.c @@ -940,8 +940,8 @@ static uint32 afp_namespace_entry_id(int volume, const struct stat *stb) static uint32 afp_fallback_entry_id(int volume, const struct stat *stb) /* * Build a stable local AFP entry id from Unix identity data when the NetWare - * namespace handle cannot represent the object and mars_nwe AFP xattr has no stored - * CNID/AppleDouble id yet. This is only a legacy compatibility fallback. + * namespace handle cannot represent the object and Netatalk metadata has no + * stored CNID/AppleDouble id yet. This is only a legacy compatibility fallback. */ { uint32 hash = 2166136261U; @@ -971,8 +971,8 @@ static uint32 afp_get_or_create_entry_id(const char *unixname, int volume, int *fallback_out) /* * Return the mars_nwe namespace basehandle as AFP entry id whenever it can be - * represented. mars_nwe AFP xattr metadata remains a cache/legacy fallback for - * entries that cannot be mapped by the NetWare namespace table. + * represented. Netatalk metadata remains a cache/legacy fallback for entries + * that cannot be mapped by the NetWare namespace table. */ { uint32 entry_id = 0; @@ -1893,7 +1893,7 @@ static int afp_dos_path_join(int volume, char *dst, int dst_len, * * AFP 0x12 returns a DOSPathString, not the real Unix directory entry name. * The reverse lookup still walks the Unix volume tree because current AFP entry - * IDs are mars_nwe AFP xattr metadata IDs, but the path returned to the client + * IDs are nwatalk metadata IDs, but the path returned to the client * must be the DOS namespace alias that mars_nwe already uses elsewhere. */ { @@ -1915,11 +1915,11 @@ static int afp_find_dos_name_from_entry_id_rec(int volume, const char *rel_dir, AFP_DOS_NAME_SEARCH *search) /* - * Reverse-map the mars_nwe AFP entry-id xattr cache back to a DOS/NetWare path. + * Reverse-map the nwatalk entry-id metadata back to a DOS/NetWare path. * * NetWare's documented AFP 0x12 call is an entry-id-only lookup. mars_nwe's * current AFP ids are not the namespace base handles used by namspace.c; they - * are mars_nwe AFP xattr metadata ids. Reuse the existing volume table for + * are nwatalk metadata ids. Reuse the existing volume table for * the search root and the nwatalk entry-id helper for the per-entry identity, * but do not create fallback ids while scanning. The first conservative smoke * target is therefore an entry that was already cached by Get Entry ID/Get File diff --git a/tests/afp/README.md b/tests/afp/README.md index 8b80d79..024bac9 100644 --- a/tests/afp/README.md +++ b/tests/afp/README.md @@ -18,9 +18,10 @@ AFP handlers must keep NetWare semantics on mars_nwe core paths: - rights checks use mars_nwe trustee/effective-rights logic; - Hidden, System, Archive, timestamps, creator/modifier, backup/archive, and related metadata use mars_nwe attribute/archive/fileinfo paths; -- AFP-only xattrs are limited to `user.org.mars-nwe.afp.entry-id`, - `user.org.mars-nwe.afp.finder-info`, `user.org.mars-nwe.afp.prodos-info`, and - future AFP-only bits in `user.org.mars-nwe.afp.attributes`. +- AFP metadata uses Netatalk-family xattrs: `user.org.netatalk.Metadata` + for AppleDouble `adouble:ea` FinderInfo and CNID/entry-id metadata, plus + `user.org.netatalk.ProDOS` for the AFP 2.0 ProDOSInfo bytes that Netatalk's + 402-byte EA header does not carry. Resource forks remain unsupported. AFP resource-fork requests should return the documented completion code instead of inventing a parallel storage backend. @@ -38,6 +39,7 @@ NSS_VOLUME OTHERFS_XATTR Finder/Mac metadata: Netatalk adouble:ea xattr org.netatalk.Metadata + AFP 2.0 ProDOSInfo: Netatalk-family xattr org.netatalk.ProDOS resource fork: Netatalk adouble:ea xattr org.netatalk.ResourceFork ``` @@ -168,10 +170,8 @@ DOS name lookup, and AFP `0x13` deleted-file metadata. It also checks the AFP xattrs on the tested Unix file: ```text -user.org.mars-nwe.afp.finder-info -user.org.mars-nwe.afp.prodos-info -user.org.mars-nwe.afp.attributes -user.org.mars-nwe.afp.entry-id +user.org.netatalk.Metadata +user.org.netatalk.ProDOS ``` Use `--no-log` when the server log is unavailable or collected separately. Use diff --git a/tests/afp/afp_endpoint_inventory.py b/tests/afp/afp_endpoint_inventory.py index 7651ac7..34ca4fb 100755 --- a/tests/afp/afp_endpoint_inventory.py +++ b/tests/afp/afp_endpoint_inventory.py @@ -61,15 +61,15 @@ HANDLER_CANDIDATES_BY_CALL: Dict[int, Tuple[str, ...]] = { # implementation shape and backend families, not final compatibility status. INLINE_ENDPOINTS: Dict[int, Tuple[str, str, Tuple[str, ...]]] = { 0x01: ("inline AFP create directory case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle")), - 0x02: ("inline AFP create file case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle", "AFP-only xattrs via nwatalk")), + 0x02: ("inline AFP create file case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle", "Netatalk-family xattrs via nwatalk")), 0x03: ("inline AFP delete case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle")), - 0x07: ("inline AFP rename case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle", "AFP-only xattrs via nwatalk")), + 0x07: ("inline AFP rename case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle", "Netatalk-family xattrs via nwatalk")), 0x08: ("inline AFP open file fork case", "implemented", ("mars_nwe path/namespace", "mars_nwe file handles/I/O")), - 0x09: ("inline AFP set file information case", "implemented", ("mars_nwe path/namespace", "mars_nwe attributes/archive/fileinfo", "mars_nwe trustee/rights", "AFP-only xattrs via nwatalk")), + 0x09: ("inline AFP set file information case", "implemented", ("mars_nwe path/namespace", "mars_nwe attributes/archive/fileinfo", "mars_nwe trustee/rights", "Netatalk-family xattrs via nwatalk")), 0x0B: ("inline AFP alloc temporary dir handle case", "implemented", ("mars_nwe path/namespace", "mars_nwe directory handles")), 0x0D: ("inline AFP 2.0 create directory case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle")), - 0x0E: ("inline AFP 2.0 create file case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle", "AFP-only xattrs via nwatalk")), - 0x10: ("inline AFP 2.0 set file information case", "implemented", ("mars_nwe path/namespace", "mars_nwe attributes/archive/fileinfo", "mars_nwe trustee/rights", "AFP-only xattrs via nwatalk")), + 0x0E: ("inline AFP 2.0 create file case", "implemented", ("mars_nwe path/namespace", "mars_nwe object lifecycle", "Netatalk-family xattrs via nwatalk")), + 0x10: ("inline AFP 2.0 set file information case", "implemented", ("mars_nwe path/namespace", "mars_nwe attributes/archive/fileinfo", "mars_nwe trustee/rights", "Netatalk-family xattrs via nwatalk")), 0x13: ("inline AFP deleted-info case", "implemented", ("mars_nwe salvage/deleted-entry backend", "AFP FinderInfo snapshot")), } @@ -130,7 +130,7 @@ BACKEND_RULES: Tuple[Tuple[str, Tuple[str, ...]], ...] = ( "unlink", "rmdir", )), - ("AFP-only xattrs via nwatalk", ( + ("Netatalk-family xattrs via nwatalk", ( "nwatalk_get_entry_id", "nwatalk_set_entry_id", "nwatalk_get_finder_info", diff --git a/tests/afp/afp_smoke_suite.sh b/tests/afp/afp_smoke_suite.sh index 1d3d3a8..308f13c 100755 --- a/tests/afp/afp_smoke_suite.sh +++ b/tests/afp/afp_smoke_suite.sh @@ -61,7 +61,7 @@ Options: The script expects the compiled smoke helpers in the same directory as this script. It prints commands with the password masked, writes an AFP-only log -snippet, and runs getfattr checks for mars_nwe AFP xattrs. +snippet, and runs getfattr checks for Netatalk-family AFP xattrs. USAGE } @@ -885,20 +885,14 @@ fi if command -v getfattr >/dev/null 2>&1; then run_cmd \ - "Linux xattr: AFP FinderInfo" \ - "getfattr -n user.org.mars-nwe.afp.finder-info -e hex '$UNIX_PATH'" \ - getfattr -n user.org.mars-nwe.afp.finder-info -e hex "$UNIX_PATH" + "Linux xattr: Netatalk AFP metadata" \ + "getfattr -n user.org.netatalk.Metadata -e hex '$UNIX_PATH'" \ + getfattr -n user.org.netatalk.Metadata -e hex "$UNIX_PATH" run_cmd \ "Linux xattr: AFP ProDOSInfo" \ - "getfattr -n user.org.mars-nwe.afp.prodos-info -e hex '$UNIX_PATH'" \ - getfattr -n user.org.mars-nwe.afp.prodos-info -e hex "$UNIX_PATH" - - run_optional_cmd \ - "Linux xattr: AFP Attributes (optional AFP-only bits)" \ - "getfattr -n user.org.mars-nwe.afp.attributes -e hex '$UNIX_PATH'" \ - getfattr -n user.org.mars-nwe.afp.attributes -e hex "$UNIX_PATH" || \ - emit "AFP-only attributes xattr is absent; this is expected when the tested Hidden/System/Archive bits are stored through the NetWare attribute path." + "getfattr -n user.org.netatalk.ProDOS -e hex '$UNIX_PATH'" \ + getfattr -n user.org.netatalk.ProDOS -e hex "$UNIX_PATH" run_cmd \ "Linux xattr: NetWare FileInfo metadata" \ @@ -912,8 +906,8 @@ if command -v getfattr >/dev/null 2>&1; then run_cmd \ "Linux xattr: AFP Entry ID" \ - "getfattr -n user.org.mars-nwe.afp.entry-id -e hex '$UNIX_PATH'" \ - getfattr -n user.org.mars-nwe.afp.entry-id -e hex "$UNIX_PATH" + "getfattr -n user.org.netatalk.Metadata -e hex '$UNIX_PATH'" \ + getfattr -n user.org.netatalk.Metadata -e hex "$UNIX_PATH" else section "Linux xattr checks" emit "getfattr not found; install the attr package to collect xattr output."