Files
erofs-freebsd-out-tree/tests/pre15/fixtures/B19b-xattr-probe.c
T
2026-08-18 09:20:44 +02:00

208 lines
5.4 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>
#define VALUE_SIZE 96
static int
fail(const char *message)
{
fprintf(stderr, "%s\n", message);
return (1);
}
static int
parse_long(const char *text, long minimum, long maximum, long *valuep)
{
char *end;
long value;
errno = 0;
value = strtol(text, &end, 10);
if (errno != 0 || text[0] == '\0' || end[0] != '\0' ||
value < minimum || value > maximum)
return (-1);
*valuep = value;
return (0);
}
static int
namespace_id(const char *name)
{
if (strcmp(name, "user") == 0)
return (EXTATTR_NAMESPACE_USER);
if (strcmp(name, "system") == 0)
return (EXTATTR_NAMESPACE_SYSTEM);
return (-1);
}
static void
expected_value(unsigned int index, uint8_t *value)
{
char prefix[16];
size_t length;
(void)snprintf(prefix, sizeof(prefix), "value-%02u-", index);
length = strlen(prefix);
for (size_t offset = 0; offset < VALUE_SIZE; offset++)
value[offset] = prefix[offset % length];
}
static int
get_hit(const char *path, const char *name, unsigned int index)
{
uint8_t actual[VALUE_SIZE + 16], expected[VALUE_SIZE];
ssize_t length;
memset(actual, 0xa5, sizeof(actual));
expected_value(index, expected);
errno = 0;
length = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, actual,
sizeof(actual));
if (length != VALUE_SIZE || errno != 0)
return (fail("extattr hit returned the wrong length or errno"));
if (memcmp(actual, expected, sizeof(expected)) != 0)
return (fail("extattr hit returned the wrong value"));
for (size_t offset = VALUE_SIZE; offset < sizeof(actual); offset++) {
if (actual[offset] != 0xa5)
return (fail("extattr hit wrote beyond the value"));
}
return (0);
}
static int
get_errno(const char *path, int attrnamespace, const char *name,
int expected_errno)
{
uint8_t actual[VALUE_SIZE + 16];
ssize_t length;
memset(actual, 0xa5, sizeof(actual));
errno = 0;
length = extattr_get_file(path, attrnamespace, name, actual,
sizeof(actual));
if (length != -1 || errno != expected_errno)
return (fail("extattr failure returned the wrong positive errno"));
for (size_t offset = 0; offset < sizeof(actual); offset++) {
if (actual[offset] != 0xa5)
return (fail("failed extattr lookup modified the output"));
}
return (0);
}
static int
list_user(const char *path)
{
uint8_t *buffer;
ssize_t length;
size_t offset;
unsigned int seen;
errno = 0;
length = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0);
if (length <= 0 || errno != 0)
return (fail("extattr list sizing failed"));
buffer = malloc((size_t)length);
if (buffer == NULL)
return (fail("extattr list allocation failed"));
if (extattr_list_file(path, EXTATTR_NAMESPACE_USER, buffer,
(size_t)length) != length) {
free(buffer);
return (fail("extattr list transfer failed"));
}
seen = 0;
for (offset = 0; offset < (size_t)length;) {
char expected[16];
uint8_t name_length;
unsigned int index;
name_length = buffer[offset++];
if (name_length == 0 || name_length > (size_t)length - offset) {
free(buffer);
return (fail("extattr list record is malformed"));
}
for (index = 0; index < 8; index++) {
(void)snprintf(expected, sizeof(expected), "attr%02u", index);
if (strlen(expected) == name_length &&
memcmp(buffer + offset, expected, name_length) == 0)
break;
}
if (index == 8 || (seen & (1U << index)) != 0) {
free(buffer);
return (fail("extattr list returned an unknown or duplicate name"));
}
seen |= 1U << index;
offset += name_length;
}
free(buffer);
if (seen != 0xff)
return (fail("extattr list omitted a user attribute"));
return (0);
}
static int
concurrent(const char *path, const char *hit, const char *miss,
const char *collision, long workers, long loops)
{
long worker;
int status;
pid_t child;
for (worker = 0; worker < workers; worker++) {
child = fork();
if (child < 0)
return (fail("fork failed"));
if (child == 0) {
for (long iteration = 0; iteration < loops; iteration++) {
if (get_hit(path, hit, 0) != 0 ||
get_errno(path, EXTATTR_NAMESPACE_USER, miss, ENOATTR) != 0 ||
get_errno(path, EXTATTR_NAMESPACE_USER, collision, ENOATTR) != 0)
_exit(1);
}
_exit(0);
}
}
for (worker = 0; worker < workers; worker++) {
if (wait(&status) < 0 || !WIFEXITED(status) ||
WEXITSTATUS(status) != 0)
return (fail("concurrent extattr worker failed"));
}
return (0);
}
int
main(int argc, char **argv)
{
long first, second;
int attrnamespace;
if (argc == 5 && strcmp(argv[1], "hit") == 0) {
if (parse_long(argv[4], 0, 7, &first) != 0)
return (fail("invalid hit index"));
return (get_hit(argv[2], argv[3], (unsigned int)first));
}
if (argc == 6 && strcmp(argv[1], "errno") == 0) {
attrnamespace = namespace_id(argv[3]);
if (attrnamespace < 0 || parse_long(argv[5], 1, 255, &first) != 0)
return (fail("invalid errno arguments"));
return (get_errno(argv[2], attrnamespace, argv[4], (int)first));
}
if (argc == 3 && strcmp(argv[1], "list") == 0)
return (list_user(argv[2]));
if (argc == 8 && strcmp(argv[1], "concurrent") == 0) {
if (parse_long(argv[6], 1, 64, &first) != 0 ||
parse_long(argv[7], 1, 1000, &second) != 0)
return (fail("invalid concurrency arguments"));
return (concurrent(argv[2], argv[3], argv[4], argv[5], first,
second));
}
return (fail("usage: B19b-xattr-probe MODE PATH ARG..."));
}