summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/fs-tests/Makefile8
-rwxr-xr-xtests/fs-tests/help_all.sh27
-rw-r--r--tests/fs-tests/integrity/Makefile22
-rw-r--r--tests/fs-tests/integrity/integck.c1395
-rw-r--r--tests/fs-tests/lib/Makefile18
-rw-r--r--tests/fs-tests/lib/tests.c1063
-rw-r--r--tests/fs-tests/lib/tests.h185
-rwxr-xr-xtests/fs-tests/run_all.sh49
-rw-r--r--tests/fs-tests/simple/Makefile26
-rw-r--r--tests/fs-tests/simple/ftrunc.c111
-rw-r--r--tests/fs-tests/simple/test_1.c150
-rw-r--r--tests/fs-tests/simple/test_2.c201
-rw-r--r--tests/fs-tests/stress/Makefile11
-rw-r--r--tests/fs-tests/stress/atoms/Makefile40
-rw-r--r--tests/fs-tests/stress/atoms/fwrite00.c209
-rw-r--r--tests/fs-tests/stress/atoms/gcd_hupper.c259
-rw-r--r--tests/fs-tests/stress/atoms/pdfrun.c143
-rw-r--r--tests/fs-tests/stress/atoms/rmdir00.c133
-rw-r--r--tests/fs-tests/stress/atoms/rndrm00.c157
-rw-r--r--tests/fs-tests/stress/atoms/rndrm99.c431
-rw-r--r--tests/fs-tests/stress/atoms/rndwrite00.c201
-rw-r--r--tests/fs-tests/stress/atoms/stress_1.c109
-rw-r--r--tests/fs-tests/stress/atoms/stress_2.c120
-rw-r--r--tests/fs-tests/stress/atoms/stress_3.c122
-rwxr-xr-xtests/fs-tests/stress/stress00.sh53
-rwxr-xr-xtests/fs-tests/stress/stress01.sh40
-rw-r--r--tests/fs-tests/utils/Makefile19
-rw-r--r--tests/fs-tests/utils/free_space.c56
-rw-r--r--tests/fs-tests/utils/fstest_monitor.c281
29 files changed, 5639 insertions, 0 deletions
diff --git a/tests/fs-tests/Makefile b/tests/fs-tests/Makefile
new file mode 100644
index 0000000..d188796
--- /dev/null
+++ b/tests/fs-tests/Makefile
@@ -0,0 +1,8 @@
+
+SUBDIRS = lib simple stress integrity utils
+
+all clean tests: $(SUBDIRS)
+
+.PHONY: $(SUBDIRS)
+$(SUBDIRS):
+ $(MAKE) -C $@ $(MAKECMDGOALS)
diff --git a/tests/fs-tests/help_all.sh b/tests/fs-tests/help_all.sh
new file mode 100755
index 0000000..34b890b
--- /dev/null
+++ b/tests/fs-tests/help_all.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+echo -------------------------------------------------------------------------------
+./simple/test_1 -h
+echo -------------------------------------------------------------------------------
+./simple/test_2 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/stress_1 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/stress_2 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/stress_3 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/fwrite00 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/gcd_hupper -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/pdfrun -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/rmdir00 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/rndrm00 -h
+echo -------------------------------------------------------------------------------
+./stress/atoms/rndwrite00 -h
+echo -------------------------------------------------------------------------------
+./integrity/integck -h
+echo -------------------------------------------------------------------------------
diff --git a/tests/fs-tests/integrity/Makefile b/tests/fs-tests/integrity/Makefile
new file mode 100644
index 0000000..a35f4d0
--- /dev/null
+++ b/tests/fs-tests/integrity/Makefile
@@ -0,0 +1,22 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib
+
+LDFLAGS := $(LDFLAGS)
+
+TARGETS = integck
+
+all: $(TARGETS)
+
+$(TARGETS): ../lib/tests.o
+
+../lib/tests.o: ../lib/tests.h
+
+clean:
+ rm -f *.o $(TARGETS)
+
+tests: all
+ ./integck
diff --git a/tests/fs-tests/integrity/integck.c b/tests/fs-tests/integrity/integck.c
new file mode 100644
index 0000000..23ad9bc
--- /dev/null
+++ b/tests/fs-tests/integrity/integck.c
@@ -0,0 +1,1395 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+
+#include "tests.h"
+
+/* Structures to store data written to the test file system,
+ so that we can check whether the file system is correct. */
+
+struct write_info /* Record of random data written into a file */
+{
+ struct write_info *next;
+ off_t offset; /* Where in the file the data was written */
+ size_t size; /* Number of bytes written */
+ unsigned random_seed; /* Seed for rand() to create random data */
+ off_t random_offset; /* Call rand() this number of times first */
+};
+
+struct file_info /* Each file has one of these */
+{
+ char *name;
+ struct dir_info *parent; /* Parent directory */
+ struct write_info *writes; /* Record all writes to the file */
+ struct fd_info *fds; /* All open file descriptors for this file */
+ off_t length;
+ int deleted; /* File has been deleted but is still open */
+ int no_space_error; /* File has incurred a ENOSPC error */
+};
+
+struct dir_info /* Each directory has one of these */
+{
+ char *name;
+ struct dir_info *parent; /* Parent directory or null
+ for our top directory */
+ unsigned number_of_entries;
+ struct dir_entry_info *first;
+};
+
+struct dir_entry_info /* Each entry in a directory has one of these */
+{
+ struct dir_entry_info *next;
+ char type; /* f => file, d=> dir */
+ int checked; /* Temporary flag used when checking */
+ union entry_
+ {
+ struct file_info *file;
+ struct dir_info *dir;
+ } entry;
+};
+
+struct fd_info /* We keep a number of files open */
+{
+ struct fd_info *next;
+ struct file_info *file;
+ int fd;
+};
+
+struct open_file_info /* We keep a list of open files */
+{
+ struct open_file_info *next;
+ struct fd_info *fdi;
+};
+
+static struct dir_info *top_dir = NULL; /* Our top directory */
+
+static struct open_file_info *open_files = NULL; /* We keep a list of
+ open files */
+static size_t open_files_count = 0;
+
+static int grow = 1; /* Should we try to grow files and directories */
+static int shrink = 0; /* Should we try to shrink files and directories */
+static int full = 0; /* Flag that the file system is full */
+static uint64_t operation_count = 0; /* Number of operations used to fill
+ up the file system */
+static uint64_t initial_free_space = 0; /* Free space on file system when
+ test starts */
+static unsigned log10_initial_free_space = 0; /* log10 of initial_free_space */
+
+static char *copy_string(const char *s)
+{
+ char *str;
+
+ if (!s)
+ return NULL;
+ str = (char *) malloc(strlen(s) + 1);
+ CHECK(str != NULL);
+ strcpy(str, s);
+ return str;
+}
+
+static char *cat_strings(const char *a, const char *b)
+{
+ char *str;
+ size_t sz;
+
+ if (a && !b)
+ return copy_string(a);
+ if (b && !a)
+ return copy_string(b);
+ if (!a && !b)
+ return NULL;
+ sz = strlen(a) + strlen(b) + 1;
+ str = (char *) malloc(sz);
+ CHECK(str != NULL);
+ strcpy(str, a);
+ strcat(str, b);
+ return str;
+}
+
+static char *cat_paths(const char *a, const char *b)
+{
+ char *str;
+ size_t sz;
+ int as, bs;
+ size_t na, nb;
+
+ if (a && !b)
+ return copy_string(a);
+ if (b && !a)
+ return copy_string(b);
+ if (!a && !b)
+ return NULL;
+
+ as = 0;
+ bs = 0;
+ na = strlen(a);
+ nb = strlen(b);
+ if (na && a[na - 1] == '/')
+ as = 1;
+ if (nb && b[0] == '/')
+ bs = 1;
+ if ((as && !bs) || (!as && bs))
+ return cat_strings(a, b);
+ if (as && bs)
+ return cat_strings(a, b + 1);
+
+ sz = na + nb + 2;
+ str = (char *) malloc(sz);
+ CHECK(str != NULL);
+ strcpy(str, a);
+ strcat(str, "/");
+ strcat(str, b);
+ return str;
+}
+
+static char *dir_path(struct dir_info *parent, const char *name)
+{
+ char *parent_path;
+ char *path;
+
+ if (!parent)
+ return cat_paths(tests_file_system_mount_dir, name);
+ parent_path = dir_path(parent->parent, parent->name);
+ path = cat_paths(parent_path, name);
+ free(parent_path);
+ 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;
+ size_t sz;
+
+ sz = sizeof(struct open_file_info);
+ ofi = (struct open_file_info *) malloc(sz);
+ CHECK(ofi != NULL);
+ memset(ofi, 0, sz);
+ ofi->next = open_files;
+ ofi->fdi = fdi;
+ open_files = ofi;
+ open_files_count += 1;
+}
+
+static void open_file_remove(struct fd_info *fdi)
+{
+ struct open_file_info *ofi;
+ struct open_file_info **prev;
+
+ prev = &open_files;
+ for (ofi = open_files; ofi; ofi = ofi->next) {
+ if (ofi->fdi == fdi) {
+ *prev = ofi->next;
+ free(ofi);
+ open_files_count -= 1;
+ return;
+ }
+ prev = &ofi->next;
+ }
+ CHECK(0); /* We are trying to remove something that is not there */
+}
+
+static struct fd_info *fd_new(struct file_info *file, int fd)
+{
+ struct fd_info *fdi;
+ size_t sz;
+
+ sz = sizeof(struct fd_info);
+ fdi = (struct fd_info *) malloc(sz);
+ CHECK(fdi != NULL);
+ memset(fdi, 0, sz);
+ fdi->next = file->fds;
+ fdi->file = file;
+ fdi->fd = fd;
+ file->fds = fdi;
+ open_file_add(fdi);
+ return fdi;
+}
+
+static struct dir_info *dir_new(struct dir_info *parent, const char *name)
+{
+ struct dir_info *dir;
+ size_t sz;
+ char *path;
+
+ path = dir_path(parent, name);
+ if (mkdir(path, 0777) == -1) {
+ CHECK(errno == ENOSPC);
+ full = 1;
+ free(path);
+ return NULL;
+ }
+ free(path);
+
+ sz = sizeof(struct dir_info);
+ dir = (struct dir_info *) malloc(sz);
+ CHECK(dir != NULL);
+ 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;
+ }
+ return dir;
+}
+
+static void file_delete(struct file_info *file);
+
+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) {
+ struct dir_entry_info *entry;
+
+ entry = dir->first;
+ if (entry->type == 'd')
+ dir_remove(entry->entry.dir);
+ else if (entry->type == 'f')
+ file_delete(entry->entry.file);
+ 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 directory itself */
+ path = dir_path(dir->parent, dir->name);
+ CHECK(rmdir(path) != -1);
+}
+
+static struct file_info *file_new(struct dir_info *parent, const char *name)
+{
+ struct file_info *file = NULL;
+ char *path;
+ mode_t mode;
+ int fd;
+ size_t sz;
+ struct dir_entry_info *entry;
+
+ CHECK(parent != NULL);
+
+ path = dir_path(parent, name);
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ fd = open(path, O_CREAT | O_EXCL | O_RDWR, mode);
+ if (fd == -1) {
+ CHECK(errno == ENOSPC);
+ free(path);
+ full = 1;
+ return NULL;
+ }
+ free(path);
+
+ sz = sizeof(struct file_info);
+ file = (struct file_info *) malloc(sz);
+ CHECK(file != NULL);
+ memset(file, 0, sz);
+ file->name = copy_string(name);
+ file->parent = parent;
+
+ fd_new(file, fd);
+
+ entry = dir_entry_new();
+ entry->type = 'f';
+ entry->entry.file = file;
+ entry->next = parent->first;
+ parent->first = entry;
+ parent->number_of_entries += 1;
+
+ return file;
+}
+
+static void file_delete(struct file_info *file)
+{
+ char *path;
+ struct dir_entry_info *entry;
+ struct dir_entry_info **prev;
+ int found;
+
+ /* 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;
+ }
+ CHECK(found); /* Check the file is in the parent directory */
+
+ /* Delete the file */
+ path = dir_path(file->parent, file->name);
+ CHECK(unlink(path) != -1);
+ free(path);
+
+ /* Free struct file_info if file is not open */
+ if (!file->fds) {
+ struct write_info *w, *next;
+
+ free(file->name);
+ w = file->writes;
+ while (w) {
+ next = w->next;
+ free(w);
+ w = next;
+ }
+ free(file);
+ } else
+ file->deleted = 1;
+}
+
+static void file_info_display(struct file_info *file)
+{
+ 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, " Length: %u\n", (unsigned) file->length);
+ fprintf(stderr, " File was open: %s\n",
+ (file->fds == NULL) ? "false" : "true");
+ fprintf(stderr, " File was deleted: %s\n",
+ (file->deleted == 0) ? "false" : "true");
+ fprintf(stderr, " File was out of space: %s\n",
+ (file->no_space_error == 0) ? "false" : "true");
+ fprintf(stderr, " Write Info:\n");
+ wcnt = 0;
+ w = file->writes;
+ while (w) {
+ fprintf(stderr, " Offset: %u Size: %u Seed: %u"
+ " R.Off: %u\n",
+ (unsigned) w->offset,
+ (unsigned) w->size,
+ (unsigned) w->random_seed,
+ (unsigned) w->random_offset);
+ wcnt += 1;
+ w = w->next;
+ }
+ fprintf(stderr, " %u writes\n", wcnt);
+ fprintf(stderr, " ============================================\n");
+}
+
+static struct fd_info *file_open(struct file_info *file)
+{
+ int fd;
+ char *path;
+
+ path = dir_path(file->parent, file->name);
+ fd = open(path, O_RDWR);
+ CHECK(fd != -1);
+ free(path);
+ return fd_new(file, fd);
+}
+
+#define BUFFER_SIZE 32768
+
+static size_t file_write_data( struct file_info *file,
+ int fd,
+ off_t offset,
+ size_t size,
+ unsigned seed)
+{
+ size_t remains, actual, block;
+ ssize_t written;
+ char buf[BUFFER_SIZE];
+
+ srand(seed);
+ CHECK(lseek(fd, offset, SEEK_SET) != (off_t) -1);
+ remains = size;
+ actual = 0;
+ written = BUFFER_SIZE;
+ while (remains) {
+ /* Fill up buffer with random data */
+ if (written < BUFFER_SIZE)
+ memmove(buf, buf + written, BUFFER_SIZE - written);
+ else
+ written = 0;
+ for (; written < BUFFER_SIZE; ++written)
+ buf[written] = rand();
+ /* Write a block of data */
+ if (remains > BUFFER_SIZE)
+ block = BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written < 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ full = 1;
+ file->no_space_error = 1;
+ break;
+ }
+ remains -= written;
+ actual += written;
+ }
+ return actual;
+}
+
+static void file_write_info(struct file_info *file,
+ off_t offset,
+ size_t size,
+ unsigned seed)
+{
+ struct write_info *new_write, *w, **prev, *tmp;
+ int inserted;
+ size_t sz;
+ off_t end, chg;
+
+ /* Create struct write_info */
+ sz = sizeof(struct write_info);
+ new_write = (struct write_info *) malloc(sz);
+ CHECK(new_write != NULL);
+ memset(new_write, 0, sz);
+ new_write->offset = offset;
+ new_write->size = size;
+ new_write->random_seed = seed;
+
+ /* Insert it into file->writes */
+ inserted = 0;
+ end = offset + size;
+ w = file->writes;
+ prev = &file->writes;
+ while (w) {
+ if (w->offset >= end) {
+ /* w comes after new_write, so insert before it */
+ new_write->next = w;
+ *prev = new_write;
+ inserted = 1;
+ break;
+ }
+ /* w does not come after new_write */
+ if (w->offset + w->size > offset) {
+ /* w overlaps new_write */
+ if (w->offset < offset) {
+ /* w begins before new_write begins */
+ if (w->offset + w->size <= end)
+ /* w ends before new_write ends */
+ w->size = offset - w->offset;
+ else {
+ /* w ends after new_write ends */
+ /* Split w */
+ tmp = (struct write_info *) malloc(sz);
+ CHECK(tmp != NULL);
+ *tmp = *w;
+ chg = end - tmp->offset;
+ tmp->offset += chg;
+ tmp->random_offset += chg;
+ tmp->size -= chg;
+ w->size = offset - w->offset;
+ /* Insert new struct write_info */
+ w->next = new_write;
+ new_write->next = tmp;
+ inserted = 1;
+ break;
+ }
+ } else {
+ /* w begins after new_write begins */
+ if (w->offset + w->size <= end) {
+ /* w is completely overlapped,
+ so remove it */
+ *prev = w->next;
+ tmp = w;
+ w = w->next;
+ free(tmp);
+ continue;
+ }
+ /* w ends after new_write ends */
+ chg = end - w->offset;
+ w->offset += chg;
+ w->random_offset += chg;
+ w->size -= chg;
+ continue;
+ }
+ }
+ prev = &w->next;
+ w = w->next;
+ }
+ if (!inserted)
+ *prev = new_write;
+ /* Update file length */
+ if (end > file->length)
+ file->length = end;
+}
+
+/* Randomly select offset and and size to write in a file */
+static void get_offset_and_size(struct file_info *file,
+ off_t *offset,
+ size_t *size)
+{
+ size_t r, n;
+
+ r = tests_random_no(100);
+ if (r == 0 && grow)
+ /* 1 time in 100, when growing, write off the end of the file */
+ *offset = file->length + tests_random_no(10000000);
+ else if (r < 4)
+ /* 3 (or 4) times in 100, write at the beginning of file */
+ *offset = 0;
+ else if (r < 52 || !grow)
+ /* 48 times in 100, write into the file */
+ *offset = tests_random_no(file->length);
+ else
+ /* 48 times in 100, write at the end of the file */
+ *offset = file->length;
+ /* Distribute the size logarithmically */
+ if (tests_random_no(1000) == 0)
+ r = tests_random_no(log10_initial_free_space + 2);
+ else
+ r = tests_random_no(log10_initial_free_space);
+ n = 1;
+ while (r--)
+ n *= 10;
+ *size = tests_random_no(n);
+ if (!grow && *offset + *size > file->length)
+ *size = file->length - *offset;
+ if (*size == 0)
+ *size = 1;
+}
+
+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)
+{
+ if (ftruncate(fd, new_length) == -1) {
+ CHECK(errno = ENOSPC);
+ file->no_space_error = 1;
+ /* Delete errored files */
+ if (!file->deleted) {
+ struct fd_info *fdi;
+
+ fdi = file->fds;
+ while (fdi) {
+ file_close(fdi);
+ fdi = file->fds;
+ }
+ file_delete(file);
+ }
+ return 0;
+ }
+ return 1;
+}
+
+static void file_write(struct file_info *file, int fd)
+{
+ off_t offset;
+ size_t size, actual;
+ unsigned seed;
+ int truncate = 0;
+
+ get_offset_and_size(file, &offset, &size);
+ seed = tests_random_no(10000000);
+ actual = file_write_data(file, fd, offset, size, seed);
+
+ if (offset + actual <= file->length && shrink)
+ /* 1 time in 100, when shrinking
+ truncate after the write */
+ if (tests_random_no(100) == 0)
+ truncate = 1;
+
+ if (actual != 0)
+ file_write_info(file, offset, actual, seed);
+
+ /* Delete errored files */
+ if (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);
+ }
+ return;
+ }
+
+ if (truncate) {
+ size_t new_length = offset + actual;
+ if (file_ftruncate(file, fd, new_length))
+ file_truncate_info(file, new_length);
+ }
+}
+
+static void file_write_file(struct file_info *file)
+{
+ int fd;
+ char *path;
+
+ path = dir_path(file->parent, file->name);
+ fd = open(path, O_WRONLY);
+ CHECK(fd != -1);
+ file_write(file, fd);
+ CHECK(close(fd) != -1);
+ free(path);
+}
+
+static void file_truncate_info(struct file_info *file, size_t new_length)
+{
+ struct write_info *w, **prev, *tmp;
+
+ /* Remove / truncate file->writes */
+ w = file->writes;
+ prev = &file->writes;
+ while (w) {
+ if (w->offset >= new_length) {
+ /* w comes after eof, so remove it */
+ *prev = w->next;
+ tmp = w;
+ w = w->next;
+ free(tmp);
+ continue;
+ }
+ if (w->offset + w->size > new_length)
+ w->size = new_length - w->offset;
+ prev = &w->next;
+ w = w->next;
+ }
+ /* Update file length */
+ file->length = new_length;
+}
+
+static void file_truncate(struct file_info *file, int fd)
+{
+ size_t new_length;
+
+ new_length = tests_random_no(file->length);
+
+ if (file_ftruncate(file, fd, new_length))
+ file_truncate_info(file, new_length);
+}
+
+static void file_truncate_file(struct file_info *file)
+{
+ int fd;
+ char *path;
+
+ path = dir_path(file->parent, file->name);
+ fd = open(path, O_WRONLY);
+ CHECK(fd != -1);
+ file_truncate(file, fd);
+ CHECK(close(fd) != -1);
+ free(path);
+}
+
+static void file_close(struct fd_info *fdi)
+{
+ struct file_info *file;
+ struct fd_info *fdp;
+ struct fd_info **prev;
+
+ /* Close file */
+ CHECK(close(fdi->fd) != -1);
+ /* Remove struct fd_info */
+ open_file_remove(fdi);
+ file = fdi->file;
+ prev = &file->fds;
+ for (fdp = file->fds; fdp; fdp = fdp->next) {
+ if (fdp == fdi) {
+ *prev = fdi->next;
+ free(fdi);
+ if (file->deleted && !file->fds) {
+ /* Closing deleted file */
+ struct write_info *w, *next;
+
+ w = file->writes;
+ while (w) {
+ next = w->next;
+ free(w);
+ w = next;
+ }
+ free(file->name);
+ free(file);
+ }
+ return;
+ }
+ prev = &fdp->next;
+ }
+ CHECK(0); /* Didn't find struct fd_info */
+}
+
+static void file_rewrite_data(int fd, struct write_info *w, char *buf)
+{
+ size_t remains, block;
+ ssize_t written;
+ off_t r;
+
+ srand(w->random_seed);
+ for (r = 0; r < w->random_offset; ++r)
+ rand();
+ CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t) -1);
+ remains = w->size;
+ written = BUFFER_SIZE;
+ while (remains) {
+ /* Fill up buffer with random data */
+ if (written < BUFFER_SIZE)
+ memmove(buf, buf + written, BUFFER_SIZE - written);
+ else
+ written = 0;
+ for (; written < BUFFER_SIZE; ++written)
+ buf[written] = rand();
+ /* Write a block of data */
+ if (remains > BUFFER_SIZE)
+ block = BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ CHECK(written == block);
+ remains -= written;
+ }
+}
+
+static void save_file(int fd, struct file_info *file)
+{
+ int w_fd;
+ struct write_info *w;
+ char buf[BUFFER_SIZE];
+ char name[256];
+
+ /* Open file to save contents to */
+ strcpy(name, "/tmp/");
+ strcat(name, file->name);
+ strcat(name, ".integ.sav.read");
+ fprintf(stderr, "Saving %s\n", name);
+ w_fd = open(name, O_CREAT | O_WRONLY, 0777);
+ CHECK(w_fd != -1);
+
+ /* Start at the beginning */
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+
+ for (;;) {
+ ssize_t r = read(fd, buf, BUFFER_SIZE);
+ CHECK(r != -1);
+ if (!r)
+ break;
+ CHECK(write(w_fd, buf, r) == r);
+ }
+ CHECK(close(w_fd) != -1);
+
+ /* Open file to save contents to */
+ strcpy(name, "/tmp/");
+ strcat(name, file->name);
+ strcat(name, ".integ.sav.written");
+ fprintf(stderr, "Saving %s\n", name);
+ w_fd = open(name, O_CREAT | O_WRONLY, 0777);
+ CHECK(w_fd != -1);
+
+ for (w = file->writes; w; w = w->next)
+ file_rewrite_data(w_fd, w, buf);
+
+ CHECK(close(w_fd) != -1);
+}
+
+static void file_check_hole( struct file_info *file,
+ int fd, off_t offset,
+ size_t size)
+{
+ size_t remains, block, i;
+ char buf[BUFFER_SIZE];
+
+ CHECK(lseek(fd, offset, SEEK_SET) != (off_t) -1);
+ remains = size;
+ while (remains) {
+ if (remains > BUFFER_SIZE)
+ block = BUFFER_SIZE;
+ else
+ block = remains;
+ CHECK(read(fd, buf, block) == block);
+ for (i = 0; i < block; ++i) {
+ if (buf[i] != 0) {
+ fprintf(stderr, "file_check_hole failed at %u "
+ "checking hole at %u size %u\n",
+ (unsigned) (size - remains + i),
+ (unsigned) offset,
+ (unsigned) size);
+ file_info_display(file);
+ save_file(fd, file);
+ }
+ CHECK(buf[i] == 0);
+ }
+ remains -= block;
+ }
+}
+
+static void file_check_data( struct file_info *file,
+ int fd,
+ struct write_info *w)
+{
+ size_t remains, block, i;
+ off_t r;
+ char buf[BUFFER_SIZE];
+
+ srand(w->random_seed);
+ for (r = 0; r < w->random_offset; ++r)
+ rand();
+ CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t) -1);
+ remains = w->size;
+ while (remains) {
+ if (remains > BUFFER_SIZE)
+ block = BUFFER_SIZE;
+ else
+ block = remains;
+ CHECK(read(fd, buf, block) == block);
+ for (i = 0; i < block; ++i) {
+ char c = (char) rand();
+ if (buf[i] != c) {
+ fprintf(stderr, "file_check_data failed at %u "
+ "checking data at %u size %u\n",
+ (unsigned) (w->size - remains + i),
+ (unsigned) w->offset,
+ (unsigned) w->size);
+ file_info_display(file);
+ save_file(fd, file);
+ }
+ CHECK(buf[i] == c);
+ }
+ remains -= block;
+ }
+}
+
+static void file_check(struct file_info *file, int fd)
+{
+ int open_and_close = 0;
+ char *path = NULL;
+ off_t pos;
+ struct write_info *w;
+
+ /* Do not check files that have errored */
+ if (file->no_space_error)
+ return;
+ if (fd == -1)
+ open_and_close = 1;
+ if (open_and_close) {
+ /* Open file */
+ path = dir_path(file->parent, file->name);
+ fd = open(path, O_RDONLY);
+ CHECK(fd != -1);
+ }
+ /* Check length */
+ pos = lseek(fd, 0, SEEK_END);
+ if (pos != file->length) {
+ fprintf(stderr, "file_check failed checking length "
+ "expected %u actual %u\n",
+ (unsigned) file->length,
+ (unsigned) pos);
+ file_info_display(file);
+ save_file(fd, file);
+ }
+ CHECK(pos == file->length);
+ /* Check each write */
+ pos = 0;
+ for (w = file->writes; w; w = w->next) {
+ if (w->offset > pos)
+ file_check_hole(file, fd, pos, w->offset - pos);
+ file_check_data(file, fd, w);
+ pos = w->offset + w->size;
+ }
+ if (file->length > pos)
+ file_check_hole(file, fd, pos, file->length - pos);
+ if (open_and_close) {
+ 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;
+ }
+}
+
+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));
+}
+
+static void dir_entry_check(struct dir_entry_info **entry_array,
+ size_t number_of_entries,
+ struct dirent *ent)
+{
+ struct dir_entry_info **found;
+ struct dir_entry_info *entry;
+ size_t sz;
+
+ sz = sizeof(struct dir_entry_info *);
+ found = bsearch(ent, entry_array, number_of_entries, sz, search_comp);
+ CHECK(found != NULL);
+ entry = *found;
+ CHECK(!entry->checked);
+ entry->checked = 1;
+}
+
+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));
+}
+
+static void dir_check(struct dir_info *dir)
+{
+ struct dir_entry_info **entry_array, **p;
+ size_t sz, n;
+ struct dir_entry_info *entry;
+ DIR *d;
+ struct dirent *ent;
+ unsigned checked = 0;
+ char *path;
+
+ /* Create an array of entries */
+ sz = sizeof(struct dir_entry_info *);
+ n = dir->number_of_entries;
+ entry_array = (struct dir_entry_info **) malloc(sz * n);
+ CHECK(entry_array != NULL);
+
+ entry = dir->first;
+ p = entry_array;
+ while (entry) {
+ *p++ = entry;
+ entry->checked = 0;
+ entry = entry->next;
+ }
+
+ /* Sort it by name */
+ qsort(entry_array, n, sz, sort_comp);
+
+ /* Go through directory on file system checking entries match */
+ path = dir_path(dir->parent, dir->name);
+ d = opendir(path);
+ CHECK(d != NULL);
+ for (;;) {
+ errno = 0;
+ ent = readdir(d);
+ if (ent) {
+ if (strcmp(".",ent->d_name) != 0 &&
+ strcmp("..",ent->d_name) != 0) {
+ dir_entry_check(entry_array, n, ent);
+ checked += 1;
+ }
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ CHECK(closedir(d) != -1);
+ CHECK(checked == dir->number_of_entries);
+ free(path);
+
+ /* Now check each entry */
+ entry = dir->first;
+ while (entry) {
+ if (entry->type == 'd')
+ dir_check(entry->entry.dir);
+ else if (entry->type == 'f')
+ file_check(entry->entry.file, -1);
+ else
+ CHECK(0);
+ entry = entry->next;
+ }
+
+ free(entry_array);
+}
+
+static void check_deleted_files(void)
+{
+ struct open_file_info *ofi;
+
+ for (ofi = open_files; ofi; ofi = ofi->next)
+ if (ofi->fdi->file->deleted)
+ file_check(ofi->fdi->file, ofi->fdi->fd);
+}
+
+static void close_open_files(void)
+{
+ struct open_file_info *ofi;
+
+ for (ofi = open_files; ofi; ofi = open_files)
+ file_close(ofi->fdi);
+}
+
+static char *make_name(struct dir_info *dir)
+{
+ static char name[256];
+ struct dir_entry_info *entry;
+ int found;
+
+ do {
+ found = 0;
+ sprintf(name, "%u", (unsigned) tests_random_no(1000000));
+ for (entry = dir->first; entry; entry = entry->next) {
+ if (strcmp(dir_entry_name(entry), name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ } while (found);
+ return name;
+}
+
+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 (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 (shrink && tests_random_no(10) == 0) {
+ file_delete(entry->entry.file);
+ return;
+ }
+ operate_on_file(entry->entry.file);
+ }
+}
+
+/* Randomly select something to do with a directory */
+static void operate_on_dir(struct dir_info *dir)
+{
+ size_t r;
+ struct dir_entry_info *entry;
+
+ r = tests_random_no(12);
+ if (r == 0 && grow)
+ /* When growing, 1 time in 12 create a file */
+ file_new(dir, make_name(dir));
+ else if (r == 1 && grow)
+ /* When growing, 1 time in 12 create a directory */
+ dir_new(dir, make_name(dir));
+ else {
+ /* Otherwise randomly select an entry to operate on */
+ r = tests_random_no(dir->number_of_entries);
+ entry = dir->first;
+ while (entry && r) {
+ entry = entry->next;
+ --r;
+ }
+ if (entry)
+ operate_on_entry(entry);
+ }
+}
+
+/* Randomly select something to do with a file */
+static void operate_on_file(struct file_info *file)
+{
+ /* Try to keep at least 10 files open */
+ if (open_files_count < 10) {
+ file_open(file);
+ return;
+ }
+ /* Try to keep about 20 files open */
+ if (open_files_count < 20 && tests_random_no(2) == 0) {
+ file_open(file);
+ return;
+ }
+ /* Try to keep up to 40 files open */
+ if (open_files_count < 40 && tests_random_no(20) == 0) {
+ file_open(file);
+ return;
+ }
+ /* Occasionly truncate */
+ if (shrink && tests_random_no(100) == 0) {
+ file_truncate_file(file);
+ return;
+ }
+ /* Mostly just write */
+ file_write_file(file);
+}
+
+/* Randomly select something to do with an open file */
+static void operate_on_open_file(struct fd_info *fdi)
+{
+ size_t r;
+
+ r = tests_random_no(1000);
+ if (shrink && r == 0)
+ file_truncate(fdi->file, fdi->fd);
+ else if (r < 21)
+ file_close(fdi);
+ else if (shrink && r < 121 && !fdi->file->deleted)
+ file_delete(fdi->file);
+ else
+ file_write(fdi->file, fdi->fd);
+}
+
+/* Select an open file at random */
+static void operate_on_an_open_file(void)
+{
+ size_t r;
+ struct open_file_info *ofi;
+
+ /* Close any open files that have errored */
+ ofi = open_files;
+ while (ofi) {
+ if (ofi->fdi->file->no_space_error) {
+ struct fd_info *fdi;
+
+ fdi = ofi->fdi;
+ ofi = ofi->next;
+ file_close(fdi);
+ } else
+ ofi = ofi->next;
+ }
+ r = tests_random_no(open_files_count);
+ for (ofi = open_files; ofi; ofi = ofi->next, --r)
+ if (!r) {
+ operate_on_open_file(ofi->fdi);
+ return;
+ }
+}
+
+static void do_an_operation(void)
+{
+ /* Half the time operate on already open files */
+ if (tests_random_no(100) < 50)
+ operate_on_dir(top_dir);
+ else
+ operate_on_an_open_file();
+}
+
+static void create_test_data(void)
+{
+ uint64_t i;
+
+ grow = 1;
+ shrink = 0;
+ full = 0;
+ operation_count = 0;
+ while (!full) {
+ do_an_operation();
+ ++operation_count;
+ }
+ grow = 0;
+ shrink = 1;
+ /* Drop to less than 90% full */
+ for (;;) {
+ uint64_t free;
+ uint64_t total;
+ for (i = 0; i < 10; ++i)
+ do_an_operation();
+ free = tests_get_free_space();
+ total = tests_get_total_space();
+ if ((free * 100) / total < 90)
+ break;
+ }
+ grow = 0;
+ shrink = 0;
+ full = 0;
+ for (i = 0; i < operation_count * 2; ++i)
+ do_an_operation();
+}
+
+static void update_test_data(void)
+{
+ uint64_t i;
+
+ grow = 1;
+ shrink = 0;
+ full = 0;
+ while (!full)
+ do_an_operation();
+ grow = 0;
+ shrink = 1;
+ /* Drop to less than 50% full */
+ for (;;) {
+ uint64_t free;
+ uint64_t total;
+ for (i = 0; i < 10; ++i)
+ do_an_operation();
+ free = tests_get_free_space();
+ total = tests_get_total_space();
+ if ((free * 100) / total < 50)
+ break;
+ }
+ grow = 0;
+ shrink = 0;
+ full = 0;
+ for (i = 0; i < operation_count * 2; ++i)
+ do_an_operation();
+}
+
+void integck(void)
+{
+ pid_t pid;
+ int64_t rpt;
+ uint64_t z;
+ char dir_name[256];
+
+ /* Make our top directory */
+ pid = getpid();
+ tests_cat_pid(dir_name, "integck_test_dir_", pid);
+ if (chdir(dir_name) != -1) {
+ /* Remove it if it is already there */
+ tests_clear_dir(".");
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+ }
+ initial_free_space = tests_get_free_space();
+ log10_initial_free_space = 0;
+ for (z = initial_free_space; z >= 10; z /= 10)
+ ++log10_initial_free_space;
+ top_dir = dir_new(NULL, dir_name);
+
+ if (!top_dir)
+ return;
+
+ srand(pid);
+
+ create_test_data();
+
+ if (!tests_fs_is_rootfs()) {
+ close_open_files();
+ tests_remount(); /* Requires root access */
+ }
+
+ /* Check everything */
+ dir_check(top_dir);
+ check_deleted_files();
+
+ for (rpt = 0; tests_repeat_parameter == 0 ||
+ rpt < tests_repeat_parameter; ++rpt) {
+ update_test_data();
+
+ if (!tests_fs_is_rootfs()) {
+ close_open_files();
+ tests_remount(); /* Requires root access */
+ }
+
+ /* Check everything */
+ dir_check(top_dir);
+ check_deleted_files();
+ }
+
+ /* Tidy up by removing everything */
+ close_open_files();
+ tests_clear_dir(dir_name);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *integck_get_title(void)
+{
+ return "Test file system integrity";
+}
+
+/* Description of this test */
+
+const char *integck_get_description(void)
+{
+ return
+ "Create a directory named integck_test_dir_pid " \
+ "where pid is the process id. " \
+ "Randomly create and delete files and directories. " \
+ "Randomly write to and truncate files. " \
+ "Un-mount and re-mount test file " \
+ "system (if it is not the root file system ). " \
+ "Check data. Make more random changes. " \
+ "Un-mount and re-mount again. Check again. " \
+ "Repeat some number of times. "
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, integck_get_title(),
+ integck_get_description(), "n");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ integck();
+ return 0;
+}
diff --git a/tests/fs-tests/lib/Makefile b/tests/fs-tests/lib/Makefile
new file mode 100644
index 0000000..8d57824
--- /dev/null
+++ b/tests/fs-tests/lib/Makefile
@@ -0,0 +1,18 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2
+
+LDFLAGS := $(LDFLAGS)
+
+all: tests.o
+
+tests.o: tests.h
+
+clean:
+ rm -f *.o
+
+tests:
+ echo
diff --git a/tests/fs-tests/lib/tests.c b/tests/fs-tests/lib/tests.c
new file mode 100644
index 0000000..2f513d7
--- /dev/null
+++ b/tests/fs-tests/lib/tests.c
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <linux/jffs2.h>
+#if 0
+#include <linux/jffs3.h>
+#endif
+#include <libgen.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <limits.h>
+#include <sys/mount.h>
+#include <mntent.h>
+#include <time.h>
+
+#include "tests.h"
+
+char *tests_file_system_mount_dir = TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR;
+
+char *tests_file_system_type = TESTS_DEFAULT_FILE_SYSTEM_TYPE;
+
+int tests_ok_to_sync = 0; /* Whether to use fsync */
+
+/* General purpose test parameter to specify some aspect of test size.
+ May be used by different tests in different ways or not at all.
+ Set by the -z or --size option. */
+int64_t tests_size_parameter = 0;
+
+/* General purpose test parameter to specify some aspect of test repetition.
+ May be used by different tests in different ways or not at all.
+ Set by the -n, --repeat options. */
+int64_t tests_repeat_parameter = 0;
+
+/* General purpose test parameter to specify some aspect of test sleeping.
+ May be used by different tests in different ways or not at all.
+ Set by the -p, --sleep options. */
+int64_t tests_sleep_parameter = 0;
+
+/* Program name from argv[0] */
+char *program_name = "unknown";
+
+/* General purpose test parameter to specify a file should be unlinked.
+ May be used by different tests in different ways or not at all. */
+int tests_unlink_flag = 0;
+
+/* General purpose test parameter to specify a file should be closed.
+ May be used by different tests in different ways or not at all. */
+int tests_close_flag = 0;
+
+/* General purpose test parameter to specify a file should be deleted.
+ May be used by different tests in different ways or not at all. */
+int tests_delete_flag = 0;
+
+/* General purpose test parameter to specify a file have a hole.
+ May be used by different tests in different ways or not at all. */
+int tests_hole_flag = 0;
+
+/* Whether it is ok to test on the root file system */
+static int rootok = 0;
+
+/* Function invoked by the CHECK macro */
+void tests_test(int test,const char *msg,const char *file,unsigned line)
+{
+ int eno;
+ time_t t;
+
+ if (test)
+ return;
+ eno = errno;
+ time(&t);
+ fprintf(stderr, "Test failed: %s on %s"
+ "Test failed: %s in %s at line %u\n",
+ program_name, ctime(&t), msg, file, line);
+ if (eno) {
+ fprintf(stderr,"errno = %d\n",eno);
+ fprintf(stderr,"strerror = %s\n",strerror(eno));
+ }
+ exit(1);
+}
+
+static int is_zero(const char *p)
+{
+ for (;*p;++p)
+ if (*p != '0')
+ return 0;
+ return 1;
+}
+
+static void fold(const char *text, int width)
+{
+ int pos, bpos = 0;
+ const char *p;
+ char line[1024];
+
+ if (width > 1023) {
+ printf("%s\n", text);
+ return;
+ }
+ p = text;
+ pos = 0;
+ while (p[pos]) {
+ while (!isspace(p[pos])) {
+ line[pos] = p[pos];
+ if (!p[pos])
+ break;
+ ++pos;
+ if (pos == width) {
+ line[pos] = '\0';
+ printf("%s\n", line);
+ p += pos;
+ pos = 0;
+ }
+ }
+ while (pos < width) {
+ line[pos] = p[pos];
+ if (!p[pos]) {
+ bpos = pos;
+ break;
+ }
+ if (isspace(p[pos]))
+ bpos = pos;
+ ++pos;
+ }
+ line[bpos] = '\0';
+ printf("%s\n", line);
+ p += bpos;
+ pos = 0;
+ while (p[pos] && isspace(p[pos]))
+ ++p;
+ }
+}
+
+/* Handle common program options */
+int tests_get_args(int argc,
+ char *argv[],
+ const char *title,
+ const char *desc,
+ const char *opts)
+{
+ int run_test = 0;
+ int display_help = 0;
+ int display_title = 0;
+ int display_description = 0;
+ int i;
+ char *s;
+
+ program_name = argv[0];
+
+ s = getenv("TEST_FILE_SYSTEM_MOUNT_DIR");
+ if (s)
+ tests_file_system_mount_dir = strdup(s);
+ s = getenv("TEST_FILE_SYSTEM_TYPE");
+ if (s)
+ tests_file_system_type = strdup(s);
+
+ run_test = 1;
+ rootok = 1;
+ for (i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--help") == 0 ||
+ strcmp(argv[i], "-h") == 0)
+ display_help = 1;
+ else if (strcmp(argv[i], "--title") == 0 ||
+ strcmp(argv[i], "-t") == 0)
+ display_title = 1;
+ else if (strcmp(argv[i], "--description") == 0 ||
+ strcmp(argv[i], "-d") == 0)
+ display_description = 1;
+ else if (strcmp(argv[i], "--sync") == 0 ||
+ strcmp(argv[i], "-s") == 0)
+ tests_ok_to_sync = 1;
+ else if (strncmp(argv[i], "--size", 6) == 0 ||
+ strncmp(argv[i], "-z", 2) == 0) {
+ int64_t n;
+ char *p;
+ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1]))
+ ++i;
+ p = argv[i];
+ while (*p && !isdigit(*p))
+ ++p;
+ n = atoll(p);
+ if (n)
+ tests_size_parameter = n;
+ else {
+ int all_zero = 1;
+ for (; all_zero && *p; ++p)
+ if (*p != '0')
+ all_zero = 0;
+ if (all_zero)
+ tests_size_parameter = 0;
+ else
+ display_help = 1;
+ }
+ } else if (strncmp(argv[i], "--repeat", 8) == 0 ||
+ strncmp(argv[i], "-n", 2) == 0) {
+ int64_t n;
+ char *p;
+ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1]))
+ ++i;
+ p = argv[i];
+ while (*p && !isdigit(*p))
+ ++p;
+ n = atoll(p);
+ if (n || is_zero(p))
+ tests_repeat_parameter = n;
+ else
+ display_help = 1;
+ } else if (strncmp(argv[i], "--sleep", 7) == 0 ||
+ strncmp(argv[i], "-p", 2) == 0) {
+ int64_t n;
+ char *p;
+ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1]))
+ ++i;
+ p = argv[i];
+ while (*p && !isdigit(*p))
+ ++p;
+ n = atoll(p);
+ if (n || is_zero(p))
+ tests_sleep_parameter = n;
+ else
+ display_help = 1;
+ } else if (strcmp(argv[i], "--unlink") == 0 ||
+ strcmp(argv[i], "-u") == 0)
+ tests_unlink_flag = 1;
+ else if (strcmp(argv[i], "--hole") == 0 ||
+ strcmp(argv[i], "-o") == 0)
+ tests_hole_flag = 1;
+ else if (strcmp(argv[i], "--close") == 0 ||
+ strcmp(argv[i], "-c") == 0)
+ tests_close_flag = 1;
+ else if (strcmp(argv[i], "--delete") == 0 ||
+ strcmp(argv[i], "-e") == 0)
+ tests_delete_flag = 1;
+ else
+ display_help = 1;
+ }
+
+ if (display_help) {
+ run_test = 0;
+ display_title = 0;
+ display_description = 0;
+ if (!opts)
+ opts = "";
+ printf("File System Test Program\n\n");
+ printf("Test Title: %s\n\n", title);
+ printf("Usage is: %s [ options ]\n",argv[0]);
+ printf(" Options are:\n");
+ printf(" -h, --help ");
+ printf("Display this help\n");
+ printf(" -t, --title ");
+ printf("Display the test title\n");
+ printf(" -d, --description ");
+ printf("Display the test description\n");
+ if (strchr(opts, 's')) {
+ printf(" -s, --sync ");
+ printf("Make use of fsync\n");
+ }
+ if (strchr(opts, 'z')) {
+ printf(" -z, --size ");
+ printf("Set size parameter\n");
+ }
+ if (strchr(opts, 'n')) {
+ printf(" -n, --repeat ");
+ printf("Set repeat parameter\n");
+ }
+ if (strchr(opts, 'p')) {
+ printf(" -p, --sleep ");
+ printf("Set sleep parameter\n");
+ }
+ if (strchr(opts, 'u')) {
+ printf(" -u, --unlink ");
+ printf("Unlink file\n");
+ }
+ if (strchr(opts, 'o')) {
+ printf(" -o, --hole ");
+ printf("Create a hole in a file\n");
+ }
+ if (strchr(opts, 'c')) {
+ printf(" -c, --close ");
+ printf("Close file\n");
+ }
+ if (strchr(opts, 'e')) {
+ printf(" -e, --delete ");
+ printf("Delete file\n");
+ }
+ printf("\nBy default, testing is done in directory ");
+ printf("/mnt/test_file_system. To change this\nuse ");
+ printf("environmental variable ");
+ printf("TEST_FILE_SYSTEM_MOUNT_DIR. By default, ");
+ printf("the file\nsystem tested is jffs2. To change this ");
+ printf("set TEST_FILE_SYSTEM_TYPE.\n\n");
+ printf("Test Description:\n");
+ fold(desc, 80);
+ } else {
+ if (display_title)
+ printf("%s\n", title);
+ if (display_description)
+ printf("%s\n", desc);
+ if (display_title || display_description)
+ if (argc == 2 || (argc == 3 &&
+ display_title &&
+ display_description))
+ run_test = 0;
+ }
+ return run_test;
+}
+
+/* Return the number of files (or directories) in the given directory */
+unsigned tests_count_files_in_dir(const char *dir_name)
+{
+ DIR *dir;
+ struct dirent *entry;
+ unsigned count = 0;
+
+ dir = opendir(dir_name);
+ CHECK(dir != NULL);
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0)
+ ++count;
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ CHECK(closedir(dir) != -1);
+ return count;
+}
+
+/* Change to the file system mount directory, check that it is empty,
+ matches the file system type, and is not the root file system */
+void tests_check_test_file_system(void)
+{
+ struct statfs fs_info;
+ struct stat f_info;
+ struct stat root_f_info;
+
+ if (chdir(tests_file_system_mount_dir) == -1 ||
+ statfs(tests_file_system_mount_dir, &fs_info) == -1) {
+ fprintf(stderr, "Invalid test file system mount directory:"
+ " %s\n", tests_file_system_mount_dir);
+ fprintf(stderr, "Use environment variable "
+ "TEST_FILE_SYSTEM_MOUNT_DIR\n");
+ CHECK(0);
+ }
+ if (strcmp(tests_file_system_type, "jffs2") != 0 &&
+ strcmp(tests_file_system_type, "jffs3") != 0) {
+ fprintf(stderr, "Invalid test file system type:"
+ " %s\n", tests_file_system_type);
+ fprintf(stderr, "Must be jffs2 or jffs3\n");
+ CHECK(0);
+ }
+ if (strcmp(tests_file_system_type, "jffs2") == 0 &&
+ fs_info.f_type != JFFS2_SUPER_MAGIC) {
+ fprintf(stderr, "File system type is not jffs2\n");
+ CHECK(0);
+ }
+#ifdef JFFS3_SUPER_MAGIC
+ if (strcmp(tests_file_system_type, "jffs3") == 0 &&
+ fs_info.f_type != JFFS3_SUPER_MAGIC) {
+ fprintf(stderr, "File system type is not jffs3\n");
+ CHECK(0);
+ }
+#endif
+ /* Check that the test file system is not the root file system */
+ if (!rootok) {
+ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1);
+ CHECK(stat("/", &root_f_info) != -1);
+ CHECK(f_info.st_dev != root_f_info.st_dev);
+ }
+}
+
+/* Get the free space for the file system of the current directory */
+uint64_t tests_get_free_space(void)
+{
+ struct statvfs fs_info;
+
+ CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1);
+ return (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize;
+}
+
+/* Get the total space for the file system of the current directory */
+uint64_t tests_get_total_space(void)
+{
+ struct statvfs fs_info;
+
+ CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1);
+ return (uint64_t) fs_info.f_blocks * (uint64_t) fs_info.f_frsize;
+}
+
+#define WRITE_BUFFER_SIZE 32768
+
+static char write_buffer[WRITE_BUFFER_SIZE];
+
+static void init_write_buffer()
+{
+ static int init = 0;
+
+ if (!init) {
+ int i, d;
+ uint64_t u;
+
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ srand(1);
+ for (i = 0; i < WRITE_BUFFER_SIZE; ++i)
+ write_buffer[i] = rand() / d;
+ init = 1;
+ }
+}
+
+/* Write size random bytes into file descriptor fd at the current position,
+ returning the number of bytes actually written */
+uint64_t tests_fill_file(int fd, uint64_t size)
+{
+ ssize_t written;
+ size_t sz;
+ unsigned start = 0, length;
+ uint64_t remains;
+ uint64_t actual_size = 0;
+
+ init_write_buffer();
+ remains = size;
+ while (remains > 0) {
+ length = WRITE_BUFFER_SIZE - start;
+ if (remains > length)
+ sz = length;
+ else
+ sz = (size_t) remains;
+ written = write(fd, write_buffer + start, sz);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ actual_size += written;
+ if (written == sz)
+ start = 0;
+ else
+ start += written;
+ }
+ tests_maybe_sync(fd);
+ return actual_size;
+}
+
+/* Write size random bytes into file descriptor fd at offset,
+ returning the number of bytes actually written */
+uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size)
+{
+ ssize_t written;
+ size_t sz;
+ unsigned start = 0, length;
+ uint64_t remains;
+ uint64_t actual_size = 0;
+
+ CHECK(lseek(fd, offset, SEEK_SET) == offset);
+
+ init_write_buffer();
+ remains = size;
+ start = offset % WRITE_BUFFER_SIZE;
+ while (remains > 0) {
+ length = WRITE_BUFFER_SIZE - start;
+ if (remains > length)
+ sz = length;
+ else
+ sz = (size_t) remains;
+ written = write(fd, write_buffer + start, sz);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ actual_size += written;
+ if (written == sz)
+ start = 0;
+ else
+ start += written;
+ }
+ tests_maybe_sync(fd);
+ return actual_size;
+}
+
+/* Check that a file written using tests_fill_file() and/or
+ tests_write_filled_file() and/or tests_create_file()
+ contains the expected random data */
+void tests_check_filled_file_fd(int fd)
+{
+ ssize_t sz;
+ char buf[WRITE_BUFFER_SIZE];
+
+ do {
+ sz = read(fd,buf,WRITE_BUFFER_SIZE);
+ CHECK(sz >= 0);
+ CHECK(memcmp(buf,write_buffer,sz) == 0);
+ } while (sz);
+}
+
+/* Check that a file written using tests_fill_file() and/or
+ tests_write_filled_file() and/or tests_create_file()
+ contains the expected random data */
+void tests_check_filled_file(const char *file_name)
+{
+ int fd;
+
+ fd = open(file_name, O_RDONLY);
+ CHECK(fd != -1);
+ tests_check_filled_file_fd(fd);
+ CHECK(close(fd) != -1);
+}
+
+void tests_sync_directory(const char *file_name)
+{
+ char *path;
+ char *dir;
+ int fd;
+
+ if (!tests_ok_to_sync)
+ return;
+
+ path = strdup(file_name);
+ dir = dirname(path);
+ fd = open(dir,O_RDONLY | tests_maybe_sync_flag());
+ CHECK(fd != -1);
+ CHECK(fsync(fd) != -1);
+ CHECK(close(fd) != -1);
+ free(path);
+}
+
+/* Delete a file */
+void tests_delete_file(const char *file_name)
+{
+ CHECK(unlink(file_name) != -1);
+ tests_sync_directory(file_name);
+}
+
+/* Create a file of size file_size */
+uint64_t tests_create_file(const char *file_name, uint64_t file_size)
+{
+ int fd;
+ int flags;
+ mode_t mode;
+ uint64_t actual_size; /* Less than size if the file system is full */
+
+ flags = O_CREAT | O_TRUNC | O_WRONLY | tests_maybe_sync_flag();
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ fd = open(file_name, flags, mode);
+ if (fd == -1 && errno == ENOSPC) {
+ errno = 0;
+ return 0; /* File system full */
+ }
+ CHECK(fd != -1);
+ actual_size = tests_fill_file(fd, file_size);
+ CHECK(close(fd) != -1);
+ if (file_size != 0 && actual_size == 0)
+ tests_delete_file(file_name);
+ else
+ tests_sync_directory(file_name);
+ return actual_size;
+}
+
+/* Calculate: free_space * numerator / denominator */
+uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator)
+{
+ if (denominator == 0)
+ denominator = 1;
+ if (numerator > denominator)
+ numerator = denominator;
+ return numerator * (tests_get_free_space() / denominator);
+}
+
+/* Write size bytes at offset to the file "fragment_n" where n is the
+ file_number and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_write_fragment_file(unsigned file_number,
+ int fd,
+ off_t offset,
+ unsigned size)
+{
+ int i, d;
+ uint64_t u;
+ ssize_t written;
+ off_t pos;
+ char buf[WRITE_BUFFER_SIZE];
+
+ if (size > WRITE_BUFFER_SIZE)
+ size = WRITE_BUFFER_SIZE;
+
+ pos = lseek(fd, 0, SEEK_END);
+ CHECK(pos != (off_t) -1);
+ if (offset > pos)
+ offset = pos;
+
+ pos = lseek(fd, offset, SEEK_SET);
+ CHECK(pos != (off_t) -1);
+ CHECK(pos == offset);
+
+ srand(file_number);
+ while (offset--)
+ rand();
+
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ for (i = 0; i < size; ++i)
+ buf[i] = rand() / d;
+
+ written = write(fd, buf, size);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ written = 0;
+ }
+ tests_maybe_sync(fd);
+ return (unsigned) written;
+}
+
+/* Write size bytes to the end of file descriptor fd using file_number
+ to determine the random data written i.e. seed for random numbers */
+unsigned tests_fill_fragment_file(unsigned file_number, int fd, unsigned size)
+{
+ off_t offset;
+
+ offset = lseek(fd, 0, SEEK_END);
+ CHECK(offset != (off_t) -1);
+
+ return tests_write_fragment_file(file_number, fd, offset, size);
+}
+
+/* Write size bytes to the end of file "fragment_n" where n is the file_number
+ and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_append_to_fragment_file(unsigned file_number,
+ unsigned size,
+ int create)
+{
+ int fd;
+ int flags;
+ mode_t mode;
+ unsigned actual_growth;
+ char file_name[256];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ if (create)
+ flags = O_CREAT | O_EXCL | O_WRONLY | tests_maybe_sync_flag();
+ else
+ flags = O_WRONLY | tests_maybe_sync_flag();
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ fd = open(file_name, flags, mode);
+ if (fd == -1 && errno == ENOSPC) {
+ errno = 0;
+ return 0; /* File system full */
+ }
+ CHECK(fd != -1);
+ actual_growth = tests_fill_fragment_file(file_number, fd, size);
+ CHECK(close(fd) != -1);
+ if (create && !actual_growth)
+ tests_delete_fragment_file(file_number);
+ return actual_growth;
+}
+
+/* Write size bytes at offset to the file "fragment_n" where n is the
+ file_number and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_overwite_fragment_file( unsigned file_number,
+ off_t offset,
+ unsigned size)
+{
+ int fd;
+ unsigned actual_size;
+ char file_name[256];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ fd = open(file_name, O_RDWR | tests_maybe_sync_flag());
+ if (fd == -1 && errno == ENOSPC) {
+ errno = 0;
+ return 0; /* File system full */
+ }
+ CHECK(fd != -1);
+ actual_size = tests_write_fragment_file(file_number,
+ fd, offset, size);
+ CHECK(close(fd) != -1);
+ return actual_size;
+}
+
+/* Delete file "fragment_n" where n is the file_number */
+void tests_delete_fragment_file(unsigned file_number)
+{
+ char file_name[256];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ tests_delete_file(file_name);
+}
+
+/* Check the random data in file "fragment_n" is what is expected */
+void tests_check_fragment_file(unsigned file_number)
+{
+ int fd;
+ ssize_t sz, i;
+ int d;
+ uint64_t u;
+ char file_name[256];
+ char buf[8192];
+
+ sprintf(file_name, "fragment_%u", file_number);
+ fd = open(file_name, O_RDONLY);
+ CHECK(fd != -1);
+ srand(file_number);
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ for (;;) {
+ sz = read(fd, buf, 8192);
+ if (sz == 0)
+ break;
+ CHECK(sz >= 0);
+ for (i = 0; i < sz; ++i)
+ CHECK(buf[i] == (char) (rand() / d));
+ }
+ CHECK(close(fd) != -1);
+}
+
+/* Central point to decide whether to use fsync */
+void tests_maybe_sync(int fd)
+{
+ if (tests_ok_to_sync)
+ CHECK(fsync(fd) != -1);
+}
+
+/* Return O_SYNC if ok to sync otherwise return 0 */
+int tests_maybe_sync_flag(void)
+{
+ if (tests_ok_to_sync)
+ return O_SYNC;
+ return 0;
+}
+
+/* Return random number from 0 to n - 1 */
+size_t tests_random_no(size_t n)
+{
+ uint64_t a, b;
+
+ if (!n)
+ return 0;
+ if (n - 1 <= RAND_MAX) {
+ a = rand();
+ b = RAND_MAX;
+ b += 1;
+ } else {
+ const uint64_t u = 1 + (uint64_t) RAND_MAX;
+ a = rand();
+ a *= u;
+ a += rand();
+ b = u * u;
+ CHECK(n <= b);
+ }
+ if (RAND_MAX <= UINT32_MAX && n <= UINT32_MAX)
+ return a * n / b;
+ else /*if (RAND_MAX <= UINT64_MAX && n <= UINT64_MAX)*/ {
+ uint64_t x, y;
+ if (a < n) {
+ x = a;
+ y = n;
+ } else {
+ x = n;
+ y = a;
+ }
+ return (x * (y / b)) + ((x * (y % b)) / b);
+ }
+}
+
+/* Make a directory empty */
+void tests_clear_dir(const char *dir_name)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char buf[4096];
+
+ dir = opendir(dir_name);
+ CHECK(dir != NULL);
+ CHECK(getcwd(buf, 4096) != NULL);
+ CHECK(chdir(dir_name) != -1);
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0) {
+ if (entry->d_type == DT_DIR) {
+ tests_clear_dir(entry->d_name);
+ CHECK(rmdir(entry->d_name) != -1);
+ } else
+ CHECK(unlink(entry->d_name) != -1);
+ }
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ CHECK(chdir(buf) != -1);
+ CHECK(closedir(dir) != -1);
+}
+
+/* Create an empty sub-directory or small file in the current directory */
+int64_t tests_create_entry(char *return_name)
+{
+ int fd;
+ char name[256];
+
+ for (;;) {
+ sprintf(name, "%u", (unsigned) tests_random_no(10000000));
+ fd = open(name, O_RDONLY);
+ if (fd == -1)
+ break;
+ close(fd);
+ }
+ if (return_name)
+ strcpy(return_name, name);
+ if (tests_random_no(2)) {
+ return tests_create_file(name, tests_random_no(4096));
+ } else {
+ if (mkdir(name, 0777) == -1) {
+ CHECK(errno == ENOSPC);
+ errno = 0;
+ return 0;
+ }
+ return TESTS_EMPTY_DIR_SIZE;
+ }
+}
+
+/* Remove a random file of empty sub-directory from the current directory */
+int64_t tests_remove_entry(void)
+{
+ DIR *dir;
+ struct dirent *entry;
+ unsigned count = 0, pos;
+ int64_t result = 0;
+
+ dir = opendir(".");
+ CHECK(dir != NULL);
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0)
+ ++count;
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ pos = tests_random_no(count);
+ count = 0;
+ rewinddir(dir);
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (!entry) {
+ CHECK(errno == 0);
+ break;
+ }
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0) {
+ if (count == pos) {
+ if (entry->d_type == DT_DIR) {
+ tests_clear_dir(entry->d_name);
+ CHECK(rmdir(entry->d_name) != -1);
+ result = TESTS_EMPTY_DIR_SIZE;
+ } else {
+ struct stat st;
+ CHECK(stat(entry->d_name, &st) != -1);
+ result = st.st_size;
+ CHECK(unlink(entry->d_name) != -1);
+ }
+ }
+ ++count;
+ }
+ }
+ CHECK(closedir(dir) != -1);
+ return result;
+}
+
+/* Read mount information from /proc/mounts or /etc/mtab */
+int tests_get_mount_info(struct mntent *info)
+{
+ FILE *f;
+ struct mntent *entry;
+ int found = 0;
+
+ f = fopen("/proc/mounts", "rb");
+ if (!f)
+ f = fopen("/etc/mtab", "rb");
+ CHECK(f != NULL);
+ while (!found) {
+ entry = getmntent(f);
+ if (entry) {
+ if (strcmp(entry->mnt_dir,
+ tests_file_system_mount_dir) == 0) {
+ found = 1;
+ *info = *entry;
+ }
+ } else
+ break;
+ }
+ CHECK(fclose(f) == 0);
+ return found;
+}
+
+/* Un-mount and re-mount test file system */
+void tests_remount(void)
+{
+ struct mntent mount_info;
+ char *source;
+ char *target;
+ char *filesystemtype;
+ unsigned long mountflags;
+ void *data;
+ char cwd[4096];
+
+ CHECK(tests_get_mount_info(&mount_info));
+
+ if (strcmp(mount_info.mnt_dir,"/") == 0)
+ return;
+
+ CHECK(getcwd(cwd, 4096) != NULL);
+ CHECK(chdir("/") != -1);
+
+ CHECK(umount(tests_file_system_mount_dir) != -1);
+
+ source = mount_info.mnt_fsname;
+ target = tests_file_system_mount_dir;
+ filesystemtype = tests_file_system_type;
+ mountflags = 0;
+ data = NULL;
+
+ CHECK(mount(source, target, filesystemtype, mountflags, data) != -1);
+
+ CHECK(chdir(cwd) != -1);
+}
+
+/* Check whether the test file system is also the root file system */
+int tests_fs_is_rootfs(void)
+{
+ struct stat f_info;
+ struct stat root_f_info;
+
+ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1);
+ CHECK(stat("/", &root_f_info) != -1);
+ if (f_info.st_dev == root_f_info.st_dev)
+ return 1;
+ else
+ return 0;
+}
+
+/* Try to make a directory empty */
+void tests_try_to_clear_dir(const char *dir_name)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char buf[4096];
+
+ dir = opendir(dir_name);
+ if (dir == NULL)
+ return;
+ if (getcwd(buf, 4096) == NULL || chdir(dir_name) == -1) {
+ closedir(dir);
+ return;
+ }
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0) {
+ if (entry->d_type == DT_DIR) {
+ tests_try_to_clear_dir(entry->d_name);
+ rmdir(entry->d_name);
+ } else
+ unlink(entry->d_name);
+ }
+ } else {
+ CHECK(errno == 0);
+ break;
+ }
+ }
+ chdir(buf);
+ closedir(dir);
+}
+
+/* Check whether the test file system is also the current file system */
+int tests_fs_is_currfs(void)
+{
+ struct stat f_info;
+ struct stat curr_f_info;
+
+ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1);
+ CHECK(stat(".", &curr_f_info) != -1);
+ if (f_info.st_dev == curr_f_info.st_dev)
+ return 1;
+ else
+ return 0;
+}
+
+#define PID_BUF_SIZE 64
+
+/* Concatenate a pid to a string in a signal safe way */
+void tests_cat_pid(char *buf, const char *name, pid_t pid)
+{
+ char *p;
+ unsigned x;
+ const char digits[] = "0123456789";
+ char pid_buf[PID_BUF_SIZE];
+
+ x = (unsigned) pid;
+ p = pid_buf + PID_BUF_SIZE;
+ *--p = '\0';
+ if (x)
+ while (x) {
+ *--p = digits[x % 10];
+ x /= 10;
+ }
+ else
+ *--p = '0';
+ buf[0] = '\0';
+ strcat(buf, name);
+ strcat(buf, p);
+}
diff --git a/tests/fs-tests/lib/tests.h b/tests/fs-tests/lib/tests.h
new file mode 100644
index 0000000..36ca310
--- /dev/null
+++ b/tests/fs-tests/lib/tests.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#ifndef included_tests_tests_h__
+#define included_tests_tests_h__
+
+#include <stdint.h>
+
+/* Main macro for testing */
+#define CHECK(x) tests_test((x),__func__,__FILE__,__LINE__)
+
+/* The default directory in which tests are conducted */
+#define TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR "/mnt/test_file_system"
+
+/* The default file system type to test */
+#define TESTS_DEFAULT_FILE_SYSTEM_TYPE "jffs2"
+
+/* Estimated size of an empty directory */
+#define TESTS_EMPTY_DIR_SIZE 128
+
+/* Function invoked by the CHECK macro */
+void tests_test(int test,const char *msg,const char *file,unsigned line);
+
+/* Handle common program options */
+int tests_get_args(int argc,
+ char *argv[],
+ const char *title,
+ const char *desc,
+ const char *opts);
+
+/* Return the number of files (or directories) in the given directory */
+unsigned tests_count_files_in_dir(const char *dir_name);
+
+/* Change to the file system mount directory, check that it is empty,
+ matches the file system type, and is not the root file system */
+void tests_check_test_file_system(void);
+
+/* Get the free space for the file system of the current directory */
+uint64_t tests_get_free_space(void);
+
+/* Get the total space for the file system of the current directory */
+uint64_t tests_get_total_space(void);
+
+/* Write size random bytes into file descriptor fd at the current position,
+ returning the number of bytes actually written */
+uint64_t tests_fill_file(int fd, uint64_t size);
+
+/* Write size random bytes into file descriptor fd at offset,
+ returning the number of bytes actually written */
+uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size);
+
+/* Check that a file written using tests_fill_file() and/or
+ tests_write_filled_file() and/or tests_create_file()
+ contains the expected random data */
+void tests_check_filled_file_fd(int fd);
+
+/* Check that a file written using tests_fill_file() and/or
+ tests_write_filled_file() and/or tests_create_file()
+ contains the expected random data */
+void tests_check_filled_file(const char *file_name);
+
+/* Delete a file */
+void tests_delete_file(const char *file_name);
+
+/* Create a file of size file_size */
+uint64_t tests_create_file(const char *file_name, uint64_t file_size);
+
+/* Calculate: free_space * numerator / denominator */
+uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator);
+
+/* Write size bytes to the end of file descriptor fd using file_number
+ to determine the random data written i.e. seed for random numbers */
+unsigned tests_fill_fragment_file(unsigned file_number,
+ int fd,
+ unsigned size);
+
+/* Write size bytes to the end of file "fragment_n" where n is the file_number
+ and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_append_to_fragment_file(unsigned file_number,
+ unsigned size,
+ int create);
+
+/* Write size bytes at offset to the file "fragment_n" where n is the
+ file_number and file_number also determines the random data written
+ i.e. seed for random numbers */
+unsigned tests_overwite_fragment_file( unsigned file_number,
+ off_t offset,
+ unsigned size);
+
+/* Delete file "fragment_n" where n is the file_number */
+void tests_delete_fragment_file(unsigned file_number);
+
+/* Check the random data in file "fragment_n" is what is expected */
+void tests_check_fragment_file(unsigned file_number);
+
+/* Central point to decide whether to use fsync */
+void tests_maybe_sync(int fd);
+
+/* Return O_SYNC if ok to sync otherwise return 0 */
+int tests_maybe_sync_flag(void);
+
+/* Return random number from 0 to n - 1 */
+size_t tests_random_no(size_t n);
+
+/* Make a directory empty */
+void tests_clear_dir(const char *dir_name);
+
+/* Create an empty sub-directory or small file in the current directory */
+int64_t tests_create_entry(char *return_name);
+
+/* Remove a random file of empty sub-directory from the current directory */
+int64_t tests_remove_entry(void);
+
+/* Un-mount and re-mount test file system */
+void tests_remount(void);
+
+/* Check whether the test file system is also the root file system */
+int tests_fs_is_rootfs(void);
+
+/* Try to make a directory empty */
+void tests_try_to_clear_dir(const char *dir_name);
+
+/* Check whether the test file system is also the current file system */
+int tests_fs_is_currfs(void);
+
+/* Concatenate a pid to a string in a signal safe way */
+void tests_cat_pid(char *buf, const char *name, pid_t pid);
+
+extern char *tests_file_system_mount_dir;
+
+extern char *tests_file_system_type;
+
+/* General purpose test parameter to specify some aspect of test size.
+ May be used by different tests in different ways.
+ Set by the -z, --size options. */
+extern int64_t tests_size_parameter;
+
+/* General purpose test parameter to specify some aspect of test repetition.
+ May be used by different tests in different ways.
+ Set by the -n, --repeat options. */
+extern int64_t tests_repeat_parameter;
+
+/* General purpose test parameter to specify some aspect of test sleeping.
+ May be used by different tests in different ways.
+ Set by the -p, --sleep options. */
+extern int64_t tests_sleep_parameter;
+
+/* General purpose test parameter to specify a file should be unlinked.
+ May be used by different tests in different ways or not at all. */
+extern int tests_unlink_flag;
+
+/* General purpose test parameter to specify a file should be closed.
+ May be used by different tests in different ways or not at all. */
+extern int tests_close_flag;
+
+/* General purpose test parameter to specify a file should be deleted.
+ May be used by different tests in different ways or not at all. */
+extern int tests_delete_flag;
+
+/* General purpose test parameter to specify a file have a hole.
+ May be used by different tests in different ways or not at all. */
+extern int tests_hole_flag;
+
+/* Program name from argv[0] */
+extern char *program_name;
+
+#endif
diff --git a/tests/fs-tests/run_all.sh b/tests/fs-tests/run_all.sh
new file mode 100755
index 0000000..e79993a
--- /dev/null
+++ b/tests/fs-tests/run_all.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR
+if test -z "$TEST_DIR";
+then
+ TEST_DIR="/mnt/test_file_system"
+fi
+
+rm -rf ${TEST_DIR}/*
+
+./simple/test_1 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./simple/test_2 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./integrity/integck || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/rndrm00 -z0 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/rmdir00 -z0 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/stress_1 -z10000000 -e || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/stress_2 -z10000000 || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+./stress/atoms/stress_3 -z1000000000 -e || exit 1
+
+rm -rf ${TEST_DIR}/*
+
+cd stress || exit 1
+
+./stress00.sh 3600 || exit 1
+
+./stress01.sh 3600 || exit 1
+
+cd .. || exit 1
diff --git a/tests/fs-tests/simple/Makefile b/tests/fs-tests/simple/Makefile
new file mode 100644
index 0000000..07ddf65
--- /dev/null
+++ b/tests/fs-tests/simple/Makefile
@@ -0,0 +1,26 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib
+
+LDFLAGS := $(LDFLAGS)
+
+TARGETS = test_1 \
+ test_2 \
+ ftrunc
+
+all: $(TARGETS)
+
+$(TARGETS): ../lib/tests.o
+
+../lib/tests.o: ../lib/tests.h
+
+clean:
+ rm -f *.o $(TARGETS)
+
+tests: all
+ ./test_1 --sync
+ ./test_2 --sync
+ ./ftrunc
diff --git a/tests/fs-tests/simple/ftrunc.c b/tests/fs-tests/simple/ftrunc.c
new file mode 100644
index 0000000..86edf65
--- /dev/null
+++ b/tests/fs-tests/simple/ftrunc.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void ftrunc(void)
+{
+ int fd, i;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ size_t block;
+ char *file_name;
+ off_t actual;
+ char buf[WRITE_BUFFER_SIZE];
+
+ file_name = "ftrunc_test_file";
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ pid = getpid();
+ srand(pid);
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ remains = tests_size_parameter;
+ actual = 0;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ actual += written;
+ }
+ CHECK(ftruncate(fd, (actual ? actual - 1 : actual)) != -1);
+ CHECK(close(fd) != -1);
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *ftrunc_get_title(void)
+{
+ return "Truncate a large test file";
+}
+
+/* Description of this test */
+
+const char *ftrunc_get_description(void)
+{
+ return
+ "Create a file named ftrunc_test_file. " \
+ "Truncate the file to reduce its length by 1. " \
+ "Then remove the truncated file. "
+ "The size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, ftrunc_get_title(),
+ ftrunc_get_description(), "z");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ ftrunc();
+ return 0;
+}
diff --git a/tests/fs-tests/simple/test_1.c b/tests/fs-tests/simple/test_1.c
new file mode 100644
index 0000000..69eafe4
--- /dev/null
+++ b/tests/fs-tests/simple/test_1.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "tests.h"
+
+void test_1(void)
+{
+ int fd;
+ pid_t pid;
+ uint64_t i;
+ uint64_t block;
+ uint64_t actual_size;
+ char name[256];
+ char old[16];
+ char buf[16];
+ off_t old_len;
+ char dir_name[256];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "test_1_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Create a file that fills half the free space on the file system */
+ tests_create_file("big_file", tests_get_big_file_size(1,2));
+ CHECK(tests_count_files_in_dir(".") == 1);
+ fd = open("big_file", O_RDWR | tests_maybe_sync_flag());
+ CHECK(fd != -1);
+ CHECK(read(fd, old, 5) == 5);
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+ CHECK(write(fd,"start", 5) == 5);
+ CHECK(lseek(fd,0,SEEK_END) != (off_t) -1);
+ CHECK(write(fd, "end", 3) == 3);
+ tests_maybe_sync(fd);
+ /* Delete the file while it is still open */
+ tests_delete_file("big_file");
+ CHECK(tests_count_files_in_dir(".") == 0);
+ /* Create files to file up the file system */
+ for (block = 1000000, i = 1; ; block /= 10) {
+ while (i != 0) {
+ sprintf(name, "fill_up_%llu", i);
+ actual_size = tests_create_file(name, block);
+ if (actual_size != 0)
+ ++i;
+ if (actual_size != block)
+ break;
+ }
+ if (block == 1)
+ break;
+ }
+ /* Check the big file */
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+ CHECK(read(fd, buf, 5) == 5);
+ CHECK(strncmp(buf, "start", 5) == 0);
+ CHECK(lseek(fd, -3, SEEK_END) != (off_t) -1);
+ CHECK(read(fd, buf, 3) == 3);
+ CHECK(strncmp(buf, "end", 3) == 0);
+ /* Check the other files and delete them */
+ i -= 1;
+ CHECK(tests_count_files_in_dir(".") == i);
+ for (; i > 0; --i) {
+ sprintf(name, "fill_up_%llu", i);
+ tests_check_filled_file(name);
+ tests_delete_file(name);
+ }
+ CHECK(tests_count_files_in_dir(".") == 0);
+ /* Check the big file again */
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+ CHECK(read(fd, buf, 5) == 5);
+ CHECK(strncmp(buf, "start", 5) == 0);
+ CHECK(lseek(fd, -3, SEEK_END) != (off_t) -1);
+ CHECK(read(fd, buf, 3) == 3);
+ CHECK(strncmp(buf, "end", 3) == 0);
+ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1);
+ CHECK(write(fd,old, 5) == 5);
+ old_len = lseek(fd, -3, SEEK_END);
+ CHECK(old_len != (off_t) -1);
+ CHECK(ftruncate(fd,old_len) != -1);
+ tests_check_filled_file_fd(fd);
+ /* Close the big file*/
+ CHECK(close(fd) != -1);
+ CHECK(tests_count_files_in_dir(".") == 0);
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *test_1_get_title(void)
+{
+ return "Fill file system while holding deleted big file descriptor";
+}
+
+/* Description of this test */
+
+const char *test_1_get_description(void)
+{
+ return
+ "Create a directory named test_1_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "create a big file (approx. half the file system in size), " \
+ "open it, and unlink it. " \
+ "Create many smaller files until the file system is full. " \
+ "Check the big file is ok. " \
+ "Delete all the smaller files. " \
+ "Check the big file again. " \
+ "Finally delete the big file and directory.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, test_1_get_title(),
+ test_1_get_description(), "s");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ test_1();
+ return 0;
+}
diff --git a/tests/fs-tests/simple/test_2.c b/tests/fs-tests/simple/test_2.c
new file mode 100644
index 0000000..2094460
--- /dev/null
+++ b/tests/fs-tests/simple/test_2.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "tests.h"
+
+void test_2(void)
+{
+ pid_t pid;
+ int create, full;
+ unsigned i, number_of_files;
+ unsigned growth;
+ unsigned size;
+ uint64_t big_file_size;
+ int fd;
+ off_t offset;
+ char dir_name[256];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "test_2_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Create up to 1000 files appending 400 bytes at a time to each file */
+ /* until the file system is full.*/
+ create = 1;
+ full = 0;
+ number_of_files = 1000;
+ while (!full) {
+ for (i = 0; i < number_of_files; ++i) {
+ growth = tests_append_to_fragment_file(i, 400, create);
+ if (!growth) {
+ full = 1;
+ if (create)
+ number_of_files = i;
+ break;
+ }
+ }
+ create = 0;
+ }
+ /* Check the files */
+ CHECK(tests_count_files_in_dir(".") == number_of_files);
+ for (i = 0; i < number_of_files; ++i)
+ tests_check_fragment_file(i);
+ /* Delete half of them */
+ for (i = 1; i < number_of_files; i += 2)
+ tests_delete_fragment_file(i);
+ /* Check them again */
+ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2);
+ /* Create a big file that fills two thirds of the free space */
+ big_file_size = tests_get_big_file_size(2,3);
+ /* Check the big file */
+ tests_create_file("big_file", big_file_size);
+ CHECK(tests_count_files_in_dir(".") == 1 + (number_of_files + 1) / 2);
+ tests_check_filled_file("big_file");
+ /* Open the big file */
+ fd = open("big_file",O_RDWR | tests_maybe_sync_flag());
+ CHECK(fd != -1);
+ /* Delete the big file while it is still open */
+ tests_delete_file("big_file");
+ /* Check the big file again */
+ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2);
+ tests_check_filled_file_fd(fd);
+
+ /* Write parts of the files and check them */
+
+ offset = 100; /* Offset to write at, in the small files */
+ size = 200; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ offset = 300; /* Offset to write at, in the small files */
+ size = 400; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ offset = 110; /* Offset to write at, in the small files */
+ size = 10; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ offset = 10; /* Offset to write at, in the small files */
+ size = 1000; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ offset = 0; /* Offset to write at, in the small files */
+ size = 100000; /* Number of bytes to write at the offset */
+
+ for (i = 0; i < number_of_files; i += 2)
+ tests_overwite_fragment_file(i, offset, size);
+ /* Rewrite the big file entirely */
+ tests_write_filled_file(fd, 0, big_file_size);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ tests_check_filled_file_fd(fd);
+
+ /* Close the big file*/
+ CHECK(close(fd) != -1);
+ /* Check the small files */
+ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2);
+ for (i = 0; i < number_of_files; i += 2)
+ tests_check_fragment_file(i);
+ /* Delete the small files */
+ for (i = 0; i < number_of_files; i += 2)
+ tests_delete_fragment_file(i);
+ CHECK(tests_count_files_in_dir(".") == 0);
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *test_2_get_title(void)
+{
+ return "Repeated write many small files and one big deleted file";
+}
+
+/* Description of this test */
+
+const char *test_2_get_description(void)
+{
+ return
+ "Create a directory named test_2_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "create about 1000 files. Append 400 bytes to each until " \
+ "the file system is full. Then delete half of them. Then " \
+ "create a big file that uses about 2/3 of the remaining free " \
+ "space. Get a file descriptor for the big file, and delete " \
+ "the big file. Then repeatedly write to the small files " \
+ "and the big file. " \
+ "Finally delete the big file and directory.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, test_2_get_title(),
+ test_2_get_description(), "s");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ test_2();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/Makefile b/tests/fs-tests/stress/Makefile
new file mode 100644
index 0000000..c24ff3f
--- /dev/null
+++ b/tests/fs-tests/stress/Makefile
@@ -0,0 +1,11 @@
+
+SUBDIRS = atoms
+
+all tests: $(SUBDIRS)
+
+clean: $(SUBDIRS)
+ rm -rf run_pdf_test_file_*
+
+.PHONY: $(SUBDIRS)
+$(SUBDIRS):
+ $(MAKE) -C $@ $(MAKECMDGOALS)
diff --git a/tests/fs-tests/stress/atoms/Makefile b/tests/fs-tests/stress/atoms/Makefile
new file mode 100644
index 0000000..9fbfd39
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/Makefile
@@ -0,0 +1,40 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2 -I../../lib
+
+LDFLAGS := $(LDFLAGS)
+
+TARGETS = stress_1 \
+ stress_2 \
+ stress_3 \
+ pdfrun \
+ rndwrite00 \
+ fwrite00 \
+ rmdir00 \
+ rndrm00 \
+ rndrm99 \
+ gcd_hupper
+
+all: $(TARGETS)
+
+$(TARGETS): ../../lib/tests.o
+
+../lib/tests.o: ../../lib/tests.h
+
+clean:
+ rm -f *.o $(TARGETS) run_pdf_test_file
+
+tests: all
+ ./stress_1 -e
+ ./stress_2
+ ./stress_3 -e
+ ./pdfrun
+ ./rndwrite00 -e
+ ./fwrite00
+ ./rmdir00
+ ./rndrm00
+ ./rndrm99
+ ./gcd_hupper
diff --git a/tests/fs-tests/stress/atoms/fwrite00.c b/tests/fs-tests/stress/atoms/fwrite00.c
new file mode 100644
index 0000000..2f40b3d
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/fwrite00.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+#define HOLE_BLOCK_SIZE 10000000
+
+void filestress00(void)
+{
+ int fd, i, deleted;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ int64_t repeat;
+ size_t block;
+ char file_name[256];
+ char buf[WRITE_BUFFER_SIZE];
+
+ fd = -1;
+ deleted = 1;
+ pid = getpid();
+ tests_cat_pid(file_name, "filestress00_test_file_", pid);
+ srand(pid);
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ /* Open the file */
+ if (fd == -1) {
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ deleted = 0;
+ if (tests_unlink_flag) {
+ CHECK(unlink(file_name) != -1);
+ deleted = 1;
+ }
+ }
+ /* Get a different set of random data */
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ if (tests_hole_flag) {
+ /* Make a hole */
+ CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1);
+ written = write(fd, "!", 1);
+ if (written <= 0) {
+ /* File system full */
+ CHECK(errno == ENOSPC);
+ errno = 0;
+ }
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ /* Write at set points into the hole */
+ remains = tests_size_parameter;
+ while (remains > HOLE_BLOCK_SIZE) {
+ CHECK(lseek(fd, HOLE_BLOCK_SIZE,
+ SEEK_CUR) != -1);
+ written = write(fd, "!", 1);
+ remains -= HOLE_BLOCK_SIZE;
+ if (written <= 0) {
+ /* File system full */
+ CHECK(errno == ENOSPC);
+ errno = 0;
+ break;
+ }
+ }
+ } else {
+ /* Write data into the file */
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ /* File system full */
+ CHECK(errno == ENOSPC);
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ }
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Close if tests_close_flag */
+ if (tests_close_flag) {
+ CHECK(close(fd) != -1);
+ fd = -1;
+ }
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ /* Delete if tests_delete flag */
+ if (!deleted && tests_delete_flag) {
+ CHECK(unlink(file_name) != -1);
+ deleted = 1;
+ }
+ }
+ CHECK(close(fd) != -1);
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ /* Tidy up */
+ if (!deleted)
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *filestress00_get_title(void)
+{
+ return "File stress test 00";
+}
+
+/* Description of this test */
+
+const char *filestress00_get_description(void)
+{
+ return
+ "Create a file named filestress00_test_file_pid, where " \
+ "pid is the process id. If the unlink option " \
+ "(-u or --unlink) is specified, " \
+ "unlink the file while holding the open file descriptor. " \
+ "If the hole option (-o or --hole) is specified, " \
+ "write a single character at the end of the file, creating a " \
+ "hole. " \
+ "Write a single character in the hole every 10 million " \
+ "bytes. " \
+ "If the hole option is not specified, then the file is " \
+ "filled with random data. " \
+ "If the close option (-c or --close) is specified the file " \
+ "is closed. " \
+ "If a sleep value is specified, the process sleeps. " \
+ "If the delete option (-e or --delete) is specified, then " \
+ "the file is deleted. " \
+ "If a repeat count is specified, then the task repeats " \
+ "that number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The file size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. " \
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, filestress00_get_title(),
+ filestress00_get_description(), "znpuoce");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ filestress00();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/gcd_hupper.c b/tests/fs-tests/stress/atoms/gcd_hupper.c
new file mode 100644
index 0000000..31c175d
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/gcd_hupper.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <mntent.h>
+#include <signal.h>
+
+#include "tests.h"
+
+#define MAX_NAME_SIZE 1024
+
+struct gcd_pid
+{
+ struct gcd_pid *next;
+ int pid;
+ char *name;
+ int mtd_index;
+};
+
+struct gcd_pid *gcd_pid_list = NULL;
+
+int add_gcd_pid(const char *number)
+{
+ int pid;
+ FILE *f;
+ char file_name[MAX_NAME_SIZE];
+ char program_name[MAX_NAME_SIZE];
+
+ pid = atoi(number);
+ if (pid <= 0)
+ return 0;
+ snprintf(file_name, MAX_NAME_SIZE, "/proc/%s/stat", number);
+ f = fopen(file_name, "r");
+ if (f == NULL)
+ return 0;
+ if (fscanf(f, "%d %s", &pid, program_name) != 2) {
+ fclose(f);
+ return 0;
+ }
+ if (strncmp(program_name, "(jffs2_gcd_mtd", 14) != 0)
+ pid = 0;
+ if (pid) {
+ size_t sz;
+ struct gcd_pid *g;
+
+ sz = sizeof(struct gcd_pid);
+ g = (struct gcd_pid *) malloc(sz);
+ g->pid = pid;
+ g->name = (char *) malloc(strlen(program_name) + 1);
+ if (g->name)
+ strcpy(g->name, program_name);
+ else
+ exit(1);
+ g->mtd_index = atoi(program_name + 14);
+ g->next = gcd_pid_list;
+ gcd_pid_list = g;
+ }
+ fclose(f);
+ return pid;
+}
+
+int get_pid_list(void)
+{
+ DIR *dir;
+ struct dirent *entry;
+
+ dir = opendir("/proc");
+ if (dir == NULL)
+ return 1;
+ for (;;) {
+ entry = readdir(dir);
+ if (entry) {
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0)
+ add_gcd_pid(entry->d_name);
+ } else
+ break;
+ }
+ closedir(dir);
+ return 0;
+}
+
+int parse_index_number(const char *name)
+{
+ const char *p, *q;
+ int all_zero;
+ int index;
+
+ p = name;
+ while (*p && !isdigit(*p))
+ ++p;
+ if (!*p)
+ return -1;
+ all_zero = 1;
+ for (q = p; *q; ++q) {
+ if (!isdigit(*q))
+ return -1;
+ if (*q != '0')
+ all_zero = 0;
+ }
+ if (all_zero)
+ return 0;
+ index = atoi(p);
+ if (index <= 0)
+ return -1;
+ return index;
+}
+
+int get_mtd_index(void)
+{
+ FILE *f;
+ struct mntent *entry;
+ struct stat f_info;
+ struct stat curr_f_info;
+ int found;
+ int mtd_index = -1;
+
+ if (stat(tests_file_system_mount_dir, &f_info) == -1)
+ return -1;
+ f = fopen("/proc/mounts", "rb");
+ if (!f)
+ f = fopen("/etc/mtab", "rb");
+ if (f == NULL)
+ return -1;
+ found = 0;
+ for (;;) {
+ entry = getmntent(f);
+ if (!entry)
+ break;
+ if (stat(entry->mnt_dir, &curr_f_info) == -1)
+ continue;
+ if (f_info.st_dev == curr_f_info.st_dev) {
+ int i;
+
+ i = parse_index_number(entry->mnt_fsname);
+ if (i != -1) {
+ if (found && i != mtd_index)
+ return -1;
+ found = 1;
+ mtd_index = i;
+ }
+ }
+ }
+ fclose(f);
+ return mtd_index;
+}
+
+int get_gcd_pid()
+{
+ struct gcd_pid *g;
+ int mtd_index;
+
+ if (get_pid_list())
+ return 0;
+ mtd_index = get_mtd_index();
+ if (mtd_index == -1)
+ return 0;
+ for (g = gcd_pid_list; g; g = g->next)
+ if (g->mtd_index == mtd_index)
+ return g->pid;
+ return 0;
+}
+
+void gcd_hupper(void)
+{
+ int64_t repeat;
+ int pid;
+
+ pid = get_gcd_pid();
+ CHECK(pid != 0);
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ CHECK(kill(pid, SIGHUP) != -1);
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ }
+}
+
+/* Title of this test */
+
+const char *gcd_hupper_get_title(void)
+{
+ return "Send HUP signals to gcd";
+}
+
+/* Description of this test */
+
+const char *gcd_hupper_get_description(void)
+{
+ return
+ "Determine the PID of the gcd process. " \
+ "Send it SIGHUP (may require root privileges). " \
+ "If a sleep value is specified, the process sleeps. " \
+ "If a repeat count is specified, then the task repeats " \
+ "that number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 1. "
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 1;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, gcd_hupper_get_title(),
+ gcd_hupper_get_description(), "np");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ gcd_hupper();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/pdfrun.c b/tests/fs-tests/stress/atoms/pdfrun.c
new file mode 100644
index 0000000..3536580
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/pdfrun.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void adjust_size(void)
+{
+ char dummy[1024];
+ unsigned long total_memory;
+ FILE *f;
+
+ total_memory = 0;
+ f = fopen("/proc/meminfo", "r");
+ fscanf(f, "%s %lu", dummy, &total_memory);
+ fclose(f);
+ if (total_memory > 0 && tests_size_parameter > total_memory / 2)
+ tests_size_parameter = total_memory / 2;
+}
+
+void run_pdf(void)
+{
+ int fd, i;
+ pid_t pid;
+ int64_t repeat;
+ ssize_t written;
+ int64_t remains;
+ size_t block;
+ char file_name[256];
+ char buf[WRITE_BUFFER_SIZE];
+
+ if (tests_fs_is_currfs())
+ return;
+ adjust_size();
+ pid = getpid();
+ tests_cat_pid(file_name, "run_pdf_test_file_", pid);
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ pid = getpid();
+ srand(pid);
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ CHECK(lseek(fd, 0, SEEK_SET) == 0);
+ }
+ CHECK(close(fd) != -1);
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *run_pdf_get_title(void)
+{
+ return "Create / overwrite a large file in the current directory";
+}
+
+/* Description of this test */
+
+const char *run_pdf_get_description(void)
+{
+ return
+ "Create a file named run_pdf_test_file_pid, " \
+ "where pid is the process id. The file is created " \
+ "in the current directory, " \
+ "if the current directory is NOT on the test " \
+ "file system, otherwise no action is taken. " \
+ "If a repeat count is specified, then the task repeats " \
+ "that number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "The size is adjusted so that it is not more than " \
+ "half the size of total memory.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, run_pdf_get_title(),
+ run_pdf_get_description(), "zn");
+ if (!run_test)
+ return 1;
+ /* Do the actual test */
+ run_pdf();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/rmdir00.c b/tests/fs-tests/stress/atoms/rmdir00.c
new file mode 100644
index 0000000..c1d0729
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/rmdir00.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+void rmdir00(void)
+{
+ int64_t repeat;
+ int64_t size, this_size;
+ pid_t pid;
+ char dir_name[256];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "rmdir00_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Repeat loop */
+ repeat = tests_repeat_parameter;
+ size = 0;
+ for (;;) {
+ /* Remove everything in the directory */
+ tests_clear_dir(".");
+ /* Fill with sub-dirs and small files */
+ do {
+ this_size = tests_create_entry(NULL);
+ if (!this_size)
+ break;
+ size += this_size;
+ } while (this_size &&
+ (tests_size_parameter == 0 ||
+ size < tests_size_parameter));
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ }
+ /* Tidy up by removing everything */
+ tests_clear_dir(".");
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *rmdir00_get_title(void)
+{
+ return "Create and remove directories and files";
+}
+
+/* Description of this test */
+
+const char *rmdir00_get_description(void)
+{
+ return
+ "Create a directory named rmdir00_test_dir_pid, where " \
+ "pid is the process id. Within that directory, create " \
+ "a number of sub-directories and small files. " \
+ "The total size of all sub-directories and files " \
+ "is specified by the size parameter. " \
+ "The size parameter is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "A size of zero fills the file system until there is no "
+ "space left. " \
+ "The task repeats, sleeping in between each iteration, " \
+ "and then removing the sub-directories and files created " \
+ "during the last iteration. " \
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. "
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, rmdir00_get_title(),
+ rmdir00_get_description(), "znp");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ rmdir00();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/rndrm00.c b/tests/fs-tests/stress/atoms/rndrm00.c
new file mode 100644
index 0000000..724b1c3
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/rndrm00.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+void rndrm00(void)
+{
+ int64_t repeat;
+ int64_t size, this_size;
+ pid_t pid;
+ char dir_name[256];
+
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "rndrm00_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Repeat loop */
+ repeat = tests_repeat_parameter;
+ size = 0;
+ for (;;) {
+ /* Create and remove sub-dirs and small files, */
+ /* but tending to grow */
+ do {
+ if (tests_random_no(3)) {
+ this_size = tests_create_entry(NULL);
+ if (!this_size)
+ break;
+ size += this_size;
+ } else {
+ this_size = tests_remove_entry();
+ size -= this_size;
+ if (size < 0)
+ size = 0;
+ if (!this_size)
+ this_size = 1;
+ }
+ } while (this_size &&
+ (tests_size_parameter == 0 ||
+ size < tests_size_parameter));
+ /* Create and remove sub-dirs and small files, but */
+ /* but tending to shrink */
+ do {
+ if (!tests_random_no(3)) {
+ this_size = tests_create_entry(NULL);
+ size += this_size;
+ } else {
+ this_size = tests_remove_entry();
+ size -= this_size;
+ if (size < 0)
+ size = 0;
+ }
+ } while ((tests_size_parameter != 0 &&
+ size > tests_size_parameter / 10) ||
+ (tests_size_parameter == 0 && size > 100000));
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ }
+ /* Tidy up by removing everything */
+ tests_clear_dir(".");
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+}
+
+/* Title of this test */
+
+const char *rndrm00_get_title(void)
+{
+ return "Randomly create and remove directories and files";
+}
+
+/* Description of this test */
+
+const char *rndrm00_get_description(void)
+{
+ return
+ "Create a directory named rndrm00_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "randomly create and remove " \
+ "a number of sub-directories and small files, " \
+ "but do more creates than removes. " \
+ "When the total size of all sub-directories and files " \
+ "is greater than the size specified by the size parameter, " \
+ "start to do more removes than creates. " \
+ "The size parameter is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "A size of zero fills the file system until there is no "
+ "space left. " \
+ "The task repeats, sleeping in between each iteration. " \
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. "
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, rndrm00_get_title(),
+ rndrm00_get_description(), "znp");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ rndrm00();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/rndrm99.c b/tests/fs-tests/stress/atoms/rndrm99.c
new file mode 100644
index 0000000..7751839
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/rndrm99.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "tests.h"
+
+uint32_t files_created = 0;
+uint32_t files_removed = 0;
+uint32_t dirs_created = 0;
+uint32_t dirs_removed = 0;
+int64_t *size_ptr = 0;
+
+void display_stats(void)
+{
+ printf( "\nrndrm99 stats:\n"
+ "\tNumber of files created = %u\n"
+ "\tNumber of files deleted = %u\n"
+ "\tNumber of directories created = %u\n"
+ "\tNumber of directories deleted = %u\n"
+ "\tCurrent net size of creates and deletes = %lld\n",
+ (unsigned) files_created,
+ (unsigned) files_removed,
+ (unsigned) dirs_created,
+ (unsigned) dirs_removed,
+ (long long) (size_ptr ? *size_ptr : 0));
+ fflush(stdout);
+}
+
+struct timeval tv_before;
+struct timeval tv_after;
+
+void before(void)
+{
+ CHECK(gettimeofday(&tv_before, NULL) != -1);
+}
+
+void after(const char *msg)
+{
+ time_t diff;
+ CHECK(gettimeofday(&tv_after, NULL) != -1);
+ diff = tv_after.tv_sec - tv_before.tv_sec;
+ if (diff >= 8) {
+ printf("\nrndrm99: the following fn took more than 8 seconds: %s (took %u secs)\n",msg,(unsigned) diff);
+ fflush(stdout);
+ display_stats();
+ }
+}
+
+#define WRITE_BUFFER_SIZE 32768
+
+static char write_buffer[WRITE_BUFFER_SIZE];
+
+static void init_write_buffer()
+{
+ static int init = 0;
+
+ if (!init) {
+ int i, d;
+ uint64_t u;
+
+ u = RAND_MAX;
+ u += 1;
+ u /= 256;
+ d = (int) u;
+ srand(1);
+ for (i = 0; i < WRITE_BUFFER_SIZE; ++i)
+ write_buffer[i] = rand() / d;
+ init = 1;
+ }
+}
+
+/* Write size random bytes into file descriptor fd at the current position,
+ returning the number of bytes actually written */
+uint64_t fill_file(int fd, uint64_t size)
+{
+ ssize_t written;
+ size_t sz;
+ unsigned start = 0, length;
+ uint64_t remains;
+ uint64_t actual_size = 0;
+
+ init_write_buffer();
+ remains = size;
+ while (remains > 0) {
+ length = WRITE_BUFFER_SIZE - start;
+ if (remains > length)
+ sz = length;
+ else
+ sz = (size_t) remains;
+ before();
+ written = write(fd, write_buffer + start, sz);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ after("write");
+ fprintf(stderr,"\nrndrm99: write failed with ENOSPC\n");fflush(stderr);
+ display_stats();
+ break;
+ }
+ after("write");
+ remains -= written;
+ actual_size += written;
+ if ((size_t) written == sz)
+ start = 0;
+ else
+ start += written;
+ }
+ return actual_size;
+}
+
+/* Create a file of size file_size */
+uint64_t create_file(const char *file_name, uint64_t file_size)
+{
+ int fd;
+ int flags;
+ mode_t mode;
+ uint64_t actual_size; /* Less than size if the file system is full */
+
+ flags = O_CREAT | O_TRUNC | O_WRONLY;
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ before();
+ fd = open(file_name, flags, mode);
+ if (fd == -1 && errno == ENOSPC) {
+ errno = 0;
+ after("open");
+ fprintf(stderr,"\nrndrm99: open failed with ENOSPC\n");fflush(stderr);
+ display_stats();
+ return 0; /* File system full */
+ }
+ CHECK(fd != -1);
+ after("open");
+ actual_size = fill_file(fd, file_size);
+ before();
+ CHECK(close(fd) != -1);
+ after("close");
+ if (file_size != 0 && actual_size == 0) {
+ printf("\nrndrm99: unlinking zero size file\n");fflush(stdout);
+ before();
+ CHECK(unlink(file_name) != -1);
+ after("unlink (create_file)");
+ }
+ return actual_size;
+}
+
+/* Create an empty sub-directory or small file in the current directory */
+int64_t create_entry(char *return_name)
+{
+ int fd;
+ char name[256];
+ int64_t res;
+
+ for (;;) {
+ sprintf(name, "%u", (unsigned) tests_random_no(10000000));
+ before();
+ fd = open(name, O_RDONLY);
+ after("open (create_entry)");
+ if (fd == -1)
+ break;
+ before();
+ close(fd);
+ after("close (create_entry)");
+ }
+ if (return_name)
+ strcpy(return_name, name);
+ if (tests_random_no(2)) {
+ res = create_file(name, tests_random_no(4096));
+ if (res > 0)
+ files_created += 1;
+ return res;
+ } else {
+ before();
+ if (mkdir(name, 0777) == -1) {
+ CHECK(errno == ENOSPC);
+ after("mkdir");
+ errno = 0;
+ fprintf(stderr,"\nrndrm99: mkdir failed with ENOSPC\n");fflush(stderr);
+ display_stats();
+ return 0;
+ }
+ after("mkdir");
+ dirs_created += 1;
+ return TESTS_EMPTY_DIR_SIZE;
+ }
+}
+
+/* Remove a random file of empty sub-directory from the current directory */
+int64_t remove_entry(void)
+{
+ DIR *dir;
+ struct dirent *entry;
+ unsigned count = 0, pos;
+ int64_t result = 0;
+
+ before();
+ dir = opendir(".");
+ CHECK(dir != NULL);
+ after("opendir");
+ for (;;) {
+ errno = 0;
+ before();
+ entry = readdir(dir);
+ if (entry) {
+ after("readdir 1");
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0)
+ ++count;
+ } else {
+ CHECK(errno == 0);
+ after("readdir 1");
+ break;
+ }
+ }
+ pos = tests_random_no(count);
+ count = 0;
+ before();
+ rewinddir(dir);
+ after("rewinddir");
+ for (;;) {
+ errno = 0;
+ before();
+ entry = readdir(dir);
+ if (!entry) {
+ CHECK(errno == 0);
+ after("readdir 2");
+ break;
+ }
+ after("readdir 2");
+ if (strcmp(".",entry->d_name) != 0 &&
+ strcmp("..",entry->d_name) != 0) {
+ if (count == pos) {
+ if (entry->d_type == DT_DIR) {
+ before();
+ tests_clear_dir(entry->d_name);
+ after("tests_clear_dir");
+ before();
+ CHECK(rmdir(entry->d_name) != -1);
+ after("rmdir");
+ result = TESTS_EMPTY_DIR_SIZE;
+ dirs_removed += 1;
+ } else {
+ struct stat st;
+ before();
+ CHECK(stat(entry->d_name, &st) != -1);
+ after("stat");
+ result = st.st_size;
+ before();
+ CHECK(unlink(entry->d_name) != -1);
+ after("unlink");
+ files_removed += 1;
+ }
+ }
+ ++count;
+ }
+ }
+ before();
+ CHECK(closedir(dir) != -1);
+ after("closedir");
+ return result;
+}
+
+void rndrm99(void)
+{
+ int64_t repeat, loop_cnt;
+ int64_t size, this_size;
+ pid_t pid;
+ char dir_name[256];
+
+ size_ptr = &size;
+ /* Create a directory to test in */
+ pid = getpid();
+ tests_cat_pid(dir_name, "rndrm99_test_dir_", pid);
+ if (chdir(dir_name) == -1)
+ CHECK(mkdir(dir_name, 0777) != -1);
+ CHECK(chdir(dir_name) != -1);
+ /* Repeat loop */
+ repeat = tests_repeat_parameter;
+ size = 0;
+ for (;;) {
+ /* Create and remove sub-dirs and small files, */
+ /* but tending to grow */
+ printf("\nrndrm99: growing\n");fflush(stdout);
+ loop_cnt = 0;
+ do {
+ if (loop_cnt++ % 2000 == 0)
+ display_stats();
+ if (tests_random_no(3)) {
+ this_size = create_entry(NULL);
+ if (!this_size)
+ break;
+ size += this_size;
+ } else {
+ this_size = remove_entry();
+ size -= this_size;
+ if (size < 0)
+ size = 0;
+ if (!this_size)
+ this_size = 1;
+ }
+ } while (this_size &&
+ (tests_size_parameter == 0 ||
+ size < tests_size_parameter));
+ /* Create and remove sub-dirs and small files, but */
+ /* but tending to shrink */
+ printf("\nrndrm99: shrinking\n");fflush(stdout);
+ loop_cnt = 0;
+ do {
+ if (loop_cnt++ % 2000 == 0)
+ display_stats();
+ if (!tests_random_no(3)) {
+ this_size = create_entry(NULL);
+ size += this_size;
+ } else {
+ this_size = remove_entry();
+ size -= this_size;
+ if (size < 0)
+ size = 0;
+ }
+ } while ((tests_size_parameter != 0 &&
+ size > tests_size_parameter / 10) ||
+ (tests_size_parameter == 0 && size > 100000));
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ printf("\nrndrm99: sleeping\n");fflush(stdout);
+ usleep(s);
+ }
+ }
+ printf("\nrndrm99: tidying\n");fflush(stdout);
+ display_stats();
+ /* Tidy up by removing everything */
+ tests_clear_dir(".");
+ CHECK(chdir("..") != -1);
+ CHECK(rmdir(dir_name) != -1);
+ size_ptr = 0;
+}
+
+/* Title of this test */
+
+const char *rndrm99_get_title(void)
+{
+ return "Randomly create and remove directories and files";
+}
+
+/* Description of this test */
+
+const char *rndrm99_get_description(void)
+{
+ return
+ "Create a directory named rndrm99_test_dir_pid, where " \
+ "pid is the process id. Within that directory, " \
+ "randomly create and remove " \
+ "a number of sub-directories and small files, " \
+ "but do more creates than removes. " \
+ "When the total size of all sub-directories and files " \
+ "is greater than the size specified by the size parameter, " \
+ "start to do more removes than creates. " \
+ "The size parameter is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "A size of zero fills the file system until there is no "
+ "space left. " \
+ "The task repeats, sleeping in between each iteration. " \
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 1. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. "
+ "Sleep is specified in milliseconds.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 1;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, rndrm99_get_title(),
+ rndrm99_get_description(), "znp");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ rndrm99();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/rndwrite00.c b/tests/fs-tests/stress/atoms/rndwrite00.c
new file mode 100644
index 0000000..655d9cc
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/rndwrite00.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "tests.h"
+
+#define BLOCK_SIZE 32768
+#define BUFFER_SIZE 32768
+
+static void check_file(int fd, char *data, size_t length)
+{
+ size_t n, i;
+ char buf[BUFFER_SIZE];
+
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ n = 0;
+ for (;;) {
+ i = read(fd, buf, BUFFER_SIZE);
+ CHECK(i >= 0);
+ if (i == 0)
+ break;
+ CHECK(memcmp(buf, data + n, i) == 0);
+ n += i;
+ }
+ CHECK(n == length);
+}
+
+void rndwrite00(void)
+{
+ int fd;
+ pid_t pid;
+ ssize_t written;
+ size_t remains;
+ size_t block;
+ size_t actual_size;
+ size_t check_every;
+ char *data, *p, *q;
+ off_t offset;
+ size_t size;
+ int64_t repeat;
+ char file_name[256];
+ char buf[4096];
+
+ /* Create file */
+ pid = getpid();
+ tests_cat_pid(file_name, "rndwrite00_test_file_", pid);
+ fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ /* Allocate memory to hold file data */
+ CHECK(tests_size_parameter > 0);
+ CHECK(tests_size_parameter <= SIZE_MAX);
+ data = (char *) malloc(tests_size_parameter);
+ CHECK(data != NULL);
+ /* Fill with random data */
+ srand(pid);
+ for (p = data, q = data + tests_size_parameter; p != q; ++p)
+ *p = rand();
+ /* Write to file */
+ p = data;
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > BLOCK_SIZE)
+ block = BLOCK_SIZE;
+ else
+ block = remains;
+ written = write(fd, p, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ p += written;
+ }
+ actual_size = p - data;
+ /* Repeating bit */
+ repeat = tests_repeat_parameter;
+ check_every = actual_size / 8192;
+ for (;;) {
+ offset = tests_random_no(actual_size);
+ size = tests_random_no(4096);
+ /* Don't change the file size */
+ if (offset + size > actual_size)
+ size = actual_size - offset;
+ if (!size)
+ continue;
+ for (p = buf, q = p + size; p != q; ++p)
+ *p = rand();
+ CHECK(lseek(fd, offset, SEEK_SET) != -1);
+ written = write(fd, buf, size);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ } else
+ memcpy(data + offset, buf, written);
+ /* Break if repeat count exceeded */
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ if (repeat % check_every == 0)
+ check_file(fd, data, actual_size);
+ /* Sleep */
+ if (tests_sleep_parameter > 0) {
+ unsigned us = tests_sleep_parameter * 1000;
+ unsigned rand_divisor = RAND_MAX / us;
+ unsigned s = (us / 2) + (rand() / rand_divisor);
+ usleep(s);
+ }
+ }
+ /* Check and close file */
+ check_file(fd, data, actual_size);
+ CHECK(close(fd) != -1);
+ if (tests_delete_flag)
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *rndwrite00_get_title(void)
+{
+ return "Randomly write a large test file";
+}
+
+/* Description of this test */
+
+const char *rndwrite00_get_description(void)
+{
+ return
+ "Create a file named rndwrite00_test_file_pid, where " \
+ "pid is the process id. " \
+ "The file is filled with random data. " \
+ "The size of the file is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "Then a randomly sized block of random data is written at a " \
+ "random location in the file. "\
+ "The block size is always in the range 1 to 4095. " \
+ "If a sleep value is specified, the process sleeps. " \
+ "The number of writes is given by the repeat count. " \
+ "The repeat count is set by the -n or --repeat option, " \
+ "otherwise it defaults to 10000. " \
+ "A repeat count of zero repeats forever. " \
+ "The sleep value is given by the -p or --sleep option, " \
+ "otherwise it defaults to 0. "
+ "Sleep is specified in milliseconds. " \
+ "Periodically the data in the file is checked with a copy " \
+ "held in memory. " \
+ "If the delete option is specified the file is finally " \
+ "deleted.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 10000;
+
+ /* Set default test sleep */
+ tests_sleep_parameter = 0;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, rndwrite00_get_title(),
+ rndwrite00_get_description(), "zne");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ rndwrite00();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/stress_1.c b/tests/fs-tests/stress/atoms/stress_1.c
new file mode 100644
index 0000000..86f94c2
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/stress_1.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void stress_1(void)
+{
+ int fd, i;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ size_t block;
+ char file_name[256];
+ char buf[WRITE_BUFFER_SIZE];
+
+ pid = getpid();
+ tests_cat_pid(file_name, "stress_1_test_file_", pid);
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ srand(pid);
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ CHECK(close(fd) != -1);
+ if (tests_delete_flag)
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *stress_1_get_title(void)
+{
+ return "Create / overwrite a large file";
+}
+
+/* Description of this test */
+
+const char *stress_1_get_description(void)
+{
+ return
+ "Create a file named stress_1_test_file_pid, " \
+ "where pid is the process id. " \
+ "The size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "The file will be deleted if the delete option " \
+ "is specified. ";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, stress_1_get_title(),
+ stress_1_get_description(), "ze");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ stress_1();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/stress_2.c b/tests/fs-tests/stress/atoms/stress_2.c
new file mode 100644
index 0000000..a9bc31a
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/stress_2.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void stress_2(void)
+{
+ int fd, i;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ int64_t repeat;
+ size_t block;
+ char *file_name;
+ char buf[WRITE_BUFFER_SIZE];
+
+ file_name = "stress_2_test_file";
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ CHECK(unlink(file_name) != -1);
+ pid = getpid();
+ srand(pid);
+ repeat = tests_repeat_parameter;
+ for (;;) {
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ if (tests_repeat_parameter > 0 && --repeat <= 0)
+ break;
+ }
+ CHECK(close(fd) != -1);
+}
+
+/* Title of this test */
+
+const char *stress_2_get_title(void)
+{
+ return "Create / overwrite a large deleted file";
+}
+
+/* Description of this test */
+
+const char *stress_2_get_description(void)
+{
+ return
+ "Create a file named stress_2_test_file. " \
+ "Open it, delete it while holding the open file descriptor, " \
+ "then fill it with random data. " \
+ "Repeated re-write the file some number of times. " \
+ "The repeat count is given by the -n or --repeat option, " \
+ "otherwise it defaults to 10. " \
+ "The file size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Set default test repetition */
+ tests_repeat_parameter = 10;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, stress_2_get_title(),
+ stress_2_get_description(), "zn");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ stress_2();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/atoms/stress_3.c b/tests/fs-tests/stress/atoms/stress_3.c
new file mode 100644
index 0000000..99fb05d
--- /dev/null
+++ b/tests/fs-tests/stress/atoms/stress_3.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "tests.h"
+
+#define WRITE_BUFFER_SIZE 32768
+
+void stress_3(void)
+{
+ int fd, i;
+ pid_t pid;
+ ssize_t written;
+ int64_t remains;
+ size_t block;
+ char file_name[256];
+ char buf[WRITE_BUFFER_SIZE];
+
+ pid = getpid();
+ tests_cat_pid(file_name, "stress_3_test_file_", pid);
+ fd = open(file_name, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+ CHECK(fd != -1);
+ pid = getpid();
+ srand(pid);
+ for (i = 0; i < WRITE_BUFFER_SIZE;++i)
+ buf[i] = rand();
+ CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1);
+ CHECK(write(fd, "!", 1) == 1);
+ CHECK(lseek(fd, 0, SEEK_SET) != -1);
+ remains = tests_size_parameter;
+ while (remains > 0) {
+ if (remains > WRITE_BUFFER_SIZE)
+ block = WRITE_BUFFER_SIZE;
+ else
+ block = remains;
+ written = write(fd, buf, block);
+ if (written <= 0) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ break;
+ }
+ remains -= written;
+ }
+ if (ftruncate(fd, 0) == -1) {
+ CHECK(errno == ENOSPC); /* File system full */
+ errno = 0;
+ }
+ CHECK(close(fd) != -1);
+ if (tests_delete_flag)
+ CHECK(unlink(file_name) != -1);
+}
+
+/* Title of this test */
+
+const char *stress_3_get_title(void)
+{
+ return "Create a file with a large hole and fill it";
+}
+
+/* Description of this test */
+
+const char *stress_3_get_description(void)
+{
+ return
+ "Create a file named stress_3_test_file_pid, " \
+ "where pid is the process id. " \
+ "Write a single character past the end of the file, " \
+ "based on the specified file size, " \
+ "which creates a hole in the file. "
+ "Fill the hole with random data. " \
+ "Then truncate the file length to zero. " \
+ "The size is given by the -z or --size option, " \
+ "otherwise it defaults to 1000000. " \
+ "The file will be deleted if the delete option " \
+ "is specified.";
+}
+
+int main(int argc, char *argv[])
+{
+ int run_test;
+
+ /* Set default test file size */
+ tests_size_parameter = 1000000;
+
+ /* Handle common arguments */
+ run_test = tests_get_args(argc, argv, stress_3_get_title(),
+ stress_3_get_description(), "ze");
+ if (!run_test)
+ return 1;
+ /* Change directory to the file system and check it is ok for testing */
+ tests_check_test_file_system();
+ /* Do the actual test */
+ stress_3();
+ return 0;
+}
diff --git a/tests/fs-tests/stress/stress00.sh b/tests/fs-tests/stress/stress00.sh
new file mode 100755
index 0000000..be95d7c
--- /dev/null
+++ b/tests/fs-tests/stress/stress00.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR
+if test -z "$TEST_DIR";
+then
+ TEST_DIR="/mnt/test_file_system"
+fi
+
+FREESPACE=`../utils/free_space "$TEST_DIR"`
+
+if test -z "$FREESPACE";
+then
+ echo "Failed to determine free space"
+ exit 1
+fi
+
+if test -n "$1";
+then
+ DURATION="-d$1";
+else
+ DURATION="";
+fi
+
+FWRITE00=atoms/fwrite00
+RNDWR=atoms/rndwrite00
+GCHUP=atoms/gcd_hupper
+PDFLUSH=atoms/pdfrun
+FSIZE=$(( $FREESPACE/15 ));
+
+../utils/fstest_monitor $DURATION \
+"$FWRITE00 -z $FSIZE -n0 -p 20" \
+"$FWRITE00 -z $FSIZE -n0 -p 10 -s" \
+"$FWRITE00 -z $FSIZE -n0 -p 20 -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 70 -o" \
+"$FWRITE00 -z $FSIZE -n0 -p 15 -s -o -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 10 -u -c" \
+"$FWRITE00 -z $FSIZE -n0 -p 10 -u -o -c" \
+"$FWRITE00 -z $FSIZE -n0 -p 10 -o -c" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -o -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -s -o -u -c" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -o -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -u" \
+"$FWRITE00 -z $FSIZE -n0 -p 100 -s -o" \
+"$RNDWR -z $FSIZE -n0 -p 10 -e" \
+"$RNDWR -z $FSIZE -n0 -p 100 -e" \
+"$PDFLUSH -z 1073741824 -n0" \
+"$GCHUP -n0"
+
+STATUS=$?
+
+rm -rf ${TEST_DIR}/*
+
+exit $STATUS
diff --git a/tests/fs-tests/stress/stress01.sh b/tests/fs-tests/stress/stress01.sh
new file mode 100755
index 0000000..5913c1c
--- /dev/null
+++ b/tests/fs-tests/stress/stress01.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR
+if test -z "$TEST_DIR";
+then
+ TEST_DIR="/mnt/test_file_system"
+fi
+
+FREESPACE=`../utils/free_space "$TEST_DIR"`
+
+if test -z "$FREESPACE";
+then
+ echo "Failed to determine free space"
+ exit 1
+fi
+
+if test -n "$1";
+then
+ DURATION="-d$1";
+else
+ DURATION="";
+fi
+
+FWRITE00=atoms/fwrite00
+RNDWR=atoms/rndwrite00
+PDFLUSH=atoms/pdfrun
+FSIZE=$(( $FREESPACE/15 ));
+
+../utils/fstest_monitor $DURATION \
+"$FWRITE00 -z $FSIZE -n0 -p 300" \
+"$FWRITE00 -z $FSIZE -n0 -u" \
+"$FWRITE00 -z $FSIZE -n0 -u -c" \
+"$FWRITE00 -z $FSIZE -n0 -s -o" \
+"$RNDWR -z $FSIZE -n0 -e"
+
+STATUS=$?
+
+rm -rf ${TEST_DIR}/*
+
+exit $STATUS
diff --git a/tests/fs-tests/utils/Makefile b/tests/fs-tests/utils/Makefile
new file mode 100644
index 0000000..9fb60b5
--- /dev/null
+++ b/tests/fs-tests/utils/Makefile
@@ -0,0 +1,19 @@
+
+ifeq ($(origin CC),default)
+CC = gcc
+endif
+
+CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib
+
+LDFLAGS := $(LDFLAGS)
+
+TARGETS = fstest_monitor free_space
+
+all: $(TARGETS)
+
+clean:
+ rm -f *.o $(TARGETS)
+
+tests: all
+ ./fstest_monitor
+ ./free_space > /dev/null
diff --git a/tests/fs-tests/utils/free_space.c b/tests/fs-tests/utils/free_space.c
new file mode 100644
index 0000000..88036aa
--- /dev/null
+++ b/tests/fs-tests/utils/free_space.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/statvfs.h>
+
+int main(int argc, char *argv[])
+{
+ char *dir_name = ".";
+ uint64_t free_space;
+ struct statvfs fs_info;
+
+ if (argc > 1) {
+ if (strncmp(argv[1], "--help", 6) == 0 ||
+ strncmp(argv[1], "-h", 2) == 0) {
+ printf( "Usage is: "
+ "free_space [directory]\n"
+ "\n"
+ "Display the free space of the file system "
+ "of the directory given\n"
+ "or the current directory if no "
+ "directory is given.\nThe value output is "
+ "in bytes.\n"
+ );
+ return 1;
+ }
+ dir_name = argv[1];
+ }
+ if (statvfs(dir_name, &fs_info) == -1)
+ return 1;
+
+ free_space = (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize;
+
+ printf("%llu\n", (unsigned long long) free_space);
+
+ return 0;
+}
diff --git a/tests/fs-tests/utils/fstest_monitor.c b/tests/fs-tests/utils/fstest_monitor.c
new file mode 100644
index 0000000..298ee26
--- /dev/null
+++ b/tests/fs-tests/utils/fstest_monitor.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Author: Adrian Hunter
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+struct child_info {
+ struct child_info *next;
+ pid_t pid;
+ int terminated;
+ int killed;
+ int gone;
+};
+
+struct child_info *children = 0;
+
+void kill_children(void)
+{
+ struct child_info *child;
+
+ child = children;
+ while (child) {
+ if (!child->gone) {
+ if (!child->terminated) {
+ child->terminated = 1;
+ kill(child->pid, SIGTERM);
+ } /*else if (!child->killed) {
+ child->killed = 1;
+ kill(child->pid, SIGKILL);
+ }*/
+ }
+ child = child->next;
+ }
+}
+
+void add_child(pid_t child_pid)
+{
+ struct child_info *child;
+ size_t sz;
+
+ sz = sizeof(struct child_info);
+ child = (struct child_info *) malloc(sz);
+ memset(child, 0, sz);
+ child->pid = child_pid;
+ child->next = children;
+ children = child;
+}
+
+void mark_child_gone(pid_t child_pid)
+{
+ struct child_info *child;
+
+ child = children;
+ while (child) {
+ if (child->pid == child_pid) {
+ child->gone = 1;
+ break;
+ }
+ child = child->next;
+ }
+}
+
+int have_children(void)
+{
+ struct child_info *child;
+
+ child = children;
+ while (child) {
+ if (!child->gone)
+ return 1;
+ child = child->next;
+ }
+ return 0;
+}
+
+int parse_command_line(char *cmdline, int *pargc, char ***pargv)
+{
+ char **tmp;
+ char *p, *v, *q;
+ size_t sz;
+ int argc = 0;
+ int state = 0;
+ char *argv[1024];
+
+ if (!cmdline)
+ return 1;
+ q = v = (char *) malloc(strlen(cmdline) + 1024);
+ if (!v)
+ return 1;
+ p = cmdline;
+ for (;;) {
+ char c = *p++;
+ if (!c) {
+ *v++ = 0;
+ break;
+ }
+ switch (state) {
+ case 0: /* Between args */
+ if (isspace(c))
+ break;
+ argv[argc++] = v;
+ if (c == '"') {
+ state = 2;
+ break;
+ } else if (c == '\'') {
+ state = 3;
+ break;
+ }
+ state = 1;
+ case 1: /* Not quoted */
+ if (c == '\\') {
+ if (*p)
+ *v++ = *p;
+ } else if (isspace(c)) {
+ *v++ = 0;
+ state = 0;
+ } else
+ *v++ = c;
+ break;
+ case 2: /* Double quoted */
+ if (c == '\\' && *p == '"') {
+ *v++ = '"';
+ ++p;
+ } else if (c == '"') {
+ *v++ = 0;
+ state = 0;
+ } else
+ *v++ = c;
+ break;
+ case 3: /* Single quoted */
+ if (c == '\'') {
+ *v++ = 0;
+ state = 0;
+ } else
+ *v++ = c;
+ break;
+ }
+ }
+ argv[argc] = 0;
+ sz = sizeof(char *) * (argc + 1);
+ tmp = (char **) malloc(sz);
+ if (!tmp) {
+ free(q);
+ return 1;
+ }
+ if (argc == 0)
+ free(q);
+ memcpy(tmp, argv, sz);
+ *pargc = argc;
+ *pargv = tmp;
+ return 0;
+}
+
+void signal_handler(int signum)
+{
+ kill_children();
+}
+
+int result = 0;
+int alarm_gone_off = 0;
+
+void alarm_handler(int signum)
+{
+ if (!result)
+ alarm_gone_off = 1;
+ kill_children();
+}
+
+int main(int argc, char *argv[], char **env)
+{
+ int p;
+ pid_t child_pid;
+ int status;
+ int duration = 0;
+
+ p = 1;
+ if (argc > 1) {
+ if (strncmp(argv[p], "--help", 6) == 0 ||
+ strncmp(argv[p], "-h", 2) == 0) {
+ printf( "Usage is: "
+ "fstest_monitor options programs...\n"
+ " Options are:\n"
+ " -h, --help "
+ "This help message\n"
+ " -d, --duration arg "
+ "Stop after arg seconds\n"
+ "\n"
+ "Run programs and wait for them."
+ " If duration is specified,\n"
+ "kill all programs"
+ " after that number of seconds have elapsed.\n"
+ "Example: "
+ "fstest_monitor \"/bin/ls -l\" /bin/date\n"
+ );
+ return 1;
+ }
+ if (strncmp(argv[p], "--duration", 10) == 0 ||
+ strncmp(argv[p], "-d", 2) == 0) {
+ char *s;
+ if (p+1 < argc && !isdigit(argv[p][strlen(argv[p])-1]))
+ ++p;
+ s = argv[p];
+ while (*s && !isdigit(*s))
+ ++s;
+ duration = atoi(s);
+ ++p;
+ }
+ }
+
+ signal(SIGTERM, signal_handler);
+ signal(SIGINT, signal_handler);
+ for (; p < argc; ++p) {
+ child_pid = fork();
+ if (child_pid) {
+ /* Parent */
+ if (child_pid == (pid_t) -1) {
+ kill_children();
+ result = 1;
+ break;
+ }
+ add_child(child_pid);
+ } else {
+ /* Child */
+ int cargc;
+ char **cargv;
+
+ if (parse_command_line(argv[p], &cargc, &cargv))
+ return 1;
+ execve(cargv[0], cargv, env);
+ return 1;
+ }
+ }
+ if (!result && duration > 0) {
+ signal(SIGALRM, alarm_handler);
+ alarm(duration);
+ }
+ while (have_children()) {
+ status = 0;
+ child_pid = wait(&status);
+ if (child_pid == (pid_t) -1) {
+ if (errno == EINTR)
+ continue;
+ kill_children();
+ return 1;
+ }
+ mark_child_gone(child_pid);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ result = 1;
+ kill_children();
+ }
+ }
+
+ if (alarm_gone_off)
+ return 0;
+
+ return result;
+}