Add inode test script
authorNick Downing <nick@ndcode.org>
Thu, 28 Mar 2019 12:30:27 +0000 (23:30 +1100)
committerNick Downing <nick@ndcode.org>
Thu, 28 Mar 2019 12:30:27 +0000 (23:30 +1100)
.gitignore
Makefile
fuzix_fs.c
fuzix_fs.h
inode_test_gen.c [new file with mode: 0644]
inode_test_run.c [new file with mode: 0644]
p.sh [new file with mode: 0755]
pool_test_gen.c
util.c

index 53ce89b..9ea066b 100644 (file)
@@ -1,5 +1,9 @@
 *.o
+/fs.bin
 /mkfs
+/inode_test.txt
+/inode_test_gen
+/inode_test_run
 /pool_test.txt
 /pool_test_gen
 /pool_test_run
index 51ee60c..3c1ae44 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,7 @@
 CFLAGS=-g -Wall
 
-all: pool_test_gen pool_test_run process_test_gen process_test_run ucp mkfs
+all: pool_test_gen pool_test_run process_test_gen process_test_run ucp mkfs \
+inode_test_gen inode_test_run
 
 pool_test_gen: pool_test_gen.o
        ${CC} ${CFLAGS} -o $@ $^
@@ -38,5 +39,16 @@ mkfs.o: mkfs.c fuzix_fs.h
 mkfs: mkfs.o util.o
        ${CC} ${CFLAGS} -o $@ $^
 
+inode_test_gen: inode_test_gen.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+inode_test_gen.o: inode_test_gen.c
+
+inode_test_run: inode_test_run.o fuzix_fs.o util.o
+       ${CC} ${CFLAGS} -o $@ $^
+
+inode_test_run.o: inode_test_run.c fuzix_fs.h util.h
+
 clean:
-       rm -f pool_test_gen pool_test_run process_test_gen process_test_run ucp mkfs *.o
+       rm -f pool_test_gen pool_test_run process_test_gen process_test_run \
+ucp mkfs inode_test_gen inode_test_run *.o
index 9106b3d..b198505 100644 (file)
@@ -1,3 +1,4 @@
+#include <assert.h> // Nick
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
@@ -11,7 +12,7 @@ struct oft of_tab[OFTSIZE]; /* Open File Table */
 inoptr root;
 struct cinode i_tab[ITABSIZE];
 struct filesys fs_tab[1];
-uint16_t bufclock;             /* Time-stamp counter for LRU */
+long long/*uint16_t*/ bufclock;                /* Time-stamp counter for LRU */
 struct blkbuf bufpool[NBUFS];
 struct u_data udata;
 
@@ -157,6 +158,7 @@ int fuzix_creat(char *name, int16_t mode)
                }
                if (fuzix_getmode(ino) == F_REG) {
                        /* Truncate the file to zero length */
+                       udata.u_offset = 0;
                        f_trunc(ino);
                        /* Reset any oft pointers */
                        for (j = 0; j < OFTSIZE; ++j)
@@ -411,7 +413,8 @@ uint16_t writei(inoptr ino)
                        ino->c_node.i_size = swizzle32(udata.u_offset);
                        ino->c_dirty = 1;
                }
-               return towrite;
+
+               return udata.u_count - towrite; // Nick bugfix
 
        default:
                udata.u_error = ENODEV;
