diff --git a/TODO.md b/TODO.md index 3c65c2c..8f0e7fb 100644 --- a/TODO.md +++ b/TODO.md @@ -190,7 +190,8 @@ Follow-up: Current status: -- `NCP 0x23` AFP calls still return invalid namespace. +- `NCP 0x23` still returns invalid namespace for AFP calls that are not implemented yet. +- `AFP Get Entry ID From Path Name` is the first implemented AFP subfunction when the optional Netatalk/libatalk backend is available. - The AFP dispatcher now decodes the WebSDK/NWAFP subfunction number in diagnostics so real client probes can be mapped to the corresponding AFP call before implementation work starts. @@ -207,9 +208,11 @@ Follow-up: - Candidate libatalk pieces include the new AppleDouble/Finder Info/resource fork helper wrappers, plus future CNID/directory-id helpers, attribute mapping, and filename conversion. -- Keep returning invalid namespace until MARS-NWE has a real per-volume Mac - namespace/AFP metadata layer. Do not return success for AFP calls without +- Keep returning invalid namespace for AFP calls that still lack a real per-volume Mac + namespace/AFP metadata layer. Do not return success for additional AFP calls without data/resource fork and Finder Info semantics. +- Replace the temporary stat-derived AFP entry-id fallback with a persistent + CNID/directory-id mapping once the libatalk/CNID backend is integrated. ## Deferred / optional protocol work diff --git a/include/nwatalk.h b/include/nwatalk.h index dde080c..4285520 100644 --- a/include/nwatalk.h +++ b/include/nwatalk.h @@ -9,5 +9,6 @@ int nwatalk_backend_available(void); int nwatalk_get_finder_info(const char *path, uint8 *finder_info, int finder_info_len); int nwatalk_get_resource_fork_size(const char *path, uint32 *resource_size); +int nwatalk_get_entry_id(const char *path, uint32 *entry_id); #endif diff --git a/src/nwatalk.c b/src/nwatalk.c index 16536cc..3aeeeb9 100644 --- a/src/nwatalk.c +++ b/src/nwatalk.c @@ -100,3 +100,33 @@ int nwatalk_get_resource_fork_size(const char *path, uint32 *resource_size) return(-0xbf); /* invalid namespace / backend unavailable */ #endif } + + +int nwatalk_get_entry_id(const char *path, uint32 *entry_id) +{ +#if NETATALK_SUPPORT + struct adouble ad; + struct stat stbuff; + uint32_t id; + int result; + + if (!entry_id) return(-0x9c); + *entry_id = 0; + if (!path || !*path) return(-0x9c); + + if (stat(path, &stbuff)) return(-0x9c); + + result = nwatalk_open_adouble(path, &ad); + if (result < 0) return(result); + + id = ad_getid(&ad, stbuff.st_dev, stbuff.st_ino, 0, NULL); + if (id) *entry_id = (uint32)id; + + ad_close(&ad, 0); + return(id ? 0 : -0x9c); +#else + (void)path; + (void)entry_id; + return(-0xbf); /* invalid namespace / backend unavailable */ +#endif +} diff --git a/src/nwconn.c b/src/nwconn.c index 1ad0337..150af74 100644 --- a/src/nwconn.c +++ b/src/nwconn.c @@ -32,6 +32,8 @@ # define LOC_RW_BUFFERSIZE 512 #endif #include +#include +#include #if !CALL_NWCONN_OVER_SOCKET #include #include @@ -476,6 +478,85 @@ static const char *afp_call_name(int ufunc) } } +static int afp_request_offset(void) +/* + * AFP NCP 0x23 calls are documented with a two byte high-low request + * length followed by the AFP subfunction. Some old debug paths treated the + * first payload byte as the subfunction. Accept both layouts so diagnostics + * and the first implemented AFP call work with either requester encoding. + */ +{ + if (requestlen >= 3) { + int plen = GET_BE16(requestdata); + if (plen == requestlen - 2) + return(2); + } + return(0); +} + +static uint32 afp_fallback_entry_id(int volume, const struct stat *stb) +/* + * Build a stable local AFP entry id from Unix identity data when libatalk has + * no stored CNID/AppleDouble id yet. This is not a NetWare-internal directory + * base number; it is only the AFP id returned by the path-name probe. + */ +{ + uint32 hash = 2166136261U; + const uint8 *p; + size_t len; + +#define AFP_HASH_BYTES(ptr, size) do { \ + p = (const uint8 *)(ptr); \ + len = (size); \ + while (len--) { hash ^= *p++; hash *= 16777619U; } \ + } while (0) + + AFP_HASH_BYTES(&volume, sizeof(volume)); + AFP_HASH_BYTES(&stb->st_dev, sizeof(stb->st_dev)); + AFP_HASH_BYTES(&stb->st_ino, sizeof(stb->st_ino)); +#undef AFP_HASH_BYTES + + hash &= 0x7fffffffU; + if (!hash) hash = 1; + return(hash); +} + +static int afp_get_entry_id_from_path_name(uint8 *afp_req, int afp_len, + uint8 *response) +{ + uint8 dir_handle; + int path_len; + int volume; + char unixname[PATH_MAX]; + struct stat stbuff; + uint32 entry_id = 0; + int result; + + if (afp_len < 3) return(-0x7e); /* NCP Boundary Check Failed */ + + dir_handle = afp_req[1]; + path_len = (int)afp_req[2]; + if (path_len < 0 || afp_len < 3 + path_len) return(-0x7e); + + if (!nwatalk_backend_available()) return(-0xbf); /* invalid namespace */ + + volume = conn_get_kpl_unxname(unixname, sizeof(unixname), + (int)dir_handle, afp_req + 3, path_len); + if (volume < 0) return(volume); + + 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); + + 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" : "")); + return(4); +} + static int handle_ncp_serv(void) { int function = (int)ncprequest->function; @@ -2404,7 +2485,10 @@ static int handle_ncp_serv(void) } break; case 0x23 : { /* div AFP Calls */ - int ufunc = (int) *requestdata; + int afp_off = afp_request_offset(); + uint8 *afp_req = requestdata + afp_off; + int afp_len = requestlen - afp_off; + int ufunc = (afp_len > 0) ? (int)*afp_req : -1; /* * NetWare AFP calls are server-side NCP entry points for * Mac namespace semantics: AFP entry IDs, Finder Info, @@ -2420,17 +2504,22 @@ static int handle_ncp_serv(void) * (0x0d/0x0e), Get/Set File Information (0x0f/0x10), and * Scan File Information (0x11). * - * Netatalk/libatalk can be enabled at build time as an - * optional local metadata backend for AppleDouble/Finder - * Info/resource-fork access, but mars_nwe must still - * decode and answer the NetWare NCP calls itself. Do not - * proxy these calls to afpd or report success until the - * required Mac namespace semantics exist. + * Implement the path-name entry-id probe first because the + * SDK helpers use it to test AFP support. It still requires + * the optional libatalk backend to be present; without a Mac + * namespace backend, keep returning invalid namespace. */ - XDPRINTF((3,0, "AFP call rejected: ufunc=0x%02x (%s), Mac namespace unavailable, libatalk backend=%s", - ufunc, afp_call_name(ufunc), - nwatalk_backend_available() ? "enabled" : "disabled")); - completition=0xbf; /* we say invalid namespace here */ + if (ufunc == 0x0c) { + int result = afp_get_entry_id_from_path_name(afp_req, + afp_len, responsedata); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } else { + XDPRINTF((3,0, "AFP call rejected: ufunc=0x%02x (%s), Mac namespace unavailable, libatalk backend=%s", + ufunc, afp_call_name(ufunc), + nwatalk_backend_available() ? "enabled" : "disabled")); + completition=0xbf; /* we say invalid namespace here */ + } } break; case 0x3b : /* commit file to disk */