aboutsummaryrefslogtreecommitdiff
path: root/user/usr/bin/tests/s5fstest.c
diff options
context:
space:
mode:
Diffstat (limited to 'user/usr/bin/tests/s5fstest.c')
-rw-r--r--user/usr/bin/tests/s5fstest.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/user/usr/bin/tests/s5fstest.c b/user/usr/bin/tests/s5fstest.c
new file mode 100644
index 0000000..e2441ac
--- /dev/null
+++ b/user/usr/bin/tests/s5fstest.c
@@ -0,0 +1,332 @@
+//
+// Tests some edge cases of s5fs
+// Ported to user mode by twd in 7/2018
+//
+
+#ifdef __KERNEL__
+
+#include "config.h"
+#include "errno.h"
+#include "globals.h"
+#include "limits.h"
+
+#include "test/usertest.h"
+
+#include "util/debug.h"
+#include "util/printf.h"
+#include "util/string.h"
+
+#include "fs/fcntl.h"
+#include "fs/lseek.h"
+#include "fs/s5fs/s5fs.h"
+#include "fs/vfs_syscall.h"
+
+#else
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <test/test.h>
+#include <unistd.h>
+
+#define do_write write
+#define do_read read
+#define do_lseek lseek
+#define do_open(x, y) open((x), (y), 0777)
+#define do_link link
+#define do_unlink unlink
+#define do_mknod mknod
+#define do_close close
+#define do_mkdir(x) mkdir((x), 0777)
+#define do_chdir chdir
+#define do_rmdir rmdir
+
+#define S5_BLOCK_SIZE 4096
+#define S5_MAX_FILE_BLOCKS 1052
+
+#define KASSERT(x) test_assert(x, NULL)
+#define dbg(code, fmt, args...) printf(fmt, ##args)
+
+#endif
+
+#define BUFSIZE 256
+#define BIG_BUFSIZE 2056
+
+#define S5_MAX_FILE_SIZE S5_BLOCK_SIZE *S5_MAX_FILE_BLOCKS
+
+static void get_file_name(char *buf, size_t sz, int fileno)
+{
+ snprintf(buf, sz, "file%d", fileno);
+}
+
+// Write to a fail forever until it is either filled up or we get an error.
+static int write_until_fail(int fd)
+{
+ size_t total_written = 0;
+ char buf[BIG_BUFSIZE] = {42};
+ while (total_written < S5_MAX_FILE_SIZE)
+ {
+ int res = do_write(fd, buf, BIG_BUFSIZE);
+ if (res < 0)
+ {
+ return res;
+ }
+ total_written += res;
+ }
+ KASSERT(total_written == S5_MAX_FILE_SIZE);
+ KASSERT(do_lseek(fd, 0, SEEK_END) == S5_MAX_FILE_SIZE);
+
+ return 0;
+}
+
+// Read n bytes from the file, and check they're all 0
+// We do this in increments of big_bufsize because we might want to read
+// like a million bytes from the file
+static int is_first_n_bytes_zero(int fd, int n)
+{
+ int total_read = 0;
+ while (total_read < n)
+ {
+ int amt_to_read = MIN(BIG_BUFSIZE, n - total_read);
+ char buf[BIG_BUFSIZE] = {1};
+ int res = do_read(fd, buf, amt_to_read);
+ if (res != amt_to_read)
+ {
+ dbg(DBG_TESTFAIL, "do_read result was %d\n", res);
+ return 0;
+ // KASSERT("Could not read from file" && 0);
+ }
+ total_read += res;
+
+ // Check everything that we read is indeed 0
+ for (int i = 0; i < amt_to_read; ++i)
+ {
+ if (buf[i] != 0)
+ {
+ dbg(DBG_TESTFAIL, "buf contains char %d\n", (int)buf[i]);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void test_running_out_of_inodes()
+{
+ // Open a ton of files until we get an error
+ int res = -1;
+ int fileno = 0;
+ char filename[BUFSIZE];
+
+ // open files til we get an error
+ while (1)
+ {
+ get_file_name(filename, BUFSIZE, fileno);
+ res = do_open(filename, O_RDONLY | O_CREAT);
+ if (res >= 0)
+ {
+ fileno++;
+ test_assert(do_close(res) == 0, "couldn't close");
+ }
+ else
+ {
+ break;
+ }
+ }
+#ifdef __KERNEL__
+ test_assert(res == -ENOSPC, "Did not get ENOSPC error");
+#else
+ test_assert(errno == ENOSPC, "Did not get ENOSPC error");
+#endif
+
+ // make sure mkdir fails now that we're out of inodes
+ test_assert(do_mkdir("directory") < 0, "do_mkdir worked!?");
+#ifdef __KERNEL__
+ test_assert(res == -ENOSPC, "unexpected error");
+#else
+ test_assert(errno == ENOSPC, "unexpected error");
+#endif
+
+#ifdef __KERNEL__
+ test_assert(do_mknod("nod", S_IFCHR, 123) != 0, "mknod worked!?");
+ test_assert(res == -ENOSPC, "wrong error code");
+#endif
+
+ // the last file we tried to open failed
+ fileno--;
+
+ do
+ {
+ get_file_name(filename, BUFSIZE, fileno);
+ res = do_unlink(filename);
+ test_assert(res == 0, "couldnt unlink");
+ fileno--;
+ } while (fileno >= 0);
+
+ // Now we've freed all the files, try to create another file
+ int fd = do_open("file", O_RDONLY | O_CREAT);
+ test_assert(fd >= 0, "Still cannot create files");
+ test_assert(do_close(fd) == 0, "Could not do_close fd");
+ test_assert(do_unlink("file") == 0, "Could not remove file");
+}
+
+static void test_filling_file()
+{
+ int res = 0;
+ int fd = do_open("hugefile", O_RDWR | O_CREAT);
+ KASSERT(fd >= 0);
+
+ res = write_until_fail(fd);
+ test_assert(res == 0, "Did not write to entire file");
+
+ // make sure all other writes are unsuccessful/dont complete
+ char buf[BIG_BUFSIZE] = {0};
+ res = do_write(fd, buf, sizeof(buf));
+ test_assert(res < 0, "Able to write although the file is full");
+#ifdef __KERNEL__
+ test_assert(res == -EFBIG || res == -EINVAL, "Wrong error code");
+#else
+ test_assert(errno == EFBIG || errno == EINVAL, "Wrong error code");
+#endif
+
+ test_assert(do_close(fd) == 0, "couldnt close hugefile");
+ test_assert(do_unlink("hugefile") == 0, "couldnt unlink hugefile");
+}
+
+// Fill up the disk. Apparently to do this, we should need to fill up one
+// entire file, then start to fill up another. We should eventually get
+// the ENOSPC error
+static void test_running_out_of_blocks()
+{
+ int res = 0;
+
+ int fd1 = do_open("fullfile", O_RDWR | O_CREAT);
+
+ res = write_until_fail(fd1);
+ test_assert(res == 0, "Ran out of space quicker than we expected");
+
+ int fd2 = do_open("partiallyfullfile", O_RDWR | O_CREAT);
+ res = write_until_fail(fd2);
+#ifdef __KERNEL__
+ test_assert(res == -ENOSPC, "Did not get nospc error");
+#else
+ test_assert(errno == ENOSPC, "Did not get nospc error");
+#endif
+
+ test_assert(do_close(fd1) == 0, "could not close");
+ test_assert(do_close(fd2) == 0, "could not close");
+
+ test_assert(do_unlink("fullfile") == 0, "couldnt do_unlink file");
+ test_assert(do_unlink("partiallyfullfile") == 0, "couldnt do_unlink file");
+}
+
+// Open a new file, write to some random address in the file,
+// and make sure everything up to that is all 0s.
+static int test_sparseness_direct_blocks()
+{
+ const char *filename = "sparsefile";
+ int fd = do_open(filename, O_RDWR | O_CREAT);
+
+ // Now write to some random address that'll be in a direct block
+ const int addr = 10000;
+ const char *b = "iboros";
+ const int sz = strlen(b);
+
+ test_assert(do_lseek(fd, addr, SEEK_SET) == addr, "couldnt seek");
+ test_assert(do_write(fd, b, sz) == sz, "couldnt write to random address");
+
+ test_assert(do_lseek(fd, 0, SEEK_SET) == 0, "couldnt seek back to begin");
+ test_assert(is_first_n_bytes_zero(fd, addr) == 1, "sparseness don't work");
+
+ // Get rid of this file
+ test_assert(do_close(fd) == 0, "couldn't close file");
+ test_assert(do_unlink(filename) == 0, "couldnt unlink file");
+
+ return 0;
+}
+
+/*
+ * Fixed by twd to do a better test in 7/2018
+ */
+static int test_sparseness_indirect_blocks()
+{
+ const char *filename = "bigsparsefile";
+ int fd = do_open(filename, O_RDWR | O_CREAT);
+
+ // first partially fill the first block of the file
+ char randomgarbage[4050];
+ test_assert(do_write(fd, randomgarbage, 4050) == 4050,
+ "couldn't write to first block");
+
+ // Now write to some random address that'll be in an indirect block
+ const int addr = 1000000;
+ const char *b = "iboros";
+ const int sz = strlen(b);
+
+ test_assert(do_lseek(fd, addr, SEEK_SET) == addr, "couldnt seek");
+ test_assert(do_write(fd, b, sz) == sz, "couldnt write to random address");
+
+ test_assert(do_lseek(fd, 4050, SEEK_SET) == 4050,
+ "couldnt seek back to begin");
+ test_assert(is_first_n_bytes_zero(fd, addr - 4050) == 1,
+ "sparseness don't work");
+
+ // Get rid of this file
+ test_assert(do_close(fd) == 0, "couldn't close file");
+ test_assert(do_unlink(filename) == 0, "couldnt unlink file");
+
+ return 0;
+}
+
+#ifdef __KERNEL__
+extern uint64_t jiffies;
+#endif
+
+static void seed_randomness()
+{
+#ifdef __KERNEL__
+ srand(jiffies);
+#else
+ srand(time(NULL));
+#endif
+ rand();
+}
+
+#ifdef __KERNEL__
+int s5fstest_main()
+#else
+
+int main()
+#endif
+{
+ dbg(DBG_TEST, "Starting S5FS test\n");
+
+ test_init();
+ seed_randomness();
+
+ KASSERT(do_mkdir("s5fstest") == 0);
+ KASSERT(do_chdir("s5fstest") == 0);
+ dbg(DBG_TEST, "Test dir initialized\n");
+
+ dbg(DBG_TEST, "Testing sparseness for direct blocks\n");
+ test_sparseness_direct_blocks();
+ dbg(DBG_TEST, "Testing sparseness for indirect blocks\n");
+ test_sparseness_indirect_blocks();
+
+ dbg(DBG_TEST, "Testing running out of inodes\n");
+ test_running_out_of_inodes();
+ dbg(DBG_TEST, "Testing filling a file to max capacity\n");
+ test_filling_file();
+ dbg(DBG_TEST, "Testing using all available blocks on disk\n");
+ test_running_out_of_blocks();
+
+ test_assert(do_chdir("..") == 0, "");
+ test_assert(do_rmdir("s5fstest") == 0, "");
+
+ test_fini();
+
+ return 0;
+}