test code v1

This commit is contained in:
2026-08-13 10:44:59 +02:00
commit f3b1165f19
301 changed files with 37885 additions and 0 deletions
+283
View File
@@ -0,0 +1,283 @@
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct erofs_test_fid {
uint16_t len;
uint16_t pad;
uint32_t nid_hi;
uint32_t nid_lo;
uint32_t gen;
};
_Static_assert(sizeof(struct erofs_test_fid) == 16,
"unexpected EROFS test file handle size");
_Static_assert(sizeof(struct erofs_test_fid) <= sizeof(struct fid),
"EROFS test file handle does not fit struct fid");
static void
usage(void)
{
fprintf(stderr,
"usage:\n"
" nfs_fh_tool capture path handle\n"
" nfs_fh_tool lcapture path handle\n"
" nfs_fh_tool describe handle\n"
" nfs_fh_tool compare handle1 handle2\n"
" nfs_fh_tool stat handle\n"
" nfs_fh_tool cat handle output\n"
" nfs_fh_tool mutate input output field value\n"
" nfs_fh_tool expect-stat handle errno\n"
" nfs_fh_tool expect-open handle errno\n");
exit(2);
}
static void
read_handle(const char *path, fhandle_t *fh)
{
FILE *fp;
fp = fopen(path, "rb");
if (fp == NULL)
err(1, "fopen %s", path);
if (fread(fh, sizeof(*fh), 1, fp) != 1)
err(1, "fread %s", path);
if (fgetc(fp) != EOF)
errx(1, "%s has trailing data", path);
if (fclose(fp) != 0)
err(1, "fclose %s", path);
}
static void
write_handle(const char *path, const fhandle_t *fh)
{
FILE *fp;
fp = fopen(path, "wb");
if (fp == NULL)
err(1, "fopen %s", path);
if (fwrite(fh, sizeof(*fh), 1, fp) != 1)
err(1, "fwrite %s", path);
if (fclose(fp) != 0)
err(1, "fclose %s", path);
}
static int
parse_errno(const char *name)
{
char *end;
long value;
if (strcmp(name, "EINVAL") == 0)
return (EINVAL);
if (strcmp(name, "ESTALE") == 0)
return (ESTALE);
errno = 0;
value = strtol(name, &end, 0);
if (errno != 0 || *end != '\0' || value <= 0 || value > INT_MAX)
errx(2, "invalid errno: %s", name);
return ((int)value);
}
static uint64_t
parse_value(const char *text)
{
char *end;
uintmax_t value;
errno = 0;
value = strtoumax(text, &end, 0);
if (errno != 0 || *end != '\0' || value > UINT64_MAX)
errx(2, "invalid value: %s", text);
return ((uint64_t)value);
}
static void
capture(const char *path, const char *output, int nofollow)
{
fhandle_t fh;
int error;
bzero(&fh, sizeof(fh));
error = nofollow ? lgetfh(path, &fh) : getfh(path, &fh);
if (error != 0)
err(1, "%s %s", nofollow ? "lgetfh" : "getfh", path);
write_handle(output, &fh);
}
static void
describe(const char *path)
{
struct erofs_test_fid efid;
fhandle_t fh;
uint64_t nid;
read_handle(path, &fh);
bzero(&efid, sizeof(efid));
memcpy(&efid, &fh.fh_fid, sizeof(efid));
nid = ((uint64_t)efid.nid_hi << 32) | efid.nid_lo;
printf("fsid=%08x:%08x len=%u pad=%u nid=%016" PRIx64
" gen=%u\n", (unsigned int)fh.fh_fsid.val[0],
(unsigned int)fh.fh_fsid.val[1], efid.len, efid.pad, nid, efid.gen);
}
static void
compare(const char *left, const char *right)
{
fhandle_t a, b;
read_handle(left, &a);
read_handle(right, &b);
if (memcmp(&a, &b, sizeof(a)) != 0)
errx(1, "file handles differ");
}
static void
stat_handle(const char *path)
{
fhandle_t fh;
struct stat sb;
read_handle(path, &fh);
if (fhstat(&fh, &sb) != 0)
err(1, "fhstat %s", path);
printf("mode=%#o ino=%ju gen=%u size=%jd\n", (unsigned int)sb.st_mode,
(uintmax_t)sb.st_ino, (unsigned int)sb.st_gen,
(intmax_t)sb.st_size);
}
static void
cat_handle(const char *handle, const char *output)
{
char buf[65536];
fhandle_t fh;
ssize_t nr, nw;
int fd, outfd;
read_handle(handle, &fh);
fd = fhopen(&fh, O_RDONLY);
if (fd < 0)
err(1, "fhopen %s", handle);
outfd = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (outfd < 0)
err(1, "open %s", output);
while ((nr = read(fd, buf, sizeof(buf))) > 0) {
for (ssize_t done = 0; done < nr; done += nw) {
nw = write(outfd, buf + done, nr - done);
if (nw < 0)
err(1, "write %s", output);
}
}
if (nr < 0)
err(1, "read %s", handle);
if (close(outfd) != 0)
err(1, "close %s", output);
if (close(fd) != 0)
err(1, "close fhopen");
}
static void
mutate(const char *input, const char *output, const char *field,
const char *text)
{
struct erofs_test_fid efid;
fhandle_t fh;
uint64_t value;
read_handle(input, &fh);
bzero(&efid, sizeof(efid));
memcpy(&efid, &fh.fh_fid, sizeof(efid));
value = parse_value(text);
if (strcmp(field, "len") == 0) {
if (value > UINT16_MAX)
errx(2, "len is too large");
efid.len = value;
} else if (strcmp(field, "pad") == 0) {
if (value > UINT16_MAX)
errx(2, "pad is too large");
efid.pad = value;
} else if (strcmp(field, "nid_hi") == 0) {
if (value > UINT32_MAX)
errx(2, "nid_hi is too large");
efid.nid_hi = value;
} else if (strcmp(field, "nid_lo") == 0) {
if (value > UINT32_MAX)
errx(2, "nid_lo is too large");
efid.nid_lo = value;
} else if (strcmp(field, "gen") == 0) {
if (value > UINT32_MAX)
errx(2, "gen is too large");
efid.gen = value;
} else if (strcmp(field, "gen_xor") == 0) {
if (value == 0 || value > UINT32_MAX)
errx(2, "gen_xor must be a non-zero uint32_t");
efid.gen ^= value;
} else {
errx(2, "unknown field: %s", field);
}
memcpy(&fh.fh_fid, &efid, sizeof(efid));
write_handle(output, &fh);
}
static void
expect_failure(const char *path, const char *error_name, int use_open)
{
fhandle_t fh;
struct stat sb;
int expected, fd, result;
read_handle(path, &fh);
expected = parse_errno(error_name);
errno = 0;
if (use_open) {
fd = fhopen(&fh, O_RDONLY);
result = fd;
if (fd >= 0)
close(fd);
} else {
result = fhstat(&fh, &sb);
}
if (result != -1)
errx(1, "%s unexpectedly succeeded", use_open ? "fhopen" : "fhstat");
if (errno != expected)
errx(1, "%s returned %s, expected %s",
use_open ? "fhopen" : "fhstat", strerror(errno),
strerror(expected));
}
int
main(int argc, char **argv)
{
if (argc == 4 && strcmp(argv[1], "capture") == 0)
capture(argv[2], argv[3], 0);
else if (argc == 4 && strcmp(argv[1], "lcapture") == 0)
capture(argv[2], argv[3], 1);
else if (argc == 3 && strcmp(argv[1], "describe") == 0)
describe(argv[2]);
else if (argc == 4 && strcmp(argv[1], "compare") == 0)
compare(argv[2], argv[3]);
else if (argc == 3 && strcmp(argv[1], "stat") == 0)
stat_handle(argv[2]);
else if (argc == 4 && strcmp(argv[1], "cat") == 0)
cat_handle(argv[2], argv[3]);
else if (argc == 6 && strcmp(argv[1], "mutate") == 0)
mutate(argv[2], argv[3], argv[4], argv[5]);
else if (argc == 4 && strcmp(argv[1], "expect-stat") == 0)
expect_failure(argv[2], argv[3], 0);
else if (argc == 4 && strcmp(argv[1], "expect-open") == 0)
expect_failure(argv[2], argv[3], 1);
else
usage();
return (0);
}