test code v1
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
#ifndef __FreeBSD__
|
||||
#define _DEFAULT_SOURCE
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct observed_entry {
|
||||
char *name;
|
||||
off_t cookie;
|
||||
unsigned char type;
|
||||
};
|
||||
|
||||
struct observed_list {
|
||||
struct observed_entry *entries;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
static void
|
||||
append_entry(struct observed_list *list, const char *name, off_t cookie,
|
||||
unsigned char type)
|
||||
{
|
||||
struct observed_entry *entries;
|
||||
|
||||
if (list->count == list->capacity) {
|
||||
list->capacity = list->capacity == 0 ? 64 : list->capacity * 2;
|
||||
entries = realloc(list->entries,
|
||||
list->capacity * sizeof(*list->entries));
|
||||
if (entries == NULL)
|
||||
err(1, "realloc entries");
|
||||
list->entries = entries;
|
||||
}
|
||||
list->entries[list->count].name = strdup(name);
|
||||
if (list->entries[list->count].name == NULL)
|
||||
err(1, "strdup");
|
||||
list->entries[list->count].cookie = cookie;
|
||||
list->entries[list->count].type = type;
|
||||
list->count++;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
entry_hash(const struct observed_list *list)
|
||||
{
|
||||
uint64_t hash;
|
||||
|
||||
hash = UINT64_C(14695981039346656037);
|
||||
for (size_t index = 0; index < list->count; index++) {
|
||||
for (const unsigned char *name =
|
||||
(const unsigned char *)list->entries[index].name;
|
||||
*name != '\0'; name++) {
|
||||
hash ^= *name;
|
||||
hash *= UINT64_C(1099511628211);
|
||||
}
|
||||
hash ^= 0;
|
||||
hash *= UINT64_C(1099511628211);
|
||||
hash ^= list->entries[index].type;
|
||||
hash *= UINT64_C(1099511628211);
|
||||
}
|
||||
return (hash);
|
||||
}
|
||||
|
||||
static void
|
||||
print_type_counts(const struct observed_list *list)
|
||||
{
|
||||
size_t block, character, directory, fifo, link, other, regular, socket,
|
||||
unknown;
|
||||
|
||||
block = character = directory = fifo = link = other = regular = socket =
|
||||
unknown = 0;
|
||||
for (size_t index = 0; index < list->count; index++) {
|
||||
switch (list->entries[index].type) {
|
||||
case DT_BLK:
|
||||
block++;
|
||||
break;
|
||||
case DT_CHR:
|
||||
character++;
|
||||
break;
|
||||
case DT_DIR:
|
||||
directory++;
|
||||
break;
|
||||
case DT_FIFO:
|
||||
fifo++;
|
||||
break;
|
||||
case DT_LNK:
|
||||
link++;
|
||||
break;
|
||||
case DT_REG:
|
||||
regular++;
|
||||
break;
|
||||
case DT_SOCK:
|
||||
socket++;
|
||||
break;
|
||||
case DT_UNKNOWN:
|
||||
unknown++;
|
||||
break;
|
||||
default:
|
||||
other++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("types=dir:%zu,reg:%zu,lnk:%zu,chr:%zu,blk:%zu,fifo:%zu,"
|
||||
"sock:%zu,unknown:%zu,other:%zu ", directory, regular, link,
|
||||
character, block, fifo, socket, unknown, other);
|
||||
}
|
||||
|
||||
static void
|
||||
free_list(struct observed_list *list)
|
||||
{
|
||||
for (size_t index = 0; index < list->count; index++)
|
||||
free(list->entries[index].name);
|
||||
free(list->entries);
|
||||
}
|
||||
|
||||
static void
|
||||
check_unique_names(const struct observed_list *list)
|
||||
{
|
||||
for (size_t left = 0; left < list->count; left++) {
|
||||
for (size_t right = left + 1; right < list->count; right++) {
|
||||
if (strcmp(list->entries[left].name,
|
||||
list->entries[right].name) == 0)
|
||||
errx(1, "duplicate name: %s",
|
||||
list->entries[left].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct observed_list
|
||||
scan_getdirentries(const char *path, size_t buffer_size)
|
||||
{
|
||||
struct observed_list list = { 0 };
|
||||
struct dirent *entry;
|
||||
char *buffer, *end;
|
||||
off_t base, previous;
|
||||
ssize_t bytes;
|
||||
int fd;
|
||||
|
||||
fd = open(path, O_RDONLY | O_DIRECTORY);
|
||||
if (fd < 0)
|
||||
err(1, "open %s", path);
|
||||
buffer = malloc(buffer_size);
|
||||
if (buffer == NULL)
|
||||
err(1, "malloc");
|
||||
previous = 0;
|
||||
for (;;) {
|
||||
base = -1;
|
||||
bytes = getdirentries(fd, buffer, buffer_size, &base);
|
||||
if (bytes < 0)
|
||||
err(1, "getdirentries %s", path);
|
||||
if (bytes == 0)
|
||||
break;
|
||||
end = buffer + bytes;
|
||||
for (entry = (struct dirent *)buffer; (char *)entry < end;
|
||||
entry = (struct dirent *)((char *)entry + entry->d_reclen)) {
|
||||
if (entry->d_reclen == 0 ||
|
||||
(char *)entry + entry->d_reclen > end)
|
||||
errx(1, "invalid dirent record");
|
||||
if (entry->d_off <= previous)
|
||||
errx(1, "non-increasing d_off %jd after %jd",
|
||||
(intmax_t)entry->d_off, (intmax_t)previous);
|
||||
append_entry(&list, entry->d_name, entry->d_off,
|
||||
entry->d_type);
|
||||
previous = entry->d_off;
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
if (close(fd) != 0)
|
||||
err(1, "close %s", path);
|
||||
check_unique_names(&list);
|
||||
return (list);
|
||||
}
|
||||
|
||||
static void
|
||||
verify_kernel_cookies(const char *path, size_t buffer_size,
|
||||
const struct observed_list *list)
|
||||
{
|
||||
struct dirent *entry;
|
||||
char *buffer;
|
||||
off_t base;
|
||||
ssize_t bytes;
|
||||
int fd;
|
||||
|
||||
buffer = malloc(buffer_size);
|
||||
if (buffer == NULL)
|
||||
err(1, "malloc restart buffer");
|
||||
for (size_t index = 0; index < list->count; index++) {
|
||||
fd = open(path, O_RDONLY | O_DIRECTORY);
|
||||
if (fd < 0)
|
||||
err(1, "open restart %s", path);
|
||||
if (lseek(fd, list->entries[index].cookie, SEEK_SET) < 0)
|
||||
err(1, "lseek cookie %jd",
|
||||
(intmax_t)list->entries[index].cookie);
|
||||
base = -1;
|
||||
bytes = getdirentries(fd, buffer, buffer_size, &base);
|
||||
if (bytes < 0)
|
||||
err(1, "getdirentries restart");
|
||||
if (index + 1 == list->count) {
|
||||
if (bytes != 0)
|
||||
errx(1, "final d_off cookie did not reach EOF");
|
||||
} else {
|
||||
if (bytes <= 0)
|
||||
errx(1, "restart cookie returned no dirent");
|
||||
entry = (struct dirent *)buffer;
|
||||
if (entry->d_reclen == 0 || entry->d_reclen > (size_t)bytes)
|
||||
errx(1, "restart cookie returned a truncated dirent");
|
||||
if (strcmp(entry->d_name, list->entries[index + 1].name) != 0)
|
||||
errx(1, "cookie %jd resumed at %s, expected %s",
|
||||
(intmax_t)list->entries[index].cookie,
|
||||
entry->d_name, list->entries[index + 1].name);
|
||||
}
|
||||
if (close(fd) != 0)
|
||||
err(1, "close restart");
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static struct observed_list
|
||||
scan_readdir(const char *path)
|
||||
{
|
||||
struct observed_list list = { 0 };
|
||||
struct dirent *entry;
|
||||
DIR *directory;
|
||||
long cookie, previous;
|
||||
|
||||
directory = opendir(path);
|
||||
if (directory == NULL)
|
||||
err(1, "opendir %s", path);
|
||||
previous = 0;
|
||||
while ((entry = readdir(directory)) != NULL) {
|
||||
cookie = telldir(directory);
|
||||
if (cookie <= previous)
|
||||
errx(1, "non-increasing telldir cookie %ld after %ld",
|
||||
cookie, previous);
|
||||
append_entry(&list, entry->d_name, (off_t)cookie,
|
||||
entry->d_type);
|
||||
previous = cookie;
|
||||
}
|
||||
if (closedir(directory) != 0)
|
||||
err(1, "closedir %s", path);
|
||||
check_unique_names(&list);
|
||||
return (list);
|
||||
}
|
||||
|
||||
static void
|
||||
verify_seekdir(const char *path, const struct observed_list *list)
|
||||
{
|
||||
struct dirent *entry;
|
||||
DIR *directory;
|
||||
long cookie;
|
||||
|
||||
for (size_t index = 0; index < list->count; index++) {
|
||||
directory = opendir(path);
|
||||
if (directory == NULL)
|
||||
err(1, "opendir restart %s", path);
|
||||
for (size_t position = 0; position <= index; position++) {
|
||||
entry = readdir(directory);
|
||||
if (entry == NULL ||
|
||||
strcmp(entry->d_name, list->entries[position].name) != 0)
|
||||
errx(1, "seekdir setup differs at entry %zu",
|
||||
position);
|
||||
}
|
||||
cookie = telldir(directory);
|
||||
if ((off_t)cookie != list->entries[index].cookie)
|
||||
errx(1, "telldir cookie differs at entry %zu", index);
|
||||
seekdir(directory, cookie);
|
||||
entry = readdir(directory);
|
||||
if (index + 1 == list->count) {
|
||||
if (entry != NULL)
|
||||
errx(1, "final telldir cookie did not reach EOF");
|
||||
} else if (entry == NULL ||
|
||||
strcmp(entry->d_name, list->entries[index + 1].name) != 0) {
|
||||
errx(1, "seekdir cookie %jd resumed at %s, expected %s",
|
||||
(intmax_t)list->entries[index].cookie,
|
||||
entry == NULL ? "EOF" : entry->d_name,
|
||||
list->entries[index + 1].name);
|
||||
}
|
||||
if (closedir(directory) != 0)
|
||||
err(1, "closedir restart");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct observed_list kernel, libc;
|
||||
char *end;
|
||||
unsigned long raw_size;
|
||||
size_t buffer_size;
|
||||
|
||||
if (argc < 2 || argc > 3)
|
||||
errx(2, "usage: %s directory [buffer-size]", argv[0]);
|
||||
buffer_size = 128;
|
||||
if (argc == 3) {
|
||||
raw_size = strtoul(argv[2], &end, 0);
|
||||
if (*argv[2] == '\0' || *end != '\0' || raw_size < 64 ||
|
||||
raw_size > 1024 * 1024)
|
||||
errx(2, "invalid buffer size: %s", argv[2]);
|
||||
buffer_size = (size_t)raw_size;
|
||||
}
|
||||
kernel = scan_getdirentries(argv[1], buffer_size);
|
||||
verify_kernel_cookies(argv[1], buffer_size, &kernel);
|
||||
libc = scan_readdir(argv[1]);
|
||||
verify_seekdir(argv[1], &libc);
|
||||
if (kernel.count != libc.count)
|
||||
errx(1, "getdirentries count %zu differs from readdir count %zu",
|
||||
kernel.count, libc.count);
|
||||
for (size_t index = 0; index < kernel.count; index++) {
|
||||
if (strcmp(kernel.entries[index].name, libc.entries[index].name) != 0)
|
||||
errx(1, "API order differs at entry %zu", index);
|
||||
if (kernel.entries[index].type != libc.entries[index].type)
|
||||
errx(1, "API type differs at entry %zu", index);
|
||||
}
|
||||
print_type_counts(&kernel);
|
||||
printf("entries=%zu d_off_restarts=%zu seekdir_restarts=%zu "
|
||||
"buffer=%zu fnv1a64=%016" PRIx64 "\n", kernel.count,
|
||||
kernel.count, libc.count, buffer_size, entry_hash(&kernel));
|
||||
free_list(&libc);
|
||||
free_list(&kernel);
|
||||
return (0);
|
||||
}
|
||||
Reference in New Issue
Block a user