209 lines
5.4 KiB
C
209 lines
5.4 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
static int
|
|
parse_errno(const char *text)
|
|
{
|
|
char *end;
|
|
long value;
|
|
|
|
errno = 0;
|
|
value = strtol(text, &end, 0);
|
|
if (errno != 0 || *text == '\0' || *end != '\0' || value <= 0 ||
|
|
value > 255)
|
|
errx(2, "invalid errno: %s", text);
|
|
return ((int)value);
|
|
}
|
|
|
|
static int
|
|
run_operation(const char *operation, const char *path)
|
|
{
|
|
struct stat sb;
|
|
int fd;
|
|
|
|
if (strcmp(operation, "stat") == 0)
|
|
return (stat(path, &sb));
|
|
if (strcmp(operation, "lstat") == 0)
|
|
return (lstat(path, &sb));
|
|
if (strcmp(operation, "open-read") == 0) {
|
|
fd = open(path, O_RDONLY);
|
|
if (fd < 0)
|
|
return (-1);
|
|
return (close(fd));
|
|
}
|
|
if (strcmp(operation, "open-rdwr") == 0) {
|
|
fd = open(path, O_RDWR);
|
|
if (fd < 0)
|
|
return (-1);
|
|
return (close(fd));
|
|
}
|
|
if (strcmp(operation, "access-read") == 0)
|
|
return (access(path, R_OK));
|
|
if (strcmp(operation, "access-write") == 0)
|
|
return (access(path, W_OK));
|
|
if (strcmp(operation, "access-exec") == 0)
|
|
return (access(path, X_OK));
|
|
if (strcmp(operation, "chmod") == 0)
|
|
return (chmod(path, 0777));
|
|
if (strcmp(operation, "chown") == 0)
|
|
return (chown(path, 65534, 65534));
|
|
if (strcmp(operation, "truncate") == 0)
|
|
return (truncate(path, 0));
|
|
if (strcmp(operation, "create") == 0) {
|
|
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
|
if (fd < 0)
|
|
return (-1);
|
|
if (close(fd) != 0)
|
|
return (-1);
|
|
return (unlink(path));
|
|
}
|
|
errx(2, "unknown operation: %s", operation);
|
|
}
|
|
|
|
static void
|
|
expect_result(const char *operation, const char *path, int expected_errno)
|
|
{
|
|
int error, result;
|
|
|
|
errno = 0;
|
|
result = run_operation(operation, path);
|
|
error = errno;
|
|
if (expected_errno == 0) {
|
|
if (result != 0)
|
|
errx(1, "%s %s returned errno %d (%s)", operation, path,
|
|
error, strerror(error));
|
|
printf("op=%s result=success errno=0\n", operation);
|
|
return;
|
|
}
|
|
if (result == 0)
|
|
errx(1, "%s %s unexpectedly succeeded", operation, path);
|
|
if (error != expected_errno)
|
|
errx(1, "%s %s returned errno %d (%s), expected %d", operation,
|
|
path, error, strerror(error), expected_errno);
|
|
printf("op=%s result=error errno=%d message=%s\n", operation,
|
|
error, strerror(error));
|
|
}
|
|
|
|
static long
|
|
checked_pathconf(const char *path, int name, const char *label, int *errorp)
|
|
{
|
|
long value;
|
|
|
|
errno = 0;
|
|
value = pathconf(path, name);
|
|
*errorp = value == -1 ? errno : 0;
|
|
if (*errorp != 0)
|
|
printf("%s=error:%d ", label, *errorp);
|
|
else
|
|
printf("%s=%ld ", label, value);
|
|
return (value);
|
|
}
|
|
|
|
static void
|
|
show_pathconf(const char *path)
|
|
{
|
|
int chown_error, filesizebits_error, link_error, name_error,
|
|
no_trunc_error, path_error;
|
|
long chown_restricted, filesizebits, link_max, name_max, no_trunc,
|
|
path_max;
|
|
|
|
name_max = checked_pathconf(path, _PC_NAME_MAX, "name_max",
|
|
&name_error);
|
|
path_max = checked_pathconf(path, _PC_PATH_MAX, "path_max",
|
|
&path_error);
|
|
filesizebits = checked_pathconf(path, _PC_FILESIZEBITS, "filesizebits",
|
|
&filesizebits_error);
|
|
link_max = checked_pathconf(path, _PC_LINK_MAX, "link_max",
|
|
&link_error);
|
|
no_trunc = checked_pathconf(path, _PC_NO_TRUNC, "no_trunc",
|
|
&no_trunc_error);
|
|
chown_restricted = checked_pathconf(path, _PC_CHOWN_RESTRICTED,
|
|
"chown_restricted", &chown_error);
|
|
putchar('\n');
|
|
if (name_error != 0 || path_error != 0 || filesizebits_error != 0 ||
|
|
link_error != 0 || no_trunc_error != 0 || chown_error != 0 ||
|
|
name_max != 255 || path_max <= 0 || filesizebits != 64 ||
|
|
link_max <= 0 || no_trunc != 1 || chown_restricted != 1)
|
|
errx(1, "unexpected EROFS pathconf values");
|
|
}
|
|
|
|
static void
|
|
show_readlink(const char *path)
|
|
{
|
|
unsigned char target[4096];
|
|
uint64_t hash;
|
|
ssize_t length;
|
|
|
|
length = readlink(path, (char *)target, sizeof(target));
|
|
if (length < 0)
|
|
err(1, "readlink %s", path);
|
|
if ((size_t)length == sizeof(target))
|
|
errx(1, "symlink target is too long");
|
|
hash = UINT64_C(14695981039346656037);
|
|
for (ssize_t index = 0; index < length; index++) {
|
|
hash ^= target[index];
|
|
hash *= UINT64_C(1099511628211);
|
|
}
|
|
target[length] = '\0';
|
|
printf("length=%zd fnv1a64=%016" PRIx64 " target=%s\n", length,
|
|
hash, target);
|
|
}
|
|
|
|
static void
|
|
show_stat(const char *path)
|
|
{
|
|
struct stat sb;
|
|
|
|
if (lstat(path, &sb) != 0)
|
|
err(1, "lstat %s", path);
|
|
printf("ino=%ju mode=%#jo uid=%ju gid=%ju nlink=%ju size=%jd "
|
|
"blocks=%jd blksize=%jd",
|
|
(uintmax_t)sb.st_ino, (uintmax_t)sb.st_mode,
|
|
(uintmax_t)sb.st_uid, (uintmax_t)sb.st_gid,
|
|
(uintmax_t)sb.st_nlink, (intmax_t)sb.st_size,
|
|
(intmax_t)sb.st_blocks, (intmax_t)sb.st_blksize);
|
|
#ifdef __FreeBSD__
|
|
printf(" gen=%ju", (uintmax_t)sb.st_gen);
|
|
#endif
|
|
putchar('\n');
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
if (argc == 5 && strcmp(argv[1], "expect-error") == 0) {
|
|
expect_result(argv[2], argv[3], parse_errno(argv[4]));
|
|
return (0);
|
|
}
|
|
if (argc == 4 && strcmp(argv[1], "expect-success") == 0) {
|
|
expect_result(argv[2], argv[3], 0);
|
|
return (0);
|
|
}
|
|
if (argc == 3 && strcmp(argv[1], "pathconf") == 0) {
|
|
show_pathconf(argv[2]);
|
|
return (0);
|
|
}
|
|
if (argc == 3 && strcmp(argv[1], "readlink") == 0) {
|
|
show_readlink(argv[2]);
|
|
return (0);
|
|
}
|
|
if (argc == 3 && strcmp(argv[1], "stat") == 0) {
|
|
show_stat(argv[2]);
|
|
return (0);
|
|
}
|
|
errx(2, "usage: %s expect-error OP PATH ERRNO | "
|
|
"expect-success OP PATH | pathconf PATH | readlink PATH | stat PATH",
|
|
argv[0]);
|
|
}
|