update
This commit is contained in:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user