@@ -1310,8 +1313,10 @@ void i_deref(inoptr ino)
 
                m = swizzle16(ino->c_node.i_mode);
                if (((m & F_MASK) == F_REG) ||
-                   ((m & F_MASK) == F_DIR) || ((m & F_MASK) == F_PIPE))
+                   ((m & F_MASK) == F_DIR) || ((m & F_MASK) == F_PIPE)) {
+                       udata.u_offset = 0;
                        f_trunc(ino);
+               }
        }
        /* If the inode was modified, we must write it to disk. */
        if (!(ino->c_refs) && ino->c_dirty) {
@@ -1358,28 +1363,79 @@ int isdevice(inoptr ino)
 void f_trunc(inoptr ino)
 {
        int dev;
+       blkno_t blk;
+       int32_t blocks;
        int j;
 
+       if (udata.u_offset >= ino->c_node.i_size)
+               return;
+
+       ino->c_node.i_size = udata.u_offset;
+       ino->c_dirty = 1;
+
        dev = ino->c_dev;
+       blocks = (int32_t)((udata.u_offset + 511) >> 9);
 
        /* First deallocate the double indirect blocks */
-       freeblk(dev, swizzle16(ino->c_node.i_addr[19]), 2);
+       blk = swizzle16(ino->c_node.i_addr[19]);
+       if (blocks > 18 + 256) {
+               freeblk_partial2(dev, blk, blocks - (18 + 256));
+               return;
+       }
+       freeblk(dev, blk, 2);
+       ino->c_node.i_addr[19] = 0;
 
        /* Also deallocate the indirect blocks */
-       freeblk(dev, swizzle16(ino->c_node.i_addr[18]), 1);
+       blk = swizzle16(ino->c_node.i_addr[18]);
+       if ((int)blocks > 18) {
+               freeblk_partial1(dev, blk, (int)blocks - 18);
+               return;
+       }
+       freeblk(dev, blk, 1);
+       ino->c_node.i_addr[18] = 0;
 
        /* Finally, free the direct blocks */
-       for (j = 17; j >= 0; --j)
+       for (j = 17; j >= (int)blocks; --j) {
                freeblk(dev, swizzle16(ino->c_node.i_addr[j]), 0);
+               ino->c_node.i_addr[j] = 0;
+       }
+}
 
-       bzero((char *) ino->c_node.i_addr, sizeof(ino->c_node.i_addr));
+/* Companion functions to f_trunc(). */
+void freeblk_partial2(int dev, blkno_t blk, int32_t blocks)
+{
+       blkno_t *buf;
+       int end, j;
 
-       ino->c_dirty = 1;
-       ino->c_node.i_size = 0;
+       if (!blk || blocks >= 65536)
+           return;
+
+       buf = (blkno_t *) bread(dev, blk, 0);
+       end = (int)blocks >> 8;
+       for (j = 255; j > end; --j) {
+               freeblk(dev, swizzle16(buf[j]), 1);
+               buf[j] = 0;
+       }
+       freeblk_partial1(dev, swizzle16(buf[j]), (((int)blocks - 1) & 0xff) + 1);
+       bawrite((bufptr) buf);
 }
 
+void freeblk_partial1(int dev, blkno_t blk, int blocks)
+{
+       blkno_t *buf;
+       int j;
+
+       if (!blk || blocks >= 256)
+           return;
+
+       buf = (blkno_t *) bread(dev, blk, 0);
+       for (j = 255; j >= blocks; --j) {
+               freeblk(dev, swizzle16(buf[j]), 0);
+               buf[j] = 0;
+       }
+       bawrite((bufptr) buf);
+}
 
-/* Companion function to f_trunc(). */
 void freeblk(int dev, blkno_t blk, int level)
 {
        blkno_t *buf;
@@ -1398,8 +1454,6 @@ void freeblk(int dev, blkno_t blk, int level)
        blk_free(dev, blk);
 }
 
-
-
 /* Changes: blk_alloc zeroes block it allocates */
 /*
  * Bmap defines the structure of file system storage by returning
@@ -1615,6 +1669,7 @@ char *bread(int dev, blkno_t blk, int rewrite)
 
 done:
        bp->bf_busy = 1;
+ //printf("bread %d\n", (int)(bp - bufpool));
        bp->bf_time = ++bufclock;       /* Time stamp it */
        return (bp->bf_data);
 }
