Files
mars-nwe/tests/afp/afp_deleted_info_smoke.c
ai 59bfdd65b2
All checks were successful
Source release / source-package (push) Successful in 52s
afp: persist ProDOS info metadata
2026-06-01 11:51:05 +02:00

311 lines
9.9 KiB
C

/*
* Linux smoke test for AFP 0x13 Get Macintosh Info On Deleted File.
*/
#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_MAC_INFO_DELETED 0x13
#define NWE_NO_MORE_FILES 0x89ff
#define AFP_DELETED_REPLY_MAX 512
static void usage(const char *prog)
{
fprintf(stderr,
"Usage: %s [--expect-name NAME] [--expect-type FOURCC] [--expect-creator FOURCC] [--expect-prodos-hex 12HEX] [--purge-after] [--purge-all] [ncpfs options] DIRECTORY\n"
"\n"
"ncpfs options are parsed by ncp_initialize(), for example:\n"
" -S SERVER -U USER -P PASSWORD -n\n"
"\n"
"Examples:\n"
" %s -S MARS -U SUPERVISOR -P secret --expect-name TEST SYS:PUBLIC\n"
" %s --purge-all -S MARS -U SUPERVISOR -P secret SYS:PUBLIC\n",
prog, prog, prog);
}
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 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 int hex_nibble(int c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
static int parse_hex_bytes(const char *text, uint8_t *out, size_t out_len)
{
size_t i;
if (!text || strlen(text) != out_len * 2)
return -1;
for (i = 0; i < out_len; i++) {
int hi = hex_nibble((unsigned char)text[i * 2]);
int lo = hex_nibble((unsigned char)text[i * 2 + 1]);
if (hi < 0 || lo < 0)
return -1;
out[i] = (uint8_t)((hi << 4) | lo);
}
return 0;
}
static int find_deleted(NWCONN_HANDLE conn, const char *dir,
const char *expect_name,
struct ncp_deleted_file *info,
char *name, size_t name_len)
{
long err;
memset(info, 0, sizeof(*info));
info->seq = -1;
while ((err = ncp_ns_scan_salvageable_file(conn, NW_NS_DOS,
NCP_DIRSTYLE_NOHANDLE, 0, 0, (const unsigned char *)dir,
NCP_PATH_STD, info, name, (int)name_len)) == 0) {
printf("NCP salvage scan candidate seq=%d vol=%u base=%u name=%s\n",
(int)info->seq, (unsigned int)info->vol,
(unsigned int)info->base, name);
if (!expect_name || !strcmp(name, expect_name))
return 0;
}
fprintf(stderr, "No matching deleted AFP entry found: dir=%s expect=%s last_error=0x%04x\n",
dir, expect_name ? expect_name : "<any>", (unsigned int)err);
return 1;
}
static int purge_all(NWCONN_HANDLE conn, const char *dir)
{
struct ncp_deleted_file info;
char name[512];
long err;
int count = 0;
memset(&info, 0, sizeof(info));
info.seq = -1;
while ((err = ncp_ns_scan_salvageable_file(conn, NW_NS_DOS,
NCP_DIRSTYLE_NOHANDLE, 0, 0, (const unsigned char *)dir,
NCP_PATH_STD, &info, name, sizeof(name))) == 0) {
printf("NCP salvage purge candidate seq=%d vol=%u base=%u name=%s\n",
(int)info.seq, (unsigned int)info.vol,
(unsigned int)info.base, name);
err = ncp_ns_purge_file(conn, &info);
if (err) {
fprintf(stderr, "NCP purge failed: dir=%s name=%s err=0x%04x\n",
dir, name, (unsigned int)err);
return 1;
}
count++;
memset(&info, 0, sizeof(info));
info.seq = -1;
}
printf("NCP purge all done dir=%s purged=%d last_error=0x%04x\n",
dir, count, (unsigned int)err);
return 0;
}
int main(int argc, char **argv)
{
NWCONN_HANDLE conn;
NW_FRAGMENT reply;
long init_err = 0;
const char *dir = NULL;
const char *expect_name = NULL;
const char *expect_type = NULL;
const char *expect_creator = NULL;
uint8_t expect_prodos[6];
int have_expect_prodos = 0;
int purge_after = 0;
int do_purge_all = 0;
struct ncp_deleted_file info;
char name[512];
uint8_t request[5];
uint8_t reply_buf[AFP_DELETED_REPLY_MAX];
uint32_t resource_size;
int name_len;
NWCCODE err;
int i;
memset(expect_prodos, 0, sizeof(expect_prodos));
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], "--expect-name")) {
if (++i >= argc) { usage(argv[0]); ncp_close(conn); return 2; }
expect_name = argv[i];
} else if (!strcmp(argv[i], "--expect-type")) {
if (++i >= argc || strlen(argv[i]) != 4) {
fprintf(stderr, "--expect-type must be four characters\n");
ncp_close(conn); return 2;
}
expect_type = argv[i];
} else if (!strcmp(argv[i], "--expect-creator")) {
if (++i >= argc || strlen(argv[i]) != 4) {
fprintf(stderr, "--expect-creator must be four characters\n");
ncp_close(conn); return 2;
}
expect_creator = argv[i];
} else if (!strcmp(argv[i], "--expect-prodos-hex")) {
if (++i >= argc || parse_hex_bytes(argv[i], expect_prodos, sizeof(expect_prodos))) {
fprintf(stderr, "--expect-prodos-hex must be 12 hex characters\n");
ncp_close(conn); return 2;
}
have_expect_prodos = 1;
} else if (!strcmp(argv[i], "--purge-after")) {
purge_after = 1;
} else if (!strcmp(argv[i], "--purge-all")) {
do_purge_all = 1;
} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
usage(argv[0]);
ncp_close(conn);
return 0;
} else if (!dir) {
dir = argv[i];
} else {
fprintf(stderr, "unexpected argument: %s\n", argv[i]);
usage(argv[0]);
ncp_close(conn);
return 2;
}
}
if (!dir) {
usage(argv[0]);
ncp_close(conn);
return 2;
}
if (do_purge_all) {
int rc = purge_all(conn, dir);
ncp_close(conn);
return rc;
}
if (find_deleted(conn, dir, expect_name, &info, name, sizeof(name))) {
ncp_close(conn);
return 1;
}
request[0] = (uint8_t)info.vol;
cpu_to_be32(info.base, 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_MAC_INFO_DELETED),
request, sizeof(request), &reply);
if (err) {
fprintf(stderr,
"AFP Get Macintosh Info On Deleted File failed: completion=0x%02x (%u) vol=%u dosdir=0x%08x\n",
(unsigned int)err & 0xff, (unsigned int)err,
(unsigned int)info.vol, (unsigned int)info.base);
ncp_close(conn);
return 1;
}
if (reply.fragSize < 43) {
fprintf(stderr, "short AFP deleted-info reply: %zu bytes\n", reply.fragSize);
ncp_close(conn);
return 1;
}
resource_size = be32_to_cpu(reply_buf + 38);
name_len = reply_buf[42];
if (name_len < 0 || 43 + name_len > (int)reply.fragSize) {
fprintf(stderr, "bad AFP deleted-info name length: %d reply=%zu\n",
name_len, reply.fragSize);
ncp_close(conn);
return 1;
}
printf("AFP deleted info seq=%d vol=%u dosdir=%u resource=%u name=%.*s\n",
(int)info.seq, (unsigned int)info.vol, (unsigned int)info.base,
(unsigned int)resource_size, name_len, reply_buf + 43);
printf("AFP deleted finder type=%.4s creator=%.4s\n", reply_buf, reply_buf + 4);
printf("AFP deleted prodos=%02x%02x%02x%02x%02x%02x\n",
reply_buf[32], reply_buf[33], reply_buf[34],
reply_buf[35], reply_buf[36], reply_buf[37]);
if (expect_name && (name_len != (int)strlen(expect_name) ||
memcmp(reply_buf + 43, expect_name, name_len))) {
fprintf(stderr, "AFP deleted info name mismatch: got=%.*s expected=%s\n",
name_len, reply_buf + 43, expect_name);
ncp_close(conn);
return 1;
}
if (expect_type && memcmp(reply_buf, expect_type, 4)) {
fprintf(stderr, "AFP deleted info Finder type mismatch: got=%.4s expected=%s\n",
reply_buf, expect_type);
ncp_close(conn);
return 1;
}
if (expect_creator && memcmp(reply_buf + 4, expect_creator, 4)) {
fprintf(stderr, "AFP deleted info Finder creator mismatch: got=%.4s expected=%s\n",
reply_buf + 4, expect_creator);
ncp_close(conn);
return 1;
}
if (have_expect_prodos && memcmp(reply_buf + 32, expect_prodos, sizeof(expect_prodos))) {
fprintf(stderr, "AFP deleted info ProDOSInfo mismatch: got=%02x%02x%02x%02x%02x%02x\n",
reply_buf[32], reply_buf[33], reply_buf[34],
reply_buf[35], reply_buf[36], reply_buf[37]);
ncp_close(conn);
return 1;
}
if (purge_after) {
err = ncp_ns_purge_file(conn, &info);
if (err) {
fprintf(stderr, "NCP purge after AFP deleted-info failed: err=0x%04x\n",
(unsigned int)err);
ncp_close(conn);
return 1;
}
printf("NCP purge after AFP deleted-info ok seq=%d name=%s\n",
(int)info.seq, name);
}
ncp_close(conn);
return 0;
}