#include #include #include #include #include #include #include #include #include #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...")); }