]> www.infradead.org Git - mtd-utils.git/commitdiff
Add fs-tests from Adrian Hunter
authorAdrian Hunter <ext-adrian.hunter@nokia.com>
Thu, 15 Mar 2007 13:45:19 +0000 (08:45 -0500)
committerJosh Boyer <jwboyer@gmail.com>
Thu, 15 Mar 2007 13:45:19 +0000 (08:45 -0500)
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
Signed-off-by: Josh Boyer <jwboyer@gmail.com>
29 files changed:
tests/fs-tests/Makefile [new file with mode: 0644]
tests/fs-tests/help_all.sh [new file with mode: 0755]
tests/fs-tests/integrity/Makefile [new file with mode: 0644]
tests/fs-tests/integrity/integck.c [new file with mode: 0644]
tests/fs-tests/lib/Makefile [new file with mode: 0644]
tests/fs-tests/lib/tests.c [new file with mode: 0644]
tests/fs-tests/lib/tests.h [new file with mode: 0644]
tests/fs-tests/run_all.sh [new file with mode: 0755]
tests/fs-tests/simple/Makefile [new file with mode: 0644]
tests/fs-tests/simple/ftrunc.c [new file with mode: 0644]
tests/fs-tests/simple/test_1.c [new file with mode: 0644]
tests/fs-tests/simple/test_2.c [new file with mode: 0644]
tests/fs-tests/stress/Makefile [new file with mode: 0644]
tests/fs-tests/stress/atoms/Makefile [new file with mode: 0644]
tests/fs-tests/stress/atoms/fwrite00.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/gcd_hupper.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/pdfrun.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/rmdir00.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/rndrm00.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/rndrm99.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/rndwrite00.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/stress_1.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/stress_2.c [new file with mode: 0644]
tests/fs-tests/stress/atoms/stress_3.c [new file with mode: 0644]
tests/fs-tests/stress/stress00.sh [new file with mode: 0755]
tests/fs-tests/stress/stress01.sh [new file with mode: 0755]
tests/fs-tests/utils/Makefile [new file with mode: 0644]
tests/fs-tests/utils/free_space.c [new file with mode: 0644]
tests/fs-tests/utils/fstest_monitor.c [new file with mode: 0644]

diff --git a/tests/fs-tests/Makefile b/tests/fs-tests/Makefile
new file mode 100644 (file)
index 0000000..d188796
--- /dev/null
@@ -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 (executable)
index 0000000..34b890b
--- /dev/null
@@ -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 (file)
index 0000000..a35f4d0
--- /dev/null
@@ -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 (file)
index 0000000..23ad9bc
--- /dev/null
@@ -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 (file)
index 0000000..8d57824
--- /dev/null
@@ -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 (file)
index 0000000..2f513d7
--- /dev/null
@@ -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 (file)
index 0000000..36ca310
--- /dev/null
@@ -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 (executable)
index 0000000..e79993a
--- /dev/null
@@ -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 (file)
index 0000000..07ddf65
--- /dev/null
@@ -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 (file)
index 0000000..86edf65
--- /dev/null
@@ -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 (file)
index 0000000..69eafe4
--- /dev/null
@@ -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 (file)
index 0000000..2094460
--- /dev/null
@@ -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 (file)
index 0000000..c24ff3f
--- /dev/null
@@ -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 (file)
index 0000000..9fbfd39
--- /dev/null
@@ -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 (file)
index 0000000..2f40b3d
--- /dev/null
@@ -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 (file)
index 0000000..31c175d
--- /dev/null
@@ -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 (file)
index 0000000..3536580
--- /dev/null
@@ -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 (file)
index 0000000..c1d0729
--- /dev/null
@@ -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 (file)
index 0000000..724b1c3
--- /dev/null
@@ -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 (file)
index 0000000..7751839
--- /dev/null
@@ -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 (file)
index 0000000..655d9cc
--- /dev/null
@@ -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 (file)
index 0000000..86f94c2
--- /dev/null
@@ -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 (file)
index 0000000..a9bc31a
--- /dev/null
@@ -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 (file)
index 0000000..99fb05d
--- /dev/null
@@ -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 (executable)
index 0000000..be95d7c
--- /dev/null
@@ -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 (executable)
index 0000000..5913c1c
--- /dev/null
@@ -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 (file)
index 0000000..9fb60b5
--- /dev/null
@@ -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 (file)
index 0000000..88036aa
--- /dev/null
@@ -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 (file)
index 0000000..298ee26
--- /dev/null
@@ -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;
+}