@@ -1634,6 +1689,8 @@ void bawrite(bufptr bp)
 
 int bfree(bufptr bp, int dirty)
 {
+ assert(bp->bf_busy);
+ //printf("bfree %d %d\n", (int)(bp - bufpool), dirty);
 /*printf("Releasing block %d (%d)\n", bp->bf_blk, dirty);*/
        bp->bf_dirty |= dirty;
        bp->bf_busy = 0;
@@ -1661,6 +1718,7 @@ char *tmpbuf(void)
        bp = freebuf();
        bp->bf_dev = -1;
        bp->bf_busy = 1;
+ //printf("tmpbuf %d\n", (int)(bp - bufpool));
        bp->bf_time = ++bufclock;       /* Time stamp it */
        return (bp->bf_data);
 }
@@ -1711,12 +1769,16 @@ bufptr freebuf(void)
        /* Try to find a non-busy buffer and write out the data if it is dirty */
        oldest = NULL;
        oldtime = 0;
+ //printf("nonbusy");
        for (bp = bufpool; bp < bufpool + NBUFS; ++bp) {
+ //if (!bp->bf_busy)
+ // printf(" %d(%lld)", (int)(bp -bufpool), bufclock - bp->bf_time);
                if (bufclock - bp->bf_time >= oldtime && !bp->bf_busy) {
                        oldest = bp;
                        oldtime = bufclock - bp->bf_time;
                }
        }
+ //printf("\n");
        ifnot(oldest)
            panic("no free buffers");
 
index 2f3ea69..53cb69e 100644 (file)
@@ -16,7 +16,7 @@
 #define NDEVS 1
 #define NBUFS 10
 #define OFTSIZE 15
-#define ITABSIZE 20
+#define ITABSIZE 96 //20
 #define _NSIG NSIGS
 #define NULLINODE ((inoptr)NULL)
 #define NULLBLK ((blkno_t)-1)
@@ -71,7 +71,7 @@ typedef struct blkbuf {
     blkno_t     bf_blk;
     char        bf_dirty;
     char        bf_busy;
-    uint16_t      bf_time;         /* LRU time stamp */
+    long long/*uint16_t*/      bf_time;         /* LRU time stamp */
 } blkbuf;
 
 typedef blkbuf *bufptr;
@@ -227,7 +227,7 @@ extern struct oft of_tab[OFTSIZE]; /* Open File Table */
 extern inoptr root;
 extern struct cinode i_tab[ITABSIZE];
 extern struct filesys fs_tab[1];
-extern uint16_t bufclock;              /* Time-stamp counter for LRU */
+extern long long/*uint16_t*/ bufclock;         /* Time-stamp counter for LRU */
 extern struct blkbuf bufpool[NBUFS];
 extern struct u_data udata;
 
@@ -276,6 +276,8 @@ void i_deref(inoptr ino);
 void wr_inode(inoptr ino);
 int isdevice(inoptr ino);
 void f_trunc(inoptr ino);
+void freeblk_partial2(int dev, blkno_t blk, int32_t blocks);
+void freeblk_partial1(int dev, blkno_t blk, int blocks);
 void freeblk(int dev, blkno_t blk, int level);
 blkno_t bmap(inoptr ip, blkno_t bn, int rwflg);
 void validblk(int dev, blkno_t num);
