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