nwconn: implement AFP DOS name reverse lookup
All checks were successful
Source release / source-package (push) Successful in 50s
All checks were successful
Source release / source-package (push) Successful in 50s
Implement the WebSDK/NWAFP Get DOS Name From Entry ID subfunction (NCP 0x2222/35/18) as a conservative, read-only reverse lookup over mars_nwe's existing volume and AFP metadata infrastructure. The documented request carries a volume number and 32-bit Macintosh directory entry ID, and the reply returns a length-prefixed DOS path string. mars_nwe's current AFP entry IDs are not the namespace base handles maintained by namspace.c; they are mars_nwe/libatalk AFP metadata IDs cached through nwatalk. Reuse the existing volume table as the search root and nwatalk_get_entry_id() as the identity probe instead of inventing a parallel namespace handle mapping. The reverse lookup deliberately does not create fallback IDs while walking the volume. It only matches entries that already have mars_nwe or Netatalk AFP metadata, which is the normal smoke-test sequence after Get Entry ID, Get File Information, or Scan File Information has cached the target ID. This keeps the lookup read-only and avoids populating entry-id xattrs across an entire volume as a side effect. Add a Linux afp_dos_name_smoke helper and wire it into the AFP smoke suite. The helper can resolve the supplied VOL:PATH to an entry ID first, then sends the 0x12 request and verifies the returned path without the volume prefix. The suite continues to exercise the existing path-backed AFP compatibility flow before future create/rename/remove work. Tests:\n- git diff --check\n- bash -n tests/linux/afp_smoke_suite.sh\n- gcc -Iinclude -I/mnt/data/stubs -fsyntax-only tests/linux/afp_dos_name_smoke.c\n\nTODO:\n- Replace the volume walk with a real CNID/base-ID index when persistent AFP identity storage grows one.\n- Return true DOS 8.3 aliases once the AFP reverse lookup is wired to the namespace alias helpers rather than preserving the cached path component spelling.
This commit is contained in:
6
TODO.md
6
TODO.md
@@ -328,6 +328,12 @@ Current status:
|
||||
Netatalk's `org.netatalk.*` EA abstraction.
|
||||
- NetWare AFP calls are NCP entry points for Mac namespace semantics on a
|
||||
NetWare volume, not transport-level AFP proxy calls to `afpd`.
|
||||
- AFP Get DOS Name From Entry ID (0x12) is implemented as a conservative
|
||||
read-only reverse lookup over the existing mars_nwe volume table and the
|
||||
`nwatalk_get_entry_id()` metadata probe. It returns the DOS/NetWare path
|
||||
relative to the requested volume for entries that already have a cached
|
||||
mars_nwe/Netatalk AFP ID, and deliberately does not create fallback IDs while
|
||||
scanning a volume.
|
||||
|
||||
Follow-up:
|
||||
|
||||
|
||||
159
src/nwconn.c
159
src/nwconn.c
@@ -433,6 +433,7 @@ static const char *afp_call_name(int ufunc)
|
||||
case 0x0f: return("AFP 2.0 Get File Information");
|
||||
case 0x10: return("AFP 2.0 Set File Information");
|
||||
case 0x11: return("AFP 2.0 Scan File Information");
|
||||
case 0x12: return("AFP Get DOS Name From Entry ID");
|
||||
default: return("unknown AFP call");
|
||||
}
|
||||
}
|
||||
@@ -880,6 +881,156 @@ static int afp_get_entry_id_from_path_name(uint8 *afp_req, int afp_len,
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32 target_entry_id;
|
||||
char path[256];
|
||||
} AFP_DOS_NAME_SEARCH;
|
||||
|
||||
static int afp_path_join(char *dst, int dst_len, const char *base,
|
||||
const char *name)
|
||||
{
|
||||
int used;
|
||||
|
||||
if (!dst || dst_len < 1 || !base || !name) return(-1);
|
||||
if (!*base)
|
||||
used = slprintf(dst, dst_len, "%s", name);
|
||||
else
|
||||
used = slprintf(dst, dst_len, "%s/%s", base, name);
|
||||
if (used < 0 || used >= dst_len) return(-1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int afp_find_dos_name_from_entry_id_rec(int volume,
|
||||
const char *unix_dir,
|
||||
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.
|
||||
*
|
||||
* 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/libatalk AFP 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
|
||||
* Information/Scan.
|
||||
*/
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
char unix_child[PATH_MAX];
|
||||
char rel_child[256];
|
||||
|
||||
(void)volume;
|
||||
dir = opendir(unix_dir);
|
||||
if (!dir) return(-0x89); /* No Search Privilege */
|
||||
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
struct stat stb;
|
||||
uint32 entry_id = 0;
|
||||
|
||||
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
|
||||
continue;
|
||||
if (afp_path_join(unix_child, sizeof(unix_child), unix_dir, de->d_name))
|
||||
continue;
|
||||
if (afp_path_join(rel_child, sizeof(rel_child), rel_dir, de->d_name))
|
||||
continue;
|
||||
if (lstat(unix_child, &stb))
|
||||
continue;
|
||||
|
||||
if (!nwatalk_get_entry_id(unix_child, &entry_id)
|
||||
&& entry_id == search->target_entry_id) {
|
||||
strmaxcpy((uint8 *)search->path, rel_child,
|
||||
sizeof(search->path)-1);
|
||||
closedir(dir);
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (S_ISDIR(stb.st_mode)) {
|
||||
int result = afp_find_dos_name_from_entry_id_rec(volume, unix_child,
|
||||
rel_child, search);
|
||||
if (!result || result != -0xff) {
|
||||
closedir(dir);
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return(-0xff); /* Failure, no files found */
|
||||
}
|
||||
|
||||
static int afp_find_dos_name_from_entry_id(int volume, uint32 entry_id,
|
||||
char *path, int path_len)
|
||||
{
|
||||
AFP_DOS_NAME_SEARCH search;
|
||||
char root[PATH_MAX];
|
||||
int len;
|
||||
|
||||
if (volume < 0 || volume >= used_nw_volumes || !nw_volumes[volume].unixname)
|
||||
return(-0x98); /* Volume does not exist */
|
||||
if (entry_id == 1) {
|
||||
if (path && path_len > 0) *path = '\0';
|
||||
return(0);
|
||||
}
|
||||
|
||||
len = nw_volumes[volume].unixnamlen;
|
||||
if (len < 1 || len >= (int)sizeof(root)) return(-0x98);
|
||||
memcpy(root, nw_volumes[volume].unixname, len);
|
||||
root[len] = '\0';
|
||||
while (len > 1 && root[len-1] == '/')
|
||||
root[--len] = '\0';
|
||||
|
||||
memset(&search, 0, sizeof(search));
|
||||
search.target_entry_id = entry_id;
|
||||
if (!afp_find_dos_name_from_entry_id_rec(volume, root, "", &search)) {
|
||||
strmaxcpy((uint8 *)path, search.path, path_len-1);
|
||||
return(0);
|
||||
}
|
||||
return(-0xff);
|
||||
}
|
||||
|
||||
static int afp_get_dos_name_from_entry_id(uint8 *afp_req, int afp_len,
|
||||
uint8 *response)
|
||||
{
|
||||
uint8 volume_number;
|
||||
uint32 entry_id;
|
||||
char path[256];
|
||||
int result;
|
||||
int len;
|
||||
|
||||
if (afp_len < 6) {
|
||||
XDPRINTF((2,0, "AFP Get DOS Name From Entry ID rejected: short request len=%d",
|
||||
afp_len));
|
||||
return(-0x7e); /* NCP Boundary Check Failed */
|
||||
}
|
||||
|
||||
volume_number = afp_req[1];
|
||||
entry_id = GET_BE32(afp_req + 2);
|
||||
|
||||
if (!nwatalk_backend_available()) {
|
||||
XDPRINTF((3,0, "AFP Get DOS Name From Entry ID rejected: libatalk backend unavailable"));
|
||||
return(-0xbf); /* Invalid Namespace */
|
||||
}
|
||||
|
||||
result = afp_find_dos_name_from_entry_id((int)volume_number, entry_id,
|
||||
path, sizeof(path));
|
||||
if (result) {
|
||||
XDPRINTF((2,0, "AFP Get DOS Name From Entry ID lookup failed: vol=%d entry=0x%08x result=-0x%x",
|
||||
(int)volume_number, entry_id, -result));
|
||||
return(result);
|
||||
}
|
||||
|
||||
len = strlen(path);
|
||||
if (len > 255) return(-0x96); /* Server out of memory / path too long */
|
||||
response[0] = (uint8)len;
|
||||
memcpy(response + 1, path, len);
|
||||
|
||||
XDPRINTF((3,0, "AFP Get DOS Name From Entry ID: vol=%d entry=0x%08x path='%s'",
|
||||
(int)volume_number, entry_id, path));
|
||||
return(1 + len);
|
||||
}
|
||||
|
||||
|
||||
static void afp_copy_fixed_name(uint8 *dst, int dst_len,
|
||||
const uint8 *src, int src_len)
|
||||
{
|
||||
@@ -3402,7 +3553,8 @@ static int handle_ncp_serv(void)
|
||||
* Information (0x05), Get Entry ID From
|
||||
* NetWare Handle (0x06), Rename (0x07), Open File Fork
|
||||
* (0x08), Alloc Temporary Dir Handle (0x0b), Get Entry ID
|
||||
* From Path Name (0x0c), the AFP Set File Information
|
||||
* From Path Name (0x0c), Get DOS Name From Entry ID
|
||||
* (0x12), the AFP Set File Information
|
||||
* write call (0x09), the AFP 2.0 create calls
|
||||
* (0x0d/0x0e), Get/Set File Information (0x0f/0x10), and
|
||||
* Scan File Information (0x11).
|
||||
@@ -3461,6 +3613,11 @@ static int handle_ncp_serv(void)
|
||||
afp_len, responsedata);
|
||||
if (result > -1) data_len = result;
|
||||
else completition = (uint8)-result;
|
||||
} else if (ufunc == 0x12) {
|
||||
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 == 0x05 || ufunc == 0x0f) {
|
||||
int result = afp_get_file_information(afp_req,
|
||||
afp_len, responsedata,
|
||||
|
||||
@@ -49,6 +49,10 @@ add_executable(afp_file_info_smoke afp_file_info_smoke.c)
|
||||
target_include_directories(afp_file_info_smoke PRIVATE ${NCPFS_INCLUDE_DIR})
|
||||
target_link_libraries(afp_file_info_smoke ${NCPFS_LIBRARY})
|
||||
|
||||
add_executable(afp_dos_name_smoke afp_dos_name_smoke.c)
|
||||
target_include_directories(afp_dos_name_smoke PRIVATE ${NCPFS_INCLUDE_DIR})
|
||||
target_link_libraries(afp_dos_name_smoke ${NCPFS_LIBRARY})
|
||||
|
||||
add_executable(afp_scan_info_smoke afp_scan_info_smoke.c)
|
||||
target_include_directories(afp_scan_info_smoke PRIVATE ${NCPFS_INCLUDE_DIR})
|
||||
target_link_libraries(afp_scan_info_smoke ${NCPFS_LIBRARY})
|
||||
|
||||
@@ -678,3 +678,39 @@ Entry-ID-only write semantics out of this conservative smoke path.
|
||||
If the server was built without the optional Netatalk/libatalk backend, use
|
||||
`--allow-invalid-namespace` for the expected negative test. Use
|
||||
`--allow-invalid-path` for path-resolution negative tests.
|
||||
|
||||
## AFP Get DOS Name From Entry ID smoke test
|
||||
|
||||
`afp_dos_name_smoke` exercises the WebSDK-documented NetWare AFP reverse
|
||||
lookup:
|
||||
|
||||
```text
|
||||
NCP 0x2222/35/18 AFP Get DOS Name From Entry ID
|
||||
```
|
||||
|
||||
The request carries the AFP volume number and a 32-bit Macintosh directory
|
||||
entry ID. The reply is a one-byte DOS path length followed by the DOS path
|
||||
string for the matching entry. The smoke helper first resolves the supplied
|
||||
`VOL:PATH` through AFP Get Entry ID From Path Name when `--entry-id` is not
|
||||
provided, then calls AFP Get DOS Name From Entry ID and verifies that the
|
||||
returned path matches the original path without the volume prefix.
|
||||
|
||||
Example:
|
||||
|
||||
```sh
|
||||
./afp_dos_name_smoke -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini
|
||||
```
|
||||
|
||||
Expected output shape:
|
||||
|
||||
```text
|
||||
AFP Get DOS Name From Entry ID volume=0 entry_id=0x399193ed path=PUBLIC/pmdflts.ini verified
|
||||
```
|
||||
|
||||
The server implementation deliberately reuses the existing mars_nwe volume table
|
||||
and the `nwatalk_get_entry_id()` metadata probe. It does not create fallback
|
||||
entry IDs while walking the volume; the target entry must already have a cached
|
||||
mars_nwe/Netatalk AFP ID, which is the normal state after the Entry ID, Get File
|
||||
Information, or Scan File Information smoke probes. This keeps the reverse
|
||||
lookup read-only and avoids populating entry-id xattrs across a whole volume as a
|
||||
side effect of one DOS-name lookup.
|
||||
|
||||
262
tests/linux/afp_dos_name_smoke.c
Normal file
262
tests/linux/afp_dos_name_smoke.c
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Linux smoke test for NetWare AFP Get DOS Name From Entry ID.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ncp/nwcalls.h>
|
||||
#include <ncp/ncplib.h>
|
||||
#include <ncp/kernel/ncp.h>
|
||||
|
||||
#ifndef NCPC_SUBFUNCTION
|
||||
#define NCPC_SUBFUNCTION 0x10000
|
||||
#endif
|
||||
#ifndef NCPC_SFN
|
||||
#define NCPC_SFN(FN, SFN) ((FN) | ((SFN) << 8) | NCPC_SUBFUNCTION)
|
||||
#endif
|
||||
|
||||
#define AFP_GET_ENTRY_ID_FROM_PATH_NAME 0x0c
|
||||
#define AFP_GET_DOS_NAME_FROM_ENTRY_ID 0x12
|
||||
#define NWE_INVALID_NAMESPACE 0xbf
|
||||
#define NWE_INVALID_PATH 0x9c
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [--volume N] [--entry-id ID] [--expect PATH] "
|
||||
"[--allow-invalid-namespace] [--allow-invalid-path] [ncpfs options] PATH\n"
|
||||
"\n"
|
||||
"When --entry-id is omitted, the helper first resolves PATH with AFP "
|
||||
"Get Entry ID From Path Name, then calls AFP Get DOS Name From Entry ID.\n"
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
" %s -S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n"
|
||||
" %s --entry-id 0x12345678 --expect PUBLIC/pmdflts.ini "
|
||||
"-S MARS -U SUPERVISOR -P secret SYS:PUBLIC/pmdflts.ini\n",
|
||||
prog, prog, prog);
|
||||
}
|
||||
|
||||
static int parse_u32(const char *text, uint32_t *value)
|
||||
{
|
||||
char *end = NULL;
|
||||
unsigned long v;
|
||||
|
||||
errno = 0;
|
||||
v = strtoul(text, &end, 0);
|
||||
if (errno || !end || *end || v > 0xffffffffUL)
|
||||
return -1;
|
||||
*value = (uint32_t)v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t be32_to_cpu(const uint8_t p[4])
|
||||
{
|
||||
return ((uint32_t)p[0] << 24) |
|
||||
((uint32_t)p[1] << 16) |
|
||||
((uint32_t)p[2] << 8) |
|
||||
p[3];
|
||||
}
|
||||
|
||||
static void cpu_to_be32(uint32_t v, uint8_t p[4])
|
||||
{
|
||||
p[0] = (uint8_t)(v >> 24);
|
||||
p[1] = (uint8_t)(v >> 16);
|
||||
p[2] = (uint8_t)(v >> 8);
|
||||
p[3] = (uint8_t)v;
|
||||
}
|
||||
|
||||
static const char *path_without_volume(const char *path)
|
||||
{
|
||||
const char *colon = strchr(path, ':');
|
||||
if (colon && colon[1])
|
||||
return colon + 1;
|
||||
return path;
|
||||
}
|
||||
|
||||
static NWCCODE get_entry_id_for_path(NWCONN_HANDLE conn, const char *path,
|
||||
uint32_t *entry_id)
|
||||
{
|
||||
NW_FRAGMENT reply;
|
||||
uint8_t request[2 + 255];
|
||||
uint8_t reply_buf[4];
|
||||
size_t path_len = strlen(path);
|
||||
NWCCODE err;
|
||||
|
||||
if (path_len > 255)
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
request[0] = 0; /* NetWare directory handle 0, raw VOL:PATH */
|
||||
request[1] = (uint8_t)path_len;
|
||||
memcpy(request + 2, path, path_len);
|
||||
|
||||
memset(reply_buf, 0, sizeof(reply_buf));
|
||||
reply.fragAddr.rw = reply_buf;
|
||||
reply.fragSize = sizeof(reply_buf);
|
||||
|
||||
err = NWRequestSimple(conn,
|
||||
NCPC_SFN(0x23, AFP_GET_ENTRY_ID_FROM_PATH_NAME),
|
||||
request,
|
||||
2 + path_len,
|
||||
&reply);
|
||||
if (err)
|
||||
return err;
|
||||
if (reply.fragSize < 4)
|
||||
return NWE_INVALID_PATH;
|
||||
|
||||
*entry_id = be32_to_cpu(reply_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NWCONN_HANDLE conn;
|
||||
NW_FRAGMENT reply;
|
||||
long init_err = 0;
|
||||
const char *path = NULL;
|
||||
const char *expect = NULL;
|
||||
int allow_invalid_namespace = 0;
|
||||
int allow_invalid_path = 0;
|
||||
uint32_t volume_number = 0;
|
||||
uint32_t entry_id = 0;
|
||||
int have_entry_id = 0;
|
||||
int i;
|
||||
uint8_t request[1 + 4];
|
||||
uint8_t reply_buf[256];
|
||||
char dos_path[256];
|
||||
NWCCODE err;
|
||||
|
||||
if (NWCallsInit(NULL, NULL)) {
|
||||
fprintf(stderr, "NWCallsInit failed\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
conn = ncp_initialize(&argc, argv, 1, &init_err);
|
||||
if (!conn) {
|
||||
fprintf(stderr, "ncp_initialize/login failed: %ld\n", init_err);
|
||||
usage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--allow-invalid-namespace")) {
|
||||
allow_invalid_namespace = 1;
|
||||
} else if (!strcmp(argv[i], "--allow-invalid-path")) {
|
||||
allow_invalid_path = 1;
|
||||
} else if (!strcmp(argv[i], "--volume")) {
|
||||
if (++i >= argc || parse_u32(argv[i], &volume_number) || volume_number > 255) {
|
||||
fprintf(stderr, "invalid --volume value\n");
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
} else if (!strcmp(argv[i], "--entry-id")) {
|
||||
if (++i >= argc || parse_u32(argv[i], &entry_id)) {
|
||||
fprintf(stderr, "invalid --entry-id value\n");
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
have_entry_id = 1;
|
||||
} else if (!strcmp(argv[i], "--expect")) {
|
||||
if (++i >= argc) {
|
||||
fprintf(stderr, "missing --expect value\n");
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
expect = argv[i];
|
||||
} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||
usage(argv[0]);
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
} else if (!path) {
|
||||
path = argv[i];
|
||||
} else {
|
||||
fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path && !have_entry_id) {
|
||||
fprintf(stderr, "missing PATH or --entry-id\n");
|
||||
usage(argv[0]);
|
||||
ncp_close(conn);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!have_entry_id) {
|
||||
err = get_entry_id_for_path(conn, path, &entry_id);
|
||||
if (err) {
|
||||
fprintf(stderr,
|
||||
"AFP Get Entry ID before DOS-name lookup failed: completion=0x%02x (%u) path=%s\n",
|
||||
(unsigned int)err & 0xff, (unsigned int)err, path);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
request[0] = (uint8_t)volume_number;
|
||||
cpu_to_be32(entry_id, request + 1);
|
||||
|
||||
memset(reply_buf, 0, sizeof(reply_buf));
|
||||
reply.fragAddr.rw = reply_buf;
|
||||
reply.fragSize = sizeof(reply_buf);
|
||||
|
||||
err = NWRequestSimple(conn,
|
||||
NCPC_SFN(0x23, AFP_GET_DOS_NAME_FROM_ENTRY_ID),
|
||||
request,
|
||||
sizeof(request),
|
||||
&reply);
|
||||
|
||||
if (((unsigned int)err & 0xff) == NWE_INVALID_NAMESPACE && allow_invalid_namespace) {
|
||||
printf("AFP Get DOS Name From Entry ID returned invalid namespace as expected: volume=%u entry_id=0x%08x\n",
|
||||
(unsigned int)volume_number, (unsigned int)entry_id);
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((unsigned int)err & 0xff) == NWE_INVALID_PATH && allow_invalid_path) {
|
||||
printf("AFP Get DOS Name From Entry ID returned invalid path as expected: volume=%u entry_id=0x%08x\n",
|
||||
(unsigned int)volume_number, (unsigned int)entry_id);
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr,
|
||||
"AFP Get DOS Name From Entry ID failed: completion=0x%02x (%u) volume=%u entry_id=0x%08x\n",
|
||||
(unsigned int)err & 0xff, (unsigned int)err,
|
||||
(unsigned int)volume_number, (unsigned int)entry_id);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (reply.fragSize < 1 || reply_buf[0] + 1U > reply.fragSize) {
|
||||
fprintf(stderr, "short AFP DOS-name reply: %zu bytes\n", reply.fragSize);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(dos_path, reply_buf + 1, reply_buf[0]);
|
||||
dos_path[reply_buf[0]] = '\0';
|
||||
|
||||
if (!expect && path)
|
||||
expect = path_without_volume(path);
|
||||
if (expect && strcmp(expect, dos_path)) {
|
||||
fprintf(stderr,
|
||||
"AFP Get DOS Name From Entry ID verify mismatch: volume=%u entry_id=0x%08x got=%s expected=%s\n",
|
||||
(unsigned int)volume_number, (unsigned int)entry_id,
|
||||
dos_path, expect);
|
||||
ncp_close(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("AFP Get DOS Name From Entry ID volume=%u entry_id=0x%08x path=%s verified\n",
|
||||
(unsigned int)volume_number, (unsigned int)entry_id, dos_path);
|
||||
|
||||
ncp_close(conn);
|
||||
return 0;
|
||||
}
|
||||
@@ -179,6 +179,7 @@ emit "mtime_epoch=$TIMESTAMP_EPOCH"
|
||||
for helper in \
|
||||
afp_entry_id_smoke \
|
||||
afp_file_info_smoke \
|
||||
afp_dos_name_smoke \
|
||||
afp_scan_info_smoke \
|
||||
afp_temp_dir_handle_smoke \
|
||||
afp_open_file_fork_smoke \
|
||||
@@ -219,6 +220,11 @@ run_cmd \
|
||||
"./afp_file_info_smoke $COMMON_PRINT '$NETWARE_PATH'" \
|
||||
"$SCRIPT_DIR/afp_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH"
|
||||
|
||||
run_cmd \
|
||||
"AFP Get DOS Name From Entry ID" \
|
||||
"./afp_dos_name_smoke $COMMON_PRINT '$NETWARE_PATH'" \
|
||||
"$SCRIPT_DIR/afp_dos_name_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH"
|
||||
|
||||
run_cmd \
|
||||
"AFP 2.0 Scan File Information" \
|
||||
"./afp_scan_info_smoke $COMMON_PRINT '$DIR_PATH'" \
|
||||
|
||||
Reference in New Issue
Block a user