diff --git a/inode_test_gen.c b/inode_test_gen.c
new file mode 100644 (file)
index 0000000..00ba901
--- /dev/null
@@ -0,0 +1,94 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rassert.h"
+
+#define BLOCK_SIZE 0x200
+#define BLOCK_SIZE_SHIFT 9
+
+#define BLOCK_PTRS 0x100
+#define BLOCK_PTRS_SHIFT 8
+
+int rand_int(int n) {
+  return (int)((long long)rand() * n / (RAND_MAX + 1LL));
+}
+
+int bytes_to_blocks(int size) {
+  size = (size + (BLOCK_SIZE - 1)) >> BLOCK_SIZE_SHIFT;
+  int blocks = size;
+  if (size > 18) {
+    // has indirect blocks
+    size = (size + (BLOCK_PTRS - 1 - 18)) >> BLOCK_PTRS_SHIFT;
+    blocks += size;
+    if (size > 1) {
+      // has double indirect blocks
+      size = (size + (BLOCK_PTRS - 1 - 1)) >> BLOCK_PTRS_SHIFT;
+      rassert(size == 1);
+      blocks += size;
+    }
+  }
+  return blocks;
+}
+
+int main(int argc, char **argv) {
+  if (argc < 5) {
+    printf("usage: %s n_files n_blocks n_events file_size [seed]\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  int n_files = atoi(argv[1]);
+  int n_blocks = atoi(argv[2]);
+  int n_events = atoi(argv[3]);
+  int file_size = atoi(argv[4]);
+  int seed = argc >= 6 ? atoi(argv[5]) : 1;
+
+  int *files = malloc(n_files * sizeof(int));
+  rassert(files);
+  memset(files, -1, n_files * sizeof(int));
+
+  srand(seed);
+  int blocks_used = 0;
+  for (int i = 0; i < n_events; ++i) {
+    int file = rand_int(n_files);
+    int old_size = files[file];
+    if (old_size == -1) {
+      int size = rand_int(file_size * BLOCK_SIZE);
+      int size_blocks = bytes_to_blocks(size);
+      bool success = blocks_used + size_blocks <= n_blocks;
+      printf(
+        "alloc %d %d %s\n",
+        file,
+        size,
+        success ? "true" : "false"
+      );
+      if (success) {
+        files[file] = size;
+        blocks_used += size_blocks;
+      }
+    }
+    else if (rand_int(4)) {
+      int size = rand_int(file_size * BLOCK_SIZE);
+      int size_blocks = bytes_to_blocks(size);
+      int old_size_blocks = bytes_to_blocks(old_size);
+      bool success = blocks_used + size_blocks - old_size_blocks <= n_blocks;
+      printf(
+        "realloc %d %d %d %s\n",
+        file,
+        old_size,
+        size,
+        success ? "true" : "false"
+      );
+      if (success) {
+        files[file] = size;
+        blocks_used += size_blocks - old_size_blocks;
+      }
+    }
+    else {
+      printf("free %d %d\n", file, old_size);
+      files[file] = -1;
+      blocks_used -= bytes_to_blocks(old_size);
+    }
+  }
+
+  return 0;
+}
diff --git a/inode_test_run.c b/inode_test_run.c
new file mode 100644 (file)
index 0000000..b7a7f5a
--- /dev/null
@@ -0,0 +1,266 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define UCP 1
+#include "fuzix_fs.h"
+#include "rassert.h"
+#include "util.h"
+
+#define BLOCK_SIZE 0x200
+#define BLOCK_SIZE_SHIFT 9
+
+#define BLOCK_PTRS 0x100
+#define BLOCK_PTRS_SHIFT 8
+
+#define TRANSFER_SIZE 821 // a prime number somewhat near the block size
+
+int rand_int(int n) {
+  return (int)((long long)rand() * n / (RAND_MAX + 1LL));
+}
+
+bool hash_init(int file, struct cinode *inode, int base, int limit) {
+  int count;
+  for (int ptr = base; (count = limit - ptr) > 0; ptr += count) {
+    if (count > TRANSFER_SIZE)
+      count = TRANSFER_SIZE;
+
+    uint8_t buf[TRANSFER_SIZE];
+    for (int i = 0; i < count; ++i) {
+      long long hash = file * 17 + (ptr + i) * 29;
+      hash = (hash & 0xffffffffLL) + (hash >> 32);
+      hash = (hash & 0xffffLL) + (hash >> 16);
+      hash = (hash & 0xffLL) + (hash >> 8);
+      hash = (hash & 0xffLL) + (hash >> 8);
+      buf[i] = (uint8_t)hash;
+    }
+
+    udata.u_base = (char *)buf;
+    udata.u_count = count;
+    udata.u_offset = ptr;
+    udata.u_error = 0;
+    if (writei(inode) < count || udata.u_error)
+      return false;
+  }
+  rassert((int)inode->c_node.i_size == limit);
+  return true;
+}
+
+void hash_verify(int file, struct cinode *inode, int base, int limit) {
+  int count;
+  for (int ptr = base; (count = limit - ptr) > 0; ptr += count) {
+    if (count > TRANSFER_SIZE)
+      count = TRANSFER_SIZE;
+
+    uint8_t buf[TRANSFER_SIZE];
+    udata.u_base = (char *)buf;
+    udata.u_count = count;
+    udata.u_offset = ptr;
+    udata.u_error = 0;
+    rassert(readi(inode) == count && udata.u_error == 0);
+
+    for (int i = 0; i < count; ++i) {
+      long long hash = file * 17 + (ptr + i) * 29;
+      hash = (hash & 0xffffffffLL) + (hash >> 32);
+      hash = (hash & 0xffffLL) + (hash >> 16);
+      hash = (hash & 0xffLL) + (hash >> 8);
+      hash = (hash & 0xffLL) + (hash >> 8);
+      rassert(buf[i] == (uint8_t)hash);
+    }
+
+    base += count;
+  }
+}
+
+int main(int argc, char **argv) {
+  if (argc < 3) {
+    printf("usage: %s fs_path n_files\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  char *fs_path = argv[1];
+  int n_files = atoi(argv[2]);
+
+  fd_open(fs_path);
+  xfs_init(0);
+  printf("free inodes %d blocks %d\n", fs_tab[0].s_tinode, fs_tab[0].s_tfree);
+  struct cinode **files = malloc(n_files * sizeof(struct cinode *));
+  rassert(files);
+  memset(files, 0, n_files * sizeof(struct cinode *));
+
+  while (true) {
+ //printf("free inodes %d blocks %d\n", fs_tab[0].s_tinode, fs_tab[0].s_tfree);
+    char buf[256];
+    switch (scanf("%s", buf)) {
+    case -1:
+      goto done;
+    case 1:
+      break;
+    default:
+      rassert(false);
+    }
+    if (strcmp(buf, "alloc") == 0) {
+      int file, size;
+      rassert(scanf("%d %d %s", &file, &size, buf) == 3);
+      rassert(file >= 0 && file < n_files);
+      rassert(size >= 0);
+      bool success;
+      if (strcmp(buf, "false") == 0)
+        success = false;
+      else if (strcmp(buf, "true") == 0)
+        success = true;
+      else
+        rassert(false);
+      printf(
+        "alloc %d %d %s\n",
+        file,
+        size,
+        success ? "true" : "false"
+      );
+      rassert(files[file] == NULL);
+      bool result;
+      files[file] = i_open(0, 0);
+      if (files[file] == NULL)
+        result = false;
+      else {
+        /* This does not implement BSD style "sticky" groups */
+        files[file]->c_node.i_uid = udata.u_euid;
+        files[file]->c_node.i_gid = udata.u_egid;
+
+        files[file]->c_node.i_mode = F_REG;   /* For the time being */
+        files[file]->c_node.i_nlink = 0;
+        files[file]->c_node.i_size = 0;
+        for (int i = 0; i < 20; ++i)
+          files[file]->c_node.i_addr[i] = 0;
+        wr_inode(files[file]);
+
+        if (!hash_init(file, files[file], 0, size)) {
+          result = false;
+          i_deref(files[file]);
+          files[file] = NULL;
+        }
+        else
+          result = true;
+      }
+      printf(
+        "... %s\n",
+        result == success ?
+          "ok" :
+          result ? "succeeded, should fail" : "failed, should succeed"
+      );
+      if (files[file] && !success) {
+        printf("... undo\n");
+        i_deref(files[file]);
+        files[file] = NULL;
+      }
+    }
+    else if (strcmp(buf, "realloc") == 0) {
+      int file, old_size, size;
+      rassert(scanf("%d %d %d %s", &file, &old_size, &size, buf) == 4);
+      rassert(file >= 0 && file < n_files);
+      rassert(old_size >= 0);
+      rassert(size >= 0);
+      bool success;
+      if (strcmp(buf, "false") == 0)
+        success = false;
+      else if (strcmp(buf, "true") == 0)
+        success = true;
+      else
+        rassert(false);
+      printf(
+        "realloc %d %d %d %s\n",
+        file,
+        old_size,
+        size,
+        success ? "true" : "false"
+      );
+      if (files[file] == NULL)
+        printf("... not allocated, ignore\n");
+      else {
+        int actual_old_size = (int)files[file]->c_node.i_size;
+        if (actual_old_size != old_size) {
+          printf(
+            "... old size %d, should be %d\n",
+            actual_old_size,
+            old_size
+          );
+          rassert(actual_old_size <= old_size);
+        }
+        bool result;
+        if (size < actual_old_size) {
+          hash_verify(file, files[file], size, actual_old_size);
+          udata.u_offset = size;
+          f_trunc(files[file]);
+          result = true;
+        }
+        else if (!hash_init(file, files[file], actual_old_size, size)) {
+          udata.u_offset = actual_old_size;
+          f_trunc(files[file]);
+          result = false;
+        }
+        else
+          result = true;
+        printf(
+          "... %s\n",
+          result == success ?
+            "ok" :
+            result ? "succeeded, should fail" : "failed, should succeed"
+        );
+        if (result && !success) {
+          printf("... undo\n");
+          udata.u_offset = actual_old_size;
+          f_trunc(files[file]);
+        }
+      }
+    }
+    else if (strcmp(buf, "free") == 0) {
+      int file, old_size;
+      rassert(scanf("%d %d", &file, &old_size) == 2);
+      rassert(file >= 0 && file < n_files);
+      rassert(old_size >= 0);
+      printf(
+        "free %d %d\n",
+        file,
+        old_size
+      );
+      if (files[file] == NULL)
+        printf("... not allocated, ignore\n");
+      else {
+        int actual_old_size = (int)files[file]->c_node.i_size;
+        if (actual_old_size != old_size) {
+          printf(
+            "... old size %d, should be %d\n",
+            actual_old_size,
+            old_size
+          );
+          rassert(actual_old_size <= old_size);
+        }
+        hash_verify(file, files[file], 0, actual_old_size);
+        i_deref(files[file]);
+        files[file] = NULL;
+        printf("... ok\n");
+      }
+    }
+    else
+      rassert(false);
+  }
+
+done:
+  printf("free inodes %d blocks %d\n", fs_tab[0].s_tinode, fs_tab[0].s_tfree);
+  printf("final state:\n");
+  for (int i = 0; i < n_files; ++i) {
+    if (files[i] == NULL)
+      printf("file %d: not allocated\n", i);
+    else {
+      int size = (int)files[i]->c_node.i_size;
+      printf("file %d: size %d\n", i, size);
+      hash_verify(i, files[i], 0, size);
+      i_deref(files[i]);
+    }
+  }
+
+  printf("free inodes %d blocks %d\n", fs_tab[0].s_tinode, fs_tab[0].s_tfree);
+
+  return 0;
+}
diff --git a/p.sh b/p.sh
new file mode 100755 (executable)
index 0000000..ea49b0a
--- /dev/null
+++ b/p.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+echo "generate test script"
+./inode_test_gen 64 16128 16384 4096 >inode_test.txt
+
+echo "run test script"
+rm -f fs.bin
+./mkfs fs.bin 256 16384
+./inode_test_run fs.bin 64 <inode_test.txt
index 6169616..9672323 100644 (file)
@@ -58,11 +58,7 @@ int main(int argc, char **argv) {
       }
     }
     else {
-      printf(
-        "free %d %d\n",
-        item,
-        old_size
-      );
+      printf("free %d %d\n", item, old_size);
       items[item] = -1;
       pool_used -= old_size;
     }
diff --git a/util.c b/util.c
index 6293504..b127dcd 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,3 +1,5 @@
+#include <assert.h> // Nick
+#include <stdbool.h> // Nick
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -42,6 +44,7 @@ int fd_open(char *name)
 void panic(char *s)
 {
        fprintf(stderr, "panic: %s\n", s);
+ assert(false);
        exit(1);
 }