afp: store metadata in Netatalk xattrs
Source release / source-package (push) Successful in 1m30s

This commit is contained in:
Mario Fetka
2026-06-22 08:56:04 +02:00
parent 7399a7f3b4
commit ec2b645994
6 changed files with 276 additions and 136 deletions
+14 -11
View File
@@ -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
+234 -91
View File
@@ -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 <string.h>
#include <sys/stat.h>
#if XATTR_SUPPORT
#include <errno.h>
#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, &current);
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));
}
+7 -7
View File
@@ -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
+7 -7
View File
@@ -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
+6 -6
View File
@@ -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",
+8 -14
View File
@@ -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."