190 lines
4.9 KiB
C
190 lines
4.9 KiB
C
#include <sys/types.h>
|
|
#include <sys/extattr.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
static int
|
|
fail(const char *message)
|
|
{
|
|
fprintf(stderr, "%s\n", message);
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
get_repeat(const char *path, const char *name, size_t expected_size)
|
|
{
|
|
uint8_t first[1024], second[1024];
|
|
ssize_t first_size, second_size;
|
|
|
|
if (expected_size > sizeof(first))
|
|
return (fail("xattr size exceeds probe buffer"));
|
|
memset(first, 0xa5, sizeof(first));
|
|
memset(second, 0x5a, sizeof(second));
|
|
errno = 0;
|
|
first_size = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name,
|
|
first, sizeof(first));
|
|
if (first_size != (ssize_t)expected_size || errno != 0)
|
|
return (fail("first xattr read returned the wrong size or errno"));
|
|
errno = 0;
|
|
second_size = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name,
|
|
second, sizeof(second));
|
|
if (second_size != first_size || errno != 0 ||
|
|
memcmp(first, second, expected_size) != 0)
|
|
return (fail("repeated xattr read changed its result"));
|
|
for (size_t offset = expected_size; offset < sizeof(first); offset++) {
|
|
if (first[offset] != 0xa5 || second[offset] != 0x5a)
|
|
return (fail("xattr read wrote beyond its value"));
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
get_errno(const char *path, const char *name, int expected_errno)
|
|
{
|
|
uint8_t value[32];
|
|
ssize_t size;
|
|
|
|
memset(value, 0xa5, sizeof(value));
|
|
errno = 0;
|
|
size = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, value,
|
|
sizeof(value));
|
|
if (size != -1 || errno != expected_errno) {
|
|
fprintf(stderr, "xattr errno mismatch: size=%zd errno=%d expected=%d\n",
|
|
size, errno, expected_errno);
|
|
return (fail("xattr returned the wrong positive errno"));
|
|
}
|
|
for (size_t offset = 0; offset < sizeof(value); offset++) {
|
|
if (value[offset] != 0xa5)
|
|
return (fail("failed xattr lookup modified the output"));
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
list_user(const char *path)
|
|
{
|
|
uint8_t *buffer;
|
|
ssize_t size;
|
|
size_t offset;
|
|
unsigned int seen;
|
|
|
|
errno = 0;
|
|
size = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0);
|
|
if (size <= 0 || errno != 0)
|
|
return (fail("xattr list sizing failed"));
|
|
buffer = malloc((size_t)size);
|
|
if (buffer == NULL)
|
|
return (fail("xattr list allocation failed"));
|
|
if (extattr_list_file(path, EXTATTR_NAMESPACE_USER, buffer,
|
|
(size_t)size) != size) {
|
|
free(buffer);
|
|
return (fail("xattr list transfer failed"));
|
|
}
|
|
seen = 0;
|
|
for (offset = 0; offset < (size_t)size;) {
|
|
const char *name;
|
|
uint8_t name_size;
|
|
|
|
name_size = buffer[offset++];
|
|
if (name_size == 0 || name_size > (size_t)size - offset) {
|
|
free(buffer);
|
|
return (fail("xattr list record is malformed"));
|
|
}
|
|
name = (const char *)(buffer + offset);
|
|
if (name_size == sizeof("inline") - 1 &&
|
|
memcmp(name, "inline", name_size) == 0) {
|
|
if ((seen & 1U) != 0) {
|
|
free(buffer);
|
|
return (fail("xattr list duplicated inline"));
|
|
}
|
|
seen |= 1U;
|
|
} else if (name_size == sizeof("shared") - 1 &&
|
|
memcmp(name, "shared", name_size) == 0) {
|
|
if ((seen & 2U) != 0) {
|
|
free(buffer);
|
|
return (fail("xattr list duplicated shared"));
|
|
}
|
|
seen |= 2U;
|
|
} else {
|
|
free(buffer);
|
|
return (fail("xattr list returned an unexpected name"));
|
|
}
|
|
offset += name_size;
|
|
}
|
|
free(buffer);
|
|
return (seen == 3U ? 0 : fail("xattr list omitted a name"));
|
|
}
|
|
|
|
static int
|
|
valid(const char *path)
|
|
{
|
|
if (get_repeat(path, "inline", 127) != 0 ||
|
|
get_repeat(path, "shared", 511) != 0 ||
|
|
list_user(path) != 0 ||
|
|
get_errno(path, "missing", ENOATTR) != 0)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
corrupt(const char *path)
|
|
{
|
|
return (get_errno(path, "inline", EINTEGRITY));
|
|
}
|
|
|
|
static int
|
|
concurrent(const char *path, long workers, long loops)
|
|
{
|
|
int status;
|
|
pid_t child;
|
|
|
|
for (long worker = 0; worker < workers; worker++) {
|
|
child = fork();
|
|
if (child < 0)
|
|
return (fail("xattr worker fork failed"));
|
|
if (child == 0) {
|
|
for (long iteration = 0; iteration < loops; iteration++) {
|
|
if (valid(path) != 0)
|
|
_exit(1);
|
|
}
|
|
_exit(0);
|
|
}
|
|
}
|
|
for (long worker = 0; worker < workers; worker++) {
|
|
if (wait(&status) < 0 || !WIFEXITED(status) ||
|
|
WEXITSTATUS(status) != 0)
|
|
return (fail("xattr worker failed"));
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
char *end;
|
|
long workers, loops;
|
|
|
|
if (argc == 3 && strcmp(argv[1], "valid") == 0)
|
|
return (valid(argv[2]));
|
|
if (argc == 3 && strcmp(argv[1], "corrupt") == 0)
|
|
return (corrupt(argv[2]));
|
|
if (argc == 5 && strcmp(argv[1], "concurrent") == 0) {
|
|
errno = 0;
|
|
workers = strtol(argv[3], &end, 10);
|
|
if (errno != 0 || end[0] != '\0' || workers < 1 || workers > 64)
|
|
return (fail("invalid worker count"));
|
|
errno = 0;
|
|
loops = strtol(argv[4], &end, 10);
|
|
if (errno != 0 || end[0] != '\0' || loops < 1 || loops > 100)
|
|
return (fail("invalid loop count"));
|
|
return (concurrent(argv[2], workers, loops));
|
|
}
|
|
return (fail("usage: B19a-xattr-probe valid|corrupt|concurrent ..."));
|
|
}
|