#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include 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]); }