From 800ca21affbbc3f528d0021b9d68e01cd5f207b1 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Tue, 2 Jun 2026 19:25:46 +0000 Subject: [PATCH] docs: split afp selector notes --- AI.md | 25 +++ TODO.md | 40 +++++ src/nwconn.c | 421 +++++++++++++++++++++++++++++++++++---------------- 3 files changed, 352 insertions(+), 134 deletions(-) diff --git a/AI.md b/AI.md index 272d5d9..3ec2ac2 100644 --- a/AI.md +++ b/AI.md @@ -1015,3 +1015,28 @@ Latest endpoint audit checkpoint from patch 0248: unlisted gaps and ignoring 5.x-only endpoints. Next patch number should be `0249`. + +Latest endpoint audit checkpoint from patch 0249: + +- Continued NDK-first after the TTS family with the in-scope AFP/Mac namespace + family `35/01` through `35/19` in `src/nwconn.c`. The NDK lists these AFP + calls for NetWare 2.x/3.x/4.x, so they remain relevant even though the same + pages also mention 5.x. +- Converted the AFP dispatcher from grouped `if`/`else` pairs into an explicit + `switch (ufunc)` with one case per NDK selector: create directory/file, + delete, entry-ID lookup by name/handle/path, rename, open file fork, + get/set/scan file information, AFP 2.0 create/get/set/scan variants, DOS-name + lookup, and deleted-file Macintosh-info lookup. +- Runtime behavior is unchanged. Some selectors still share the same helper, + but each selector now has its own case-local `Request:` and `Response:` + summary matching the endpoint-audit rule. Do not regroup these cases in a + later cleanup. +- Future AFP work must keep stable AFP entry IDs/CNIDs, FinderInfo/ProDOSInfo, + data/resource fork identity, directory enumeration state, and Salvage metadata + grounded in real provider state. Do not fake AFP replies from plain Unix path + names when the NDK requires namespace identity or metadata persistence. +- The next NDK-first pass should continue with the next documented NetWare + 1.x/2.x/3.x endpoint or planned 4.x endpoint after the AFP family, skipping + unlisted gaps and ignoring 5.x-only endpoints. + +Next patch number should be `0250`. diff --git a/TODO.md b/TODO.md index 93c6793..45461b5 100644 --- a/TODO.md +++ b/TODO.md @@ -1652,3 +1652,43 @@ Follow-up: - Continue NDK-first with the next documented NetWare 1.x/2.x/3.x endpoint or planned 4.x endpoint after the TTS family, keeping one case and Request/Response comment per selector. + +### AFP selector split 35/01..19 + +Current status: + +- Patch `0249` audits the NDK-listed, NetWare-2.x/3.x/4.x-relevant AFP/Mac + namespace family `0x2222/35` in `src/nwconn.c`. +- The old grouped AFP `if`/`else` dispatcher has been converted to one explicit + `switch (ufunc)` case per selector, each with adjacent Request/Response notes: + - `35/01` AFP Create Directory + - `35/02` AFP Create File + - `35/03` AFP Delete + - `35/04` AFP Get Entry ID From Name + - `35/05` AFP Get File Information + - `35/06` AFP Get Entry ID From NetWare Handle + - `35/07` AFP Rename + - `35/08` AFP Open File Fork + - `35/09` AFP Set File Information + - `35/10` AFP Scan File Information + - `35/11` AFP Alloc Temporary Directory Handle + - `35/12` AFP Get Entry ID From Path Name + - `35/13` AFP 2.0 Create Directory + - `35/14` AFP 2.0 Create File + - `35/15` AFP 2.0 Get File Or Directory Information + - `35/16` AFP 2.0 Set File Information + - `35/17` AFP 2.0 Scan File Information + - `35/18` AFP Get DOS Name From Entry ID + - `35/19` AFP Get Macintosh Info On Deleted File +- Runtime behavior is unchanged; shared helpers remain shared, but no selector + is documented only through a grouped sibling case. +- Future AFP implementation work requires stable entry IDs/CNIDs, FinderInfo and + ProDOSInfo persistence, data/resource fork identity, directory enumeration + state, and Salvage metadata. Do not synthesize AFP success from partial Unix + path data alone. + +Follow-up: + +- Continue NDK-first with the next documented NetWare 1.x/2.x/3.x endpoint or + planned 4.x endpoint after the AFP family, keeping one case and + Request/Response comment per selector. diff --git a/src/nwconn.c b/src/nwconn.c index 1675944..72d2633 100644 --- a/src/nwconn.c +++ b/src/nwconn.c @@ -6117,140 +6117,293 @@ static int handle_ncp_serv(void) * missing-endpoint source stubs are needed for the current * 1.x/2.x/3.x plus planned-4.x scope. */ - if (ufunc == 0x01 || ufunc == 0x0d) { - /* 35/01 AFP Create Directory and 35/13 AFP 2.0 Create - * Directory. Request carries volume/base/path and - * creation metadata; reply has no extra payload. - * MARS-NWE routes both through the same path-backed - * directory creation helper. */ - int result = afp_create_directory(afp_req, - afp_len, responsedata, - afp_call_name(ufunc)); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x02 || ufunc == 0x0e) { - /* 35/02 AFP Create File and 35/14 AFP 2.0 Create File. - * Request carries volume/base/path, attributes and AFP - * metadata fields; reply has no extra payload. Current - * compatibility path creates the data fork/file and AFP - * metadata through the shared helper. */ - int result = afp_create_file(afp_req, - afp_len, responsedata, - afp_call_name(ufunc)); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x03) { - /* 35/03 AFP Delete. Request selects the AFP entry/path; - * reply has no payload. Current helper maps the AFP - * selector to the shared filesystem delete path. */ - int result = afp_delete_object(afp_req, - afp_len, afp_call_name(ufunc)); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x07) { - /* 35/07 AFP Rename. Request carries source and target - * AFP path/name data; reply has no payload. */ - int result = afp_rename_object(afp_req, - afp_len, afp_call_name(ufunc)); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x04) { - /* 35/04 AFP Get Entry ID From Name. Request supplies - * volume/base AFP ID and a modifying path string; reply - * returns AFP entry information. Until persistent CNID - * lookup exists this is the SYS:-style path-backed - * compatibility subset. */ - int result = afp_get_entry_id_from_name(afp_req, - afp_len, responsedata); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x06) { - /* 35/06 AFP Get Entry ID From NetWare Handle. Request - * contains an open NetWare file handle; reply returns the - * corresponding AFP entry ID for the mapped Unix path. */ - int result = afp_get_entry_id_from_netware_handle(afp_req, - afp_len, responsedata); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x08) { - /* 35/08 AFP Open File Fork. Request selects AFP entry - * and fork; reply returns a normal NetWare file handle. - * Current compatibility opens the data fork read-only for - * the path-backed subset. */ - int result = afp_open_file_fork(afp_req, - afp_len, responsedata); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x0b) { - /* 35/11 AFP Alloc Temporary Directory Handle. Request - * carries AFP path selection; reply returns a temporary - * NetWare directory handle plus effective rights. */ - int result = afp_alloc_temporary_dir_handle(afp_req, - afp_len, responsedata); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x0c) { - /* 35/12 AFP Get Entry ID From Path Name. Request uses a - * path-name selector; reply returns AFP entry ID data. - * SDK helpers use this as an AFP-support probe. */ - 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 if (ufunc == 0x12) { - /* 35/18 AFP Get DOS Name From Entry ID. Request carries - * an AFP entry ID; reply returns the DOS/NetWare name. */ - int result = afp_get_dos_name_from_entry_id(afp_req, - afp_len, responsedata); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x13) { - /* 35/19 AFP Get Macintosh Info On Deleted File. Request - * selects a deleted AFP entry; reply returns FinderInfo, - * ProDOSInfo, resource fork size, and deleted filename - * from the shared Salvage snapshot. */ - int result = afp_get_macintosh_info_on_deleted_file(afp_req, - afp_len, responsedata); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x05 || ufunc == 0x0f) { - /* 35/05 AFP Get File Information and 35/15 AFP 2.0 Get - * File Or Directory Information. Request selects AFP - * entry/path; reply returns AFP metadata and filesystem - * information. Both use the same read-only helper until - * richer persistent entry-id lookup exists. */ - int result = afp_get_file_information(afp_req, - afp_len, responsedata, - afp_call_name(ufunc)); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x09 || ufunc == 0x10) { - /* 35/09 AFP Set File Information and 35/16 AFP 2.0 Set - * File Information. Request carries AFP metadata update - * fields; reply has no payload. Current compatibility - * accepts FinderInfo plus metadata-only DOS attribute - * writes through the xattr-backed AFP metadata path. */ - int result = afp_set_file_information(afp_req, - afp_len, afp_call_name(ufunc)); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else if (ufunc == 0x0a || ufunc == 0x11) { - /* 35/10 AFP Scan File Information and 35/17 AFP 2.0 Scan - * File Information. Request carries search sequence and - * AFP path/attribute filters; reply returns one scanned - * AFP file-information record in the conservative helper. */ - int result = afp_scan_file_information(afp_req, - afp_len, responsedata, - afp_call_name(ufunc)); - if (result > -1) data_len = result; - else completition = (uint8)-result; - } else { - XDPRINTF((3,0, - "INFO AFP 35/%d UNKNOWN name=\"%s\" backend=\"%s\" fn=0x23 sub=0x%02x result=0xbf", - ufunc, afp_call_name(ufunc), - nwatalk_backend_available() ? "enabled" : "disabled", - ufunc)); - completition=0xbf; /* we say invalid namespace here */ + switch (ufunc) { + case 0x01: { /* SDK 35/01 AFP Create Directory */ + /* + * Request: SubFunctionCode=1, byte VolumeNumber, + * long BaseDirectoryID, byte Reserved, + * byte FinderInfo[32], byte PathLen, + * byte PathModString[PathLen]. + * Response: long NewDirectoryID. Requires an AFP + * namespace directory create with stable entry IDs. + */ + int result = afp_create_directory(afp_req, + afp_len, responsedata, + afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x02: { /* SDK 35/02 AFP Create File */ + /* + * Request: SubFunctionCode=2, byte VolumeNumber, + * long BaseDirectoryID, byte DeleteExistingFileFlag, + * byte FinderInfo[32], byte PathLen, + * byte PathModString[PathLen]. + * Response: long NewDirectoryID. Requires data-fork + * create plus AFP metadata persistence. + */ + int result = afp_create_file(afp_req, + afp_len, responsedata, + afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x03: { /* SDK 35/03 AFP Delete */ + /* + * Request: SubFunctionCode=3, byte VolumeNumber, + * long BaseDirectoryID, byte PathLen, + * byte PathModString[PathLen]. + * Response: no payload. Requires AFP namespace delete + * semantics for files and empty directories. + */ + int result = afp_delete_object(afp_req, + afp_len, afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x04: { /* SDK 35/04 AFP Get Entry ID From Name */ + /* + * Request: SubFunctionCode=4, byte VolumeNumber, + * long MacBaseDirectoryID, byte PathStringLen, + * byte PathModString[PathStringLen]. + * Response: long TargetEntryID. Requires stable AFP + * CNID/path lookup. + */ + int result = afp_get_entry_id_from_name(afp_req, + afp_len, responsedata); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x05: { /* SDK 35/05 AFP Get File Information */ + /* + * Request: SubFunctionCode=5, byte VolumeNumber, + * long MacBaseDirectoryID, word RequestBitMap, + * byte PathStringLen, + * byte PathModString[PathStringLen]. + * Response: AFP file/directory metadata selected by + * RequestBitMap. Requires AFP metadata plus filesystem + * stat information. + */ + int result = afp_get_file_information(afp_req, + afp_len, responsedata, + afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x06: { /* SDK 35/06 AFP Get Entry ID From NetWare Handle */ + /* + * Request: SubFunctionCode=6, + * byte NetWareFileHandle[6]. + * Response: byte VolumeID, long TargetEntryID, + * byte ForkIndicator. Requires open-handle to AFP + * namespace identity mapping. + */ + int result = afp_get_entry_id_from_netware_handle(afp_req, + afp_len, responsedata); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x07: { /* SDK 35/07 AFP Rename */ + /* + * Request: SubFunctionCode=7, byte VolumeNumber, + * long SourceBaseDirectoryID, source path, long + * DestinationBaseDirectoryID, destination path. + * Response: no payload. Requires AFP namespace rename + * and metadata-preserving move semantics. + */ + int result = afp_rename_object(afp_req, + afp_len, afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x08: { /* SDK 35/08 AFP Open File Fork */ + /* + * Request: SubFunctionCode=8, byte VolumeNumber, + * long MacBaseDirectoryID, byte ForkIndicator, access + * and path/fork selection fields. + * Response: byte NetWareFileHandle[6] plus open file + * metadata. Requires real AFP data/resource fork open. + */ + int result = afp_open_file_fork(afp_req, + afp_len, responsedata); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x09: { /* SDK 35/09 AFP Set File Information */ + /* + * Request: SubFunctionCode=9, byte VolumeNumber, + * long MacBaseDirectoryID, word RequestBitMap, + * attributes, AFP dates/times, byte FinderInfo[32], + * byte PathStringLen, + * byte PathModString[PathStringLen]. + * Response: no payload. Requires selected AFP metadata + * updates and compatible filesystem attribute writes. + */ + int result = afp_set_file_information(afp_req, + afp_len, afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x0a: { /* SDK 35/10 AFP Scan File Information */ + /* + * Request: SubFunctionCode=10, sequence/search state, + * byte VolumeNumber, long MacBaseDirectoryID, + * RequestBitMap and path/filter fields. + * Response: one AFP scan result with file information + * fields. Requires stable AFP directory enumeration. + */ + int result = afp_scan_file_information(afp_req, + afp_len, responsedata, + afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x0b: { /* SDK 35/11 AFP Alloc Temporary Directory Handle */ + /* + * Request: SubFunctionCode=11, byte VolumeNumber, + * long MacBaseDirectoryID, byte PathStringLen, + * byte PathModString[PathStringLen]. + * Response: byte DirectoryHandle, byte AccessRights. + * Requires AFP path resolution to a NetWare temporary + * directory handle. + */ + int result = afp_alloc_temporary_dir_handle(afp_req, + afp_len, responsedata); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x0c: { /* SDK 35/12 AFP Get Entry ID From Path Name */ + /* + * Request: SubFunctionCode=12, NetWare-style path + * selector and volume/path fields. + * Response: byte VolumeID, long TargetEntryID. Requires + * NetWare path to AFP entry-ID conversion. + */ + int result = afp_get_entry_id_from_path_name(afp_req, + afp_len, responsedata); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x0d: { /* SDK 35/13 AFP 2.0 Create Directory */ + /* + * Request: SubFunctionCode=13 with AFP 2.0 directory + * create fields including volume/base/path and creation + * metadata. + * Response: long NewDirectoryID. Requires AFP 2.0 + * namespace directory create semantics. + */ + int result = afp_create_directory(afp_req, + afp_len, responsedata, + afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x0e: { /* SDK 35/14 AFP 2.0 Create File */ + /* + * Request: SubFunctionCode=14 with AFP 2.0 file create + * fields including volume/base/path, attributes and AFP + * metadata. + * Response: long NewDirectoryID. Requires data-fork + * create plus AFP 2.0 metadata persistence. + */ + int result = afp_create_file(afp_req, + afp_len, responsedata, + afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x0f: { /* SDK 35/15 AFP 2.0 Get File Or Directory Information */ + /* + * Request: SubFunctionCode=15, AFP 2.0 volume/base/path + * selector and RequestBitMap. + * Response: AFP 2.0 file or directory information + * selected by RequestBitMap. Requires AFP metadata plus + * filesystem stat information. + */ + int result = afp_get_file_information(afp_req, + afp_len, responsedata, + afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x10: { /* SDK 35/16 AFP 2.0 Set File Information */ + /* + * Request: SubFunctionCode=16, AFP 2.0 volume/base/path + * selector, RequestBitMap and metadata update fields. + * Response: no payload. Requires AFP 2.0 metadata and + * filesystem attribute update semantics. + */ + int result = afp_set_file_information(afp_req, + afp_len, afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x11: { /* SDK 35/17 AFP 2.0 Scan File Information */ + /* + * Request: SubFunctionCode=17, AFP 2.0 scan sequence, + * volume/base/path and filter fields. + * Response: one AFP 2.0 scan result. Requires stable + * AFP directory enumeration and metadata projection. + */ + int result = afp_scan_file_information(afp_req, + afp_len, responsedata, + afp_call_name(ufunc)); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x12: { /* SDK 35/18 AFP Get DOS Name From Entry ID */ + /* + * Request: SubFunctionCode=18, byte VolumeNumber, + * long MacDirectoryEntryID. + * Response: byte PathStringLen, + * byte DOSPathString[PathStringLen]. Requires AFP + * entry-ID to NetWare/DOS path lookup. + */ + int result = afp_get_dos_name_from_entry_id(afp_req, + afp_len, responsedata); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + case 0x13: { /* SDK 35/19 AFP Get Macintosh Info On Deleted File */ + /* + * Request: SubFunctionCode=19 and deleted-file AFP + * salvage selector fields. + * Response: FinderInfo, ProDOSInfo, resource fork size + * and deleted filename. Requires Salvage snapshot plus + * AFP metadata for deleted entries. + */ + int result = afp_get_macintosh_info_on_deleted_file(afp_req, + afp_len, responsedata); + if (result > -1) data_len = result; + else completition = (uint8)-result; + } break; + + default: + XDPRINTF((3,0, + "INFO AFP 35/%d UNKNOWN name=\"%s\" backend=\"%s\" fn=0x23 sub=0x%02x result=0xbf", + ufunc, afp_call_name(ufunc), + nwatalk_backend_available() ? "enabled" : "disabled", + ufunc)); + completition=0xbf; /* we say invalid namespace here */ + break; } } break;