diff options
Diffstat (limited to 'kernel/test/s5fstest.c')
-rw-r--r-- | kernel/test/s5fstest.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/kernel/test/s5fstest.c b/kernel/test/s5fstest.c new file mode 100644 index 0000000..c60ee32 --- /dev/null +++ b/kernel/test/s5fstest.c @@ -0,0 +1,251 @@ +// +// Tests some edge cases of s5fs +// + +#include "errno.h" +#include "globals.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" + +#define BUFSIZE 256 +#define BIG_BUFSIZE 2056 + +static void get_file_name(char *buf, size_t sz, long fileno) +{ + snprintf(buf, sz, "file%ld", fileno); +} + +// Write to a fail forever until it is either filled up or we get an error. +static long write_until_fail(int fd) +{ + size_t total_written = 0; + char buf[BIG_BUFSIZE] = {42}; + while (total_written < S5_MAX_FILE_SIZE) + { + long 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 long is_first_n_bytes_zero(int fd, size_t n) +{ + size_t total_read = 0; + while (total_read < n) + { + size_t amt_to_read = MIN(BIG_BUFSIZE, n - total_read); + char buf[BIG_BUFSIZE] = {1}; + long res = do_read(fd, buf, amt_to_read); + if ((size_t)res != amt_to_read) + { + dbg(DBG_TESTFAIL, "do_read result was %ld\n", res); + return 0; + } + total_read += res; + + // Check everything that we read is indeed 0 + // TODO use gcc intrinsic to just scan for first non-zero + for (size_t i = 0; i < amt_to_read; i++) + { + if (buf[i]) + { + dbg(DBG_TESTFAIL, "buf contains char %d\n", buf[i]); + return 0; + } + } + } + + return 1; +} + +static void test_running_out_of_inodes() +{ + // Open a ton of files until we get an error + long res; + long 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((int)res) == 0, "couldn't close"); + } + else + { + break; + } + } + test_assert(res == -ENOSPC, "Did not get ENOSPC error"); + + // make sure mkdir fails now that we're out of inodes + test_assert(do_mkdir("directory") < 0, "do_mkdir worked!?"); + test_assert(res == -ENOSPC, "unexpected error"); + + test_assert(do_mknod("nod", S_IFCHR, 123) != 0, "mknod worked!?"); + test_assert(res == -ENOSPC, "wrong error code"); + + // 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 = (int)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() +{ + long res = 0; + int fd = (int)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"); + test_assert(res == -EFBIG || res == -EINVAL, "Wrong error code"); + + 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() +{ + long res = 0; + + int fd1 = (int)do_open("fullfile", O_RDWR | O_CREAT); + + res = write_until_fail(fd1); + test_assert(res == 0, "Ran out of space quicker than we expected"); + test_assert(do_close(fd1) == 0, "could not close"); + + int fd2 = (int)do_open("partiallyfullfile", O_RDWR | O_CREAT); + res = write_until_fail(fd2); + test_assert(res == -ENOSPC, "Did not get nospc error"); + + 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 = (int)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 size_t sz = strlen(b); + + test_assert(do_lseek(fd, addr, SEEK_SET) == addr, "couldnt seek"); + test_assert((size_t)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 for direct blocks failed"); + + // 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; +} + +static int test_sparseness_indirect_blocks() +{ + const char *filename = "bigsparsefile"; + int fd = (int)do_open(filename, O_RDWR | O_CREAT); + + // Now write to some random address that'll be in an indirect block + const int addr = 1000000; + const char *b = "iboros"; + const size_t sz = strlen(b); + + test_assert(do_lseek(fd, addr, SEEK_SET) == addr, "couldnt seek"); + test_assert((size_t)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 for indirect blocks failed"); + + // 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; +} + +long s5fstest_main(int arg0, void *arg1) +{ + dbg(DBG_TEST, "\nStarting S5FS test\n"); + + test_init(); + + 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; +}
\ No newline at end of file |