summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/fs-tests/integrity/integck.c390
1 files changed, 265 insertions, 125 deletions
diff --git a/tests/fs-tests/integrity/integck.c b/tests/fs-tests/integrity/integck.c
index 238a71d..481a035 100644
--- a/tests/fs-tests/integrity/integck.c
+++ b/tests/fs-tests/integrity/integck.c
@@ -46,17 +46,21 @@ struct write_info /* Record of random data written into a file */
int trunc; /* Records a truncation (raw_writes only) */
};
+struct dir_entry_info;
+
struct file_info /* Each file has one of these */
{
- char *name;
- struct dir_info *parent; /* Parent directory */
+ char *name; /* Original name */
struct write_info *writes; /* Record accumulated writes to the file */
struct write_info *raw_writes;
/* Record in order all writes to the file */
struct fd_info *fds; /* All open file descriptors for this file */
+ struct dir_entry_info *links;
+ int link_count;
off_t length;
int deleted; /* File has been deleted but is still open */
int no_space_error; /* File has incurred a ENOSPC error */
+ uint64_t check_run_no; /* Run number used when checking */
};
struct dir_info /* Each directory has one of these */
@@ -66,12 +70,18 @@ struct dir_info /* Each directory has one of these */
for our top directory */
unsigned number_of_entries;
struct dir_entry_info *first;
+ struct dir_entry_info *entry; /* Dir entry of this dir */
};
struct dir_entry_info /* Each entry in a directory has one of these */
{
- struct dir_entry_info *next;
- char type; /* f => file, d=> dir */
+ struct dir_entry_info *next; /* List of entries in directory */
+ struct dir_entry_info *prev; /* List of entries in directory */
+ struct dir_entry_info *next_link; /* List of hard links for same file */
+ struct dir_entry_info *prev_link; /* List of hard links for same file */
+ char *name;
+ struct dir_info *parent; /* Parent directory */
+ char type; /* f => file, d => dir */
int checked; /* Temporary flag used when checking */
union entry_
{
@@ -115,6 +125,8 @@ static int can_mmap = 0; /* Can write via mmap */
static long mem_page_size; /* Page size for mmap */
+static uint64_t check_run_no;
+
static char *copy_string(const char *s)
{
char *str;
@@ -195,18 +207,6 @@ static char *dir_path(struct dir_info *parent, const char *name)
return path;
}
-static struct dir_entry_info *dir_entry_new(void)
-{
- struct dir_entry_info *entry;
- size_t sz;
-
- sz = sizeof(struct dir_entry_info);
- entry = (struct dir_entry_info *) malloc(sz);
- CHECK(entry != NULL);
- memset(entry, 0, sz);
- return entry;
-}
-
static void open_file_add(struct fd_info *fdi)
{
struct open_file_info *ofi;
@@ -240,7 +240,7 @@ static void open_file_remove(struct fd_info *fdi)
CHECK(0); /* We are trying to remove something that is not there */
}
-static struct fd_info *fd_new(struct file_info *file, int fd)
+static struct fd_info *add_fd(struct file_info *file, int fd)
{
struct fd_info *fdi;
size_t sz;
@@ -257,6 +257,70 @@ static struct fd_info *fd_new(struct file_info *file, int fd)
return fdi;
}
+static void add_dir_entry(struct dir_info *parent, char type, const char *name,
+ void *target)
+{
+ struct dir_entry_info *entry;
+ size_t sz;
+
+ sz = sizeof(struct dir_entry_info);
+ entry = (struct dir_entry_info *) malloc(sz);
+ CHECK(entry != NULL);
+ memset(entry, 0, sz);
+
+ entry->type = type;
+ entry->name = copy_string(name);
+ entry->parent = parent;
+
+ entry->next = parent->first;
+ if (parent->first)
+ parent->first->prev = entry;
+ parent->first = entry;
+ parent->number_of_entries += 1;
+
+ if (entry->type == 'f') {
+ struct file_info *file = target;
+
+ entry->entry.file = file;
+ entry->next_link = file->links;
+ if (file->links)
+ file->links->prev_link = entry;
+ file->links = entry;
+ file->link_count += 1;
+ } else if (entry->type == 'd') {
+ struct dir_info *dir = target;
+
+ entry->entry.dir = dir;
+ dir->entry = entry;
+ }
+}
+
+static void remove_dir_entry(struct dir_entry_info *entry)
+{
+ entry->parent->number_of_entries -= 1;
+ if (entry->parent->first == entry)
+ entry->parent->first = entry->next;
+ if (entry->prev)
+ entry->prev->next = entry->next;
+ if (entry->next)
+ entry->next->prev = entry->prev;
+
+ if (entry->type == 'f') {
+ struct file_info *file = entry->entry.file;
+
+ if (entry->prev_link)
+ entry->prev_link->next_link = entry->next_link;
+ if (entry->next_link)
+ entry->next_link->prev_link = entry->prev_link;
+ if (file->links == entry)
+ file->links = entry->next_link;
+ file->link_count -= 1;
+ }
+
+ free(entry->name);
+ free(entry);
+}
+
static struct dir_info *dir_new(struct dir_info *parent, const char *name)
{
struct dir_info *dir;
@@ -278,27 +342,17 @@ static struct dir_info *dir_new(struct dir_info *parent, const char *name)
memset(dir, 0, sz);
dir->name = copy_string(name);
dir->parent = parent;
- if (parent) {
- struct dir_entry_info *entry;
-
- entry = dir_entry_new();
- entry->type = 'd';
- entry->entry.dir = dir;
- entry->next = parent->first;
- parent->first = entry;
- parent->number_of_entries += 1;
- }
+ if (parent)
+ add_dir_entry(parent, 'd', name, dir);
return dir;
}
static void file_delete(struct file_info *file);
+static void file_unlink(struct dir_entry_info *entry);
static void dir_remove(struct dir_info *dir)
{
char *path;
- struct dir_entry_info *entry;
- struct dir_entry_info **prev;
- int found;
/* Remove directory contents */
while (dir->first) {
@@ -308,27 +362,16 @@ static void dir_remove(struct dir_info *dir)
if (entry->type == 'd')
dir_remove(entry->entry.dir);
else if (entry->type == 'f')
- file_delete(entry->entry.file);
+ file_unlink(entry);
else
CHECK(0); /* Invalid struct dir_entry_info */
}
/* Remove entry from parent directory */
- found = 0;
- prev = &dir->parent->first;
- for (entry = dir->parent->first; entry; entry = entry->next) {
- if (entry->type == 'd' && entry->entry.dir == dir) {
- dir->parent->number_of_entries -= 1;
- *prev = entry->next;
- free(entry);
- found = 1;
- break;
- }
- prev = &entry->next;
- }
- CHECK(found); /* Check the file is in the parent directory */
+ remove_dir_entry(dir->entry);
/* Remove directory itself */
path = dir_path(dir->parent, dir->name);
CHECK(rmdir(path) != -1);
+ free(dir);
}
static struct file_info *file_new(struct dir_info *parent, const char *name)
@@ -338,7 +381,6 @@ static struct file_info *file_new(struct dir_info *parent, const char *name)
mode_t mode;
int fd;
size_t sz;
- struct dir_entry_info *entry;
CHECK(parent != NULL);
@@ -358,49 +400,72 @@ static struct file_info *file_new(struct dir_info *parent, const char *name)
CHECK(file != NULL);
memset(file, 0, sz);
file->name = copy_string(name);
- file->parent = parent;
- fd_new(file, fd);
+ add_dir_entry(parent, 'f', name, file);
- entry = dir_entry_new();
- entry->type = 'f';
- entry->entry.file = file;
- entry->next = parent->first;
- parent->first = entry;
- parent->number_of_entries += 1;
+ add_fd(file, fd);
return file;
}
-static void file_delete(struct file_info *file)
+static void link_new(struct dir_info *parent, const char *name,
+ struct file_info *file)
{
- char *path;
struct dir_entry_info *entry;
- struct dir_entry_info **prev;
- int found;
+ char *path, *target;
+ int ret;
- /* Remove file entry from parent directory */
- found = 0;
- prev = &file->parent->first;
- for (entry = file->parent->first; entry; entry = entry->next) {
- if (entry->type == 'f' && entry->entry.file == file) {
- file->parent->number_of_entries -= 1;
- *prev = entry->next;
- free(entry);
- found = 1;
- break;
- }
- prev = &entry->next;
+ if (!file)
+ return;
+ entry = file->links;
+ if (!entry)
+ return;
+ path = dir_path(parent, name);
+ target = dir_path(entry->parent, entry->name);
+ ret = link(target, path);
+ if (ret == -1) {
+ CHECK(errno == ENOSPC);
+ free(target);
+ free(path);
+ full = 1;
+ return;
+ }
+ free(target);
+ free(path);
+
+ add_dir_entry(parent, 'f', name, file);
+}
+
+static void file_close(struct fd_info *fdi);
+
+static void file_close_all(struct file_info *file)
+{
+ struct fd_info *fdi = file->fds;
+
+ while (fdi) {
+ struct fd_info *next = fdi->next;
+
+ file_close(fdi);
+ fdi = next;
}
- CHECK(found); /* Check the file is in the parent directory */
+}
+
+static void file_unlink(struct dir_entry_info *entry)
+{
+ struct file_info *file = entry->entry.file;
+ char *path;
+
+ path = dir_path(entry->parent, entry->name);
+
+ /* Remove file entry from parent directory */
+ remove_dir_entry(entry);
- /* Delete the file */
- path = dir_path(file->parent, file->name);
+ /* Unlink the file */
CHECK(unlink(path) != -1);
free(path);
- /* Free struct file_info if file is not open */
- if (!file->fds) {
+ /* Free struct file_info if file is not open and not linked */
+ if (!file->fds && !file->links) {
struct write_info *w, *next;
free(file->name);
@@ -411,18 +476,63 @@ static void file_delete(struct file_info *file)
w = next;
}
free(file);
- } else
+ } else if (!file->links)
file->deleted = 1;
}
+static struct dir_entry_info *pick_entry(struct file_info *file)
+{
+ struct dir_entry_info *entry;
+ size_t r;
+
+ if (!file->link_count)
+ return NULL;
+ r = tests_random_no(file->link_count);
+ entry = file->links;
+ while (entry && r--)
+ entry = entry->next_link;
+ return entry;
+}
+
+static void file_unlink_file(struct file_info *file)
+{
+ struct dir_entry_info *entry;
+
+ entry = pick_entry(file);
+ if (!entry)
+ return;
+ file_unlink(entry);
+}
+
+static void file_delete(struct file_info *file)
+{
+ struct dir_entry_info *entry = file->links;
+
+ file_close_all(file);
+ while (entry) {
+ struct dir_entry_info *next = entry->next_link;
+
+ file_unlink(entry);
+ entry = next;
+ }
+}
+
static void file_info_display(struct file_info *file)
{
+ struct dir_entry_info *entry;
struct write_info *w;
unsigned wcnt;
fprintf(stderr, "File Info:\n");
- fprintf(stderr, " Name: %s\n", file->name);
- fprintf(stderr, " Directory: %s\n", file->parent->name);
+ fprintf(stderr, " Original name: %s\n", file->name);
+ fprintf(stderr, " Link count: %d\n", file->link_count);
+ fprintf(stderr, " Links:\n");
+ entry = file->links;
+ while (entry) {
+ fprintf(stderr, " Name: %s\n", entry->name);
+ fprintf(stderr, " Directory: %s\n", entry->parent->name);
+ entry = entry->next_link;
+ }
fprintf(stderr, " Length: %u\n", (unsigned) file->length);
fprintf(stderr, " File was open: %s\n",
(file->fds == NULL) ? "false" : "true");
@@ -472,13 +582,13 @@ static struct fd_info *file_open(struct file_info *file)
int fd, flags = O_RDWR;
char *path;
- path = dir_path(file->parent, file->name);
+ path = dir_path(file->links->parent, file->links->name);
if (tests_random_no(100) == 1)
flags |= O_SYNC;
fd = open(path, flags);
CHECK(fd != -1);
free(path);
- return fd_new(file, fd);
+ return add_fd(file, fd);
}
#define BUFFER_SIZE 32768
@@ -656,7 +766,6 @@ static void get_offset_and_size(struct file_info *file,
}
static void file_truncate_info(struct file_info *file, size_t new_length);
-static void file_close(struct fd_info *fdi);
static int file_ftruncate(struct file_info *file, int fd, off_t new_length)
{
@@ -664,16 +773,8 @@ static int file_ftruncate(struct file_info *file, int fd, off_t new_length)
CHECK(errno = ENOSPC);
file->no_space_error = 1;
/* Delete errored files */
- if (!check_nospc_files && !file->deleted) {
- struct fd_info *fdi;
-
- fdi = file->fds;
- while (fdi) {
- file_close(fdi);
- fdi = file->fds;
- }
+ if (!check_nospc_files)
file_delete(file);
- }
return 0;
}
return 1;
@@ -691,6 +792,8 @@ static void file_mmap_write(struct file_info *file)
int fd;
char *path;
+ if (!file->links)
+ return;
free_space = tests_get_free_space();
if (!free_space)
return;
@@ -712,7 +815,7 @@ static void file_mmap_write(struct file_info *file)
len = 1 << 24;
/* Open it */
- path = dir_path(file->parent, file->name);
+ path = dir_path(file->links->parent, file->links->name);
fd = open(path, O_RDWR);
CHECK(fd != -1);
free(path);
@@ -771,16 +874,7 @@ static void file_write(struct file_info *file, int fd)
/* Delete errored files */
if (!check_nospc_files && file->no_space_error) {
- if (!file->deleted) {
- struct fd_info *fdi;
-
- fdi = file->fds;
- while (fdi) {
- file_close(fdi);
- fdi = file->fds;
- }
- file_delete(file);
- }
+ file_delete(file);
return;
}
@@ -796,7 +890,7 @@ static void file_write_file(struct file_info *file)
int fd;
char *path;
- path = dir_path(file->parent, file->name);
+ path = dir_path(file->links->parent, file->links->name);
fd = open(path, O_WRONLY);
CHECK(fd != -1);
file_write(file, fd);
@@ -855,7 +949,7 @@ static void file_truncate_file(struct file_info *file)
int fd;
char *path;
- path = dir_path(file->parent, file->name);
+ path = dir_path(file->links->parent, file->links->name);
fd = open(path, O_WRONLY);
CHECK(fd != -1);
file_truncate(file, fd);
@@ -1040,19 +1134,24 @@ static void file_check_data( struct file_info *file,
static void file_check(struct file_info *file, int fd)
{
- int open_and_close = 0;
+ int open_and_close = 0, link_count = 0;
char *path = NULL;
off_t pos;
struct write_info *w;
+ struct dir_entry_info *entry;
/* Do not check files that have errored */
if (!check_nospc_files && file->no_space_error)
return;
+ /* Do not check the same file twice */
+ if (file->check_run_no == check_run_no)
+ return;
+ file->check_run_no = check_run_no;
if (fd == -1)
open_and_close = 1;
if (open_and_close) {
/* Open file */
- path = dir_path(file->parent, file->name);
+ path = dir_path(file->links->parent, file->links->name);
fd = open(path, O_RDONLY);
CHECK(fd != -1);
}
@@ -1081,26 +1180,19 @@ static void file_check(struct file_info *file, int fd)
CHECK(close(fd) != -1);
free(path);
}
-}
-
-static const char *dir_entry_name(const struct dir_entry_info *entry)
-{
- CHECK(entry != NULL);
- if (entry->type == 'd')
- return entry->entry.dir->name;
- else if (entry->type == 'f')
- return entry->entry.file->name;
- else {
- CHECK(0);
- return NULL;
+ entry = file->links;
+ while (entry) {
+ link_count += 1;
+ entry = entry->next_link;
}
+ CHECK(link_count == file->link_count);
}
static int search_comp(const void *pa, const void *pb)
{
const struct dirent *a = (const struct dirent *) pa;
const struct dir_entry_info *b = * (const struct dir_entry_info **) pb;
- return strcmp(a->d_name, dir_entry_name(b));
+ return strcmp(a->d_name, b->name);
}
static void dir_entry_check(struct dir_entry_info **entry_array,
@@ -1123,7 +1215,7 @@ static int sort_comp(const void *pa, const void *pb)
{
const struct dir_entry_info *a = * (const struct dir_entry_info **) pa;
const struct dir_entry_info *b = * (const struct dir_entry_info **) pb;
- return strcmp(dir_entry_name(a), dir_entry_name(b));
+ return strcmp(a->name, b->name);
}
static void dir_check(struct dir_info *dir)
@@ -1225,7 +1317,7 @@ static char *make_name(struct dir_info *dir)
} else
sprintf(name, "%u", (unsigned) tests_random_no(1000000));
for (entry = dir->first; entry; entry = entry->next) {
- if (strcmp(dir_entry_name(entry), name) == 0) {
+ if (strcmp(entry->name, name) == 0) {
found = 1;
break;
}
@@ -1234,28 +1326,60 @@ static char *make_name(struct dir_info *dir)
return name;
}
+static struct file_info *pick_file(void)
+{
+ struct dir_info *dir = top_dir;
+
+ for (;;) {
+ struct dir_entry_info *entry;
+ size_t r;
+
+ r = tests_random_no(dir->number_of_entries);
+ entry = dir->first;
+ while (entry && r) {
+ entry = entry->next;
+ --r;
+ }
+ for (;;) {
+ if (!entry)
+ return NULL;
+ if (entry->type == 'f')
+ return entry->entry.file;
+ if (entry->type == 'd')
+ if (entry->entry.dir->number_of_entries != 0)
+ break;
+ entry = entry->next;
+ }
+ dir = entry->entry.dir;
+ }
+}
+
static void operate_on_dir(struct dir_info *dir);
static void operate_on_file(struct file_info *file);
/* Randomly select something to do with a directory entry */
static void operate_on_entry(struct dir_entry_info *entry)
{
- /* If shrinking, 1 time in 50, remove a directory */
if (entry->type == 'd') {
+ /* If shrinking, 1 time in 50, remove a directory */
if (shrink && tests_random_no(50) == 0) {
dir_remove(entry->entry.dir);
return;
}
operate_on_dir(entry->entry.dir);
}
- /* If shrinking, 1 time in 10, remove a file */
if (entry->type == 'f') {
+ /* If shrinking, 1 time in 10, remove a file */
if (shrink && tests_random_no(10) == 0) {
- while (entry->entry.file->fds)
- file_close(entry->entry.file->fds);
file_delete(entry->entry.file);
return;
}
+ /* If not growing, 1 time in 10, unlink a file with links > 1 */
+ if (!grow && entry->entry.file->link_count > 1 &&
+ tests_random_no(10) == 0) {
+ file_unlink_file(entry->entry.file);
+ return;
+ }
operate_on_file(entry->entry.file);
}
}
@@ -1265,6 +1389,7 @@ static void operate_on_dir(struct dir_info *dir)
{
size_t r;
struct dir_entry_info *entry;
+ struct file_info *file;
r = tests_random_no(12);
if (r == 0 && grow)
@@ -1273,6 +1398,9 @@ static void operate_on_dir(struct dir_info *dir)
else if (r == 1 && grow)
/* When growing, 1 time in 12 create a directory */
dir_new(dir, make_name(dir));
+ else if (r == 2 && grow && (file = pick_file()) != NULL)
+ /* When growing, 1 time in 12 create a hard link */
+ link_new(dir, make_name(dir), file);
else {
/* Otherwise randomly select an entry to operate on */
r = tests_random_no(dir->number_of_entries);
@@ -1312,8 +1440,18 @@ static void operate_on_file(struct file_info *file)
/* Mostly just write */
file_write_file(file);
/* Once in a while check it too */
- if (tests_random_no(100) == 1)
- file_check(file, -1);
+ if (tests_random_no(100) == 1) {
+ int fd = -2;
+
+ if (file->links)
+ fd = -1;
+ else if (file->fds)
+ fd = file->fds->fd;
+ if (fd != -2) {
+ check_run_no += 1;
+ file_check(file, fd);
+ }
+ }
}
/* Randomly select something to do with an open file */
@@ -1491,6 +1629,7 @@ void integck(void)
}
/* Check everything */
+ check_run_no += 1;
dir_check(top_dir);
check_deleted_files();
@@ -1504,6 +1643,7 @@ void integck(void)
}
/* Check everything */
+ check_run_no += 1;
dir_check(top_dir);
check_deleted_files();
}