From 02e247b133a227a206eff8052cda4134a90cece3 Mon Sep 17 00:00:00 2001 From: a Date: Sat, 30 May 2026 20:09:23 +0000 Subject: [PATCH] nwconn: implement AFP rename through NetWare move paths --- src/nwconn.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/nwconn.c b/src/nwconn.c index 4597eb2..610ab92 100644 --- a/src/nwconn.c +++ b/src/nwconn.c @@ -1225,6 +1225,114 @@ static int afp_delete_object(uint8 *afp_req, int afp_len, } + +static int afp_rename_object(uint8 *afp_req, int afp_len, + const char *call_name) +/* + * WebSDK / nwafp.h call 0x07 renames or moves an AFP file or directory. + * Keep the operation on existing mars_nwe paths: resolve source and + * destination volume/base-id/path tuples to normal NetWare path strings, then + * delegate to nw_mv_files() for files or nw_mv_dir_between_handles() for + * directories. Do not introduce a direct POSIX rename(2) AFP side path. + */ +{ + uint8 volume_number; + uint32 source_entry_id; + uint32 dest_entry_id; + int source_len; + int dest_len_off; + int dest_len; + uint8 source_path[512]; + uint8 dest_path[512]; + char source_unixname[PATH_MAX]; + int volume; + int result; + struct stat stbuff; + + if (afp_len < 12) { + XDPRINTF((2,0, "%s rejected: short request len=%d", + call_name, afp_len)); + return(-0x7e); + } + + volume_number = afp_req[1]; + source_entry_id = GET_BE32(afp_req + 2); + dest_entry_id = GET_BE32(afp_req + 6); + source_len = (int)afp_req[10]; + if (source_len < 0 || afp_len < 11 + source_len + 1) { + XDPRINTF((2,0, "%s rejected: source boundary check len=%d source_len=%d", + call_name, afp_len, source_len)); + return(-0x7e); + } + + dest_len_off = 11 + source_len; + dest_len = (int)afp_req[dest_len_off]; + if (dest_len < 0 || afp_len < dest_len_off + 1 + dest_len) { + XDPRINTF((2,0, "%s rejected: dest boundary check len=%d dest_len=%d", + call_name, afp_len, dest_len)); + return(-0x7e); + } + if (!source_len || !dest_len) { + XDPRINTF((2,0, "%s rejected: empty source/dest source_len=%d dest_len=%d", + call_name, source_len, dest_len)); + return(-0x9c); + } + + result = afp_build_base_relative_path((int)volume_number, source_entry_id, + afp_req + 11, source_len, + source_path, sizeof(source_path)); + if (result < 0) { + XDPRINTF((2,0, "%s source path build failed: vol=%d base=0x%08x path='%s' result=-0x%x", + call_name, (int)volume_number, source_entry_id, + visable_data(afp_req + 11, source_len), -result)); + return(result); + } + + result = afp_build_base_relative_path((int)volume_number, dest_entry_id, + afp_req + dest_len_off + 1, dest_len, + dest_path, sizeof(dest_path)); + if (result < 0) { + XDPRINTF((2,0, "%s dest path build failed: vol=%d base=0x%08x path='%s' result=-0x%x", + call_name, (int)volume_number, dest_entry_id, + visable_data(afp_req + dest_len_off + 1, dest_len), -result)); + return(result); + } + + volume = conn_get_kpl_unxname(source_unixname, sizeof(source_unixname), 0, + source_path, strlen((char *)source_path)); + if (volume < 0) return(volume); + if (stat(source_unixname, &stbuff)) { + XDPRINTF((2,0, "%s source stat failed: vol=%d source_base=0x%08x path='%s' mars_path='%s' unix='%s' errno=%d", + call_name, (int)volume_number, source_entry_id, + visable_data(afp_req + 11, source_len), source_path, + source_unixname, errno)); + return(-0x9c); + } + + if (S_ISDIR(stbuff.st_mode)) { + result = nw_mv_dir_between_handles(0, source_path, strlen((char *)source_path), + 0, dest_path, strlen((char *)dest_path)); + } else { + result = nw_mv_files(0x37, + 0, source_path, strlen((char *)source_path), + 0, dest_path, strlen((char *)dest_path)); + } + if (result < 0) { + XDPRINTF((2,0, "%s rename failed: vol=%d source_base=0x%08x dest_base=0x%08x source='%s' dest='%s' result=-0x%x", + call_name, (int)volume_number, source_entry_id, dest_entry_id, + source_path, dest_path, -result)); + return(result); + } + + XDPRINTF((3,0, "%s: request_vol=%d resolved_vol=%d source_base=0x%08x dest_base=0x%08x source='%s' dest='%s' directory=%d", + call_name, (int)volume_number, volume, source_entry_id, dest_entry_id, + visable_data(afp_req + 11, source_len), + visable_data(afp_req + dest_len_off + 1, dest_len), + S_ISDIR(stbuff.st_mode) ? 1 : 0)); + return(0); +} + + static int afp_get_entry_id_from_path_name(uint8 *afp_req, int afp_len, uint8 *response) { @@ -4276,6 +4384,11 @@ static int handle_ncp_serv(void) afp_len, afp_call_name(ufunc)); if (result > -1) data_len = result; else completition = (uint8)-result; + } else if (ufunc == 0x07) { + 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) { int result = afp_get_entry_id_from_name(afp_req, afp_len, responsedata);