nwatalk: cache AFP fallback entry ids in xattrs
All checks were successful
Source release / source-package (push) Successful in 48s

The AFP smoke endpoints can now read mars_nwe-owned entry ids from the versioned org.mars-nwe.afp.entry-id xattr, but a newly discovered file still had to fall back to the temporary stat-derived id on every request until a real CNID allocator exists.

Preserve the existing WebSDK/NWAFP response semantics while making that fallback sticky: when Get Entry ID, Get File Information, or Scan File Information has no mars_nwe xattr and no Netatalk/libatalk AppleDouble/CNID id, derive the existing compatibility id and cache it through nwatalk_set_entry_id().  The first request still logs fallback so diagnostics remain honest about the id origin; subsequent requests should read the xattr directly and avoid re-entering the stat fallback path.

Keep the write narrowly scoped to mars_nwe's private AFP metadata namespace.  The payload is versioned, big-endian, and stored through the nwxattr helper, so Linux persists it as user.org.mars-nwe.afp.entry-id while source-level code continues to use the Netatalk-style org.mars-nwe.afp.entry-id name.  This does not implement CNID allocation, parent-id lookup, entry-id-only resolution, FinderInfo mutation beyond the existing smoke path, or resource-fork semantics.

Tests:

- git diff --check

- cmake --build build-xattr-off --target nwconn with ENABLE_NETATALK_LIBATALK=OFF

- cmake --build build-xattr-on --target nwconn with ENABLE_NETATALK_LIBATALK=ON against Netatalk 4.4.3 headers plus local link stubs
This commit is contained in:
OpenAI
2026-05-30 09:55:44 +00:00
committed by Mario Fetka
parent 8c99eaa68b
commit 4637f3ee57
5 changed files with 110 additions and 44 deletions

View File

