update
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
#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..."));
|
||||
}
|
||||
Reference in New Issue
Block a user