311 lines
9.9 KiB
C
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;
|
|
}
|