@@ -156,6 +156,36 @@ int nwatalk_set_finder_info(const char *path, const uint8 *finder_info,
#endif
}
int nwatalk_set_entry_id(const char *path, uint32 entry_id)
{
#if XATTR_SUPPORT
MARS_NWE_AFP_ENTRY_ID_XATTR_DATA d;
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 (mars_nwe_setxattr(path, MARS_NWE_AFP_ENTRY_ID_XATTR,
&d, sizeof(d), 0)) {
int err = errno;
XDPRINTF((5,0,"AFP entry-id xattr write ignored for %s entry=0x%08x errno=%d",
path, entry_id, err));
return(-0x8c);
}
return(0);
#else
(void)path;
(void)entry_id;
return(-0xbf);
#endif
}
int nwatalk_get_resource_fork_size(const char *path, uint32 *resource_size)
{
#if NETATALK_SUPPORT

View File

@@ -507,6 +507,34 @@ static uint32 afp_fallback_entry_id(int volume, const struct stat *stb)
}
static uint32 afp_get_or_create_entry_id(const char *unixname, int volume,
const struct stat *stb,
int *fallback_out)
/*
* Return a persistent mars_nwe AFP entry id when available. If neither the
* mars_nwe xattr nor Netatalk/libatalk metadata contains an id yet, derive the
* existing stat-backed compatibility id and cache that id in mars_nwe's private
* AFP xattr namespace. The first caller still records fallback diagnostics so
* the log remains honest about the id origin; follow-up requests should read the
* xattr directly and no longer need the temporary stat derivation path.
*/
{
uint32 entry_id = 0;
int result;
result = nwatalk_get_entry_id(unixname, &entry_id);
if (!result && entry_id) {
if (fallback_out) *fallback_out = 0;
return(entry_id);
}
entry_id = afp_fallback_entry_id(volume, stb);
if (fallback_out) *fallback_out = 1;
(void)nwatalk_set_entry_id(unixname, entry_id);
return(entry_id);
}
static int afp_get_entry_id_from_name(uint8 *afp_req, int afp_len,
uint8 *response)
{
@@ -561,15 +589,13 @@ static int afp_get_entry_id_from_name(uint8 *afp_req, int afp_len,
return(-0x9c); /* Invalid Path */
}
result = nwatalk_get_entry_id(unixname, &entry_id);
if (result < 0 || !entry_id)
entry_id = afp_fallback_entry_id(volume, &stbuff);
entry_id = afp_get_or_create_entry_id(unixname, volume, &stbuff, &result);
U32_TO_BE32(entry_id, response);
XDPRINTF((3,0, "AFP Get Entry ID From Name: vol=%d entry=0x%08x path='%s' reply_entry=0x%08x%s",
(int)volume_number, request_entry_id,
visable_data(afp_req + 7, path_len), entry_id,
(result < 0) ? " fallback" : ""));
result ? " fallback" : ""));
return(4);
}
@@ -612,9 +638,7 @@ static int afp_get_entry_id_from_netware_handle(uint8 *afp_req, int afp_len,
volume = afp_volume_from_unixname((char *)unixname);
if (volume < 0) volume = 0;
result = nwatalk_get_entry_id((char *)unixname, &entry_id);
if (result < 0 || !entry_id)
entry_id = afp_fallback_entry_id(volume, &stbuff);
entry_id = afp_get_or_create_entry_id((char *)unixname, volume, &stbuff, &result);
response[0] = (uint8)volume;
U32_TO_BE32(entry_id, response + 1);
@@ -622,7 +646,7 @@ static int afp_get_entry_id_from_netware_handle(uint8 *afp_req, int afp_len,
XDPRINTF((3,0, "AFP Get Entry ID From NetWare Handle: handle=%u volume=%d unix='%s' entry=0x%08x%s",
fhandle, volume, unixname, entry_id,
(result < 0) ? " fallback" : ""));
result ? " fallback" : ""));
return(6);
}
@@ -844,14 +868,12 @@ static int afp_get_entry_id_from_path_name(uint8 *afp_req, int afp_len,
return(-0x9c); /* Invalid Path */
}
result = nwatalk_get_entry_id(unixname, &entry_id);
if (result < 0 || !entry_id)
entry_id = afp_fallback_entry_id(volume, &stbuff);
entry_id = afp_get_or_create_entry_id(unixname, volume, &stbuff, &result);
U32_TO_BE32(entry_id, response);
XDPRINTF((3,0, "AFP Get Entry ID From Path Name: dh=%d path='%s' entry=0x%08x%s",
(int)dir_handle, visable_data(afp_req + 3, path_len), entry_id,
(result < 0) ? " fallback" : ""));
result ? " fallback" : ""));
return(4);
}
@@ -956,16 +978,11 @@ static int afp_fill_file_info_response(const char *unixname,
uint32 parent_id = 0;
uint32 resource_size = 0;
uint8 finder_info[NWATALK_FINDER_INFO_LEN];
int result;
if (stat(unixname, &stbuff))
return(-0x9c); /* Invalid Path */
result = nwatalk_get_entry_id(unixname, &entry_id);
if (result < 0 || !entry_id) {
entry_id = afp_fallback_entry_id(volume, &stbuff);
if (fallback_out) *fallback_out = 1;
} else if (fallback_out) *fallback_out = 0;
entry_id = afp_get_or_create_entry_id(unixname, volume, &stbuff, fallback_out);
memset(response, 0, 120);
U32_TO_BE32(entry_id, response + 0);
@@ -1280,7 +1297,6 @@ static int afp_scan_file_information(uint8 *afp_req, int afp_len,
struct stat stbuff;
int child_fallback = 0;
uint32 child_entry_id = 0;
int child_result;
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
@@ -1289,11 +1305,8 @@ static int afp_scan_file_information(uint8 *afp_req, int afp_len,
if (stat(childname, &stbuff))
continue;
child_result = nwatalk_get_entry_id(childname, &child_entry_id);
if (child_result < 0 || !child_entry_id) {
child_entry_id = afp_fallback_entry_id(volume, &stbuff);
child_fallback = 1;
}
child_entry_id = afp_get_or_create_entry_id(childname, volume, &stbuff,
&child_fallback);
if (last_seen_id && !seen) {
if (child_entry_id == last_seen_id)