aboutsummaryrefslogtreecommitdiffstats
path: root/src/tests/filesys
diff options
context:
space:
mode:
authorklaar36 <klas.arvidsson@liu.se>2015-03-20 17:30:24 +0100
committerklaar36 <klas.arvidsson@liu.se>2015-03-20 17:30:24 +0100
commite7bc50ca8ffcaa6ed68ebd2315f78b0f5a7d10ad (patch)
tree4de97af7207676b69cb6a9aba8cb443cc134855d /src/tests/filesys
parentb0418a24e709f0632d2ede5b0f327c422931939b (diff)
downloadpintos-rs-e7bc50ca8ffcaa6ed68ebd2315f78b0f5a7d10ad.tar.gz
Initial Pintos
Diffstat (limited to 'src/tests/filesys')
-rw-r--r--src/tests/filesys/Grading.no-vm18
-rw-r--r--src/tests/filesys/Grading.with-vm22
-rw-r--r--src/tests/filesys/base/Make.tests18
-rw-r--r--src/tests/filesys/base/Rubric19
-rw-r--r--src/tests/filesys/base/child-syn-read.c44
-rw-r--r--src/tests/filesys/base/child-syn-wrt.c35
-rw-r--r--src/tests/filesys/base/full.inc20
-rw-r--r--src/tests/filesys/base/lg-create.c5
-rw-r--r--src/tests/filesys/base/lg-create.ck13
-rw-r--r--src/tests/filesys/base/lg-full.c6
-rw-r--r--src/tests/filesys/base/lg-full.ck16
-rw-r--r--src/tests/filesys/base/lg-random.c7
-rw-r--r--src/tests/filesys/base/lg-random.ck14
-rw-r--r--src/tests/filesys/base/lg-seq-block.c7
-rw-r--r--src/tests/filesys/base/lg-seq-block.ck16
-rw-r--r--src/tests/filesys/base/lg-seq-random.c6
-rw-r--r--src/tests/filesys/base/lg-seq-random.ck16
-rw-r--r--src/tests/filesys/base/random.inc59
-rw-r--r--src/tests/filesys/base/seq-block.inc20
-rw-r--r--src/tests/filesys/base/seq-random.inc22
-rw-r--r--src/tests/filesys/base/sm-create.c5
-rw-r--r--src/tests/filesys/base/sm-create.ck13
-rw-r--r--src/tests/filesys/base/sm-full.c6
-rw-r--r--src/tests/filesys/base/sm-full.ck16
-rw-r--r--src/tests/filesys/base/sm-random.c7
-rw-r--r--src/tests/filesys/base/sm-random.ck14
-rw-r--r--src/tests/filesys/base/sm-seq-block.c7
-rw-r--r--src/tests/filesys/base/sm-seq-block.ck16
-rw-r--r--src/tests/filesys/base/sm-seq-random.c6
-rw-r--r--src/tests/filesys/base/sm-seq-random.ck16
-rw-r--r--src/tests/filesys/base/syn-read.c31
-rw-r--r--src/tests/filesys/base/syn-read.ck33
-rw-r--r--src/tests/filesys/base/syn-read.h7
-rw-r--r--src/tests/filesys/base/syn-remove.c30
-rw-r--r--src/tests/filesys/base/syn-remove.ck16
-rw-r--r--src/tests/filesys/base/syn-write.c31
-rw-r--r--src/tests/filesys/base/syn-write.ck32
-rw-r--r--src/tests/filesys/base/syn-write.h9
-rw-r--r--src/tests/filesys/create.inc15
-rw-r--r--src/tests/filesys/extended/Make.tests61
-rw-r--r--src/tests/filesys/extended/Rubric.functionality26
-rw-r--r--src/tests/filesys/extended/Rubric.persistence24
-rw-r--r--src/tests/filesys/extended/Rubric.robustness9
-rw-r--r--src/tests/filesys/extended/child-syn-rw.c53
-rw-r--r--src/tests/filesys/extended/dir-empty-name-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-empty-name.c12
-rw-r--r--src/tests/filesys/extended/dir-empty-name.ck10
-rw-r--r--src/tests/filesys/extended/dir-mk-tree-persistence.ck16
-rw-r--r--src/tests/filesys/extended/dir-mk-tree.c12
-rw-r--r--src/tests/filesys/extended/dir-mk-tree.ck12
-rw-r--r--src/tests/filesys/extended/dir-mkdir-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-mkdir.c15
-rw-r--r--src/tests/filesys/extended/dir-mkdir.ck13
-rw-r--r--src/tests/filesys/extended/dir-open-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-open.c21
-rw-r--r--src/tests/filesys/extended/dir-open.ck20
-rw-r--r--src/tests/filesys/extended/dir-over-file-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-over-file.c13
-rw-r--r--src/tests/filesys/extended/dir-over-file.ck11
-rw-r--r--src/tests/filesys/extended/dir-rm-cwd-persistence.ck8
-rw-r--r--src/tests/filesys/extended/dir-rm-cwd.c75
-rw-r--r--src/tests/filesys/extended/dir-rm-cwd.ck51
-rw-r--r--src/tests/filesys/extended/dir-rm-parent-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-rm-parent.c16
-rw-r--r--src/tests/filesys/extended/dir-rm-parent.ck14
-rw-r--r--src/tests/filesys/extended/dir-rm-root-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-rm-root.c13
-rw-r--r--src/tests/filesys/extended/dir-rm-root.ck11
-rw-r--r--src/tests/filesys/extended/dir-rm-tree-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-rm-tree.c62
-rw-r--r--src/tests/filesys/extended/dir-rm-tree.ck14
-rw-r--r--src/tests/filesys/extended/dir-rmdir-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-rmdir.c14
-rw-r--r--src/tests/filesys/extended/dir-rmdir.ck12
-rw-r--r--src/tests/filesys/extended/dir-under-file-persistence.ck6
-rw-r--r--src/tests/filesys/extended/dir-under-file.c13
-rw-r--r--src/tests/filesys/extended/dir-under-file.ck11
-rw-r--r--src/tests/filesys/extended/dir-vine-persistence.ck37
-rw-r--r--src/tests/filesys/extended/dir-vine.c85
-rw-r--r--src/tests/filesys/extended/dir-vine.ck11
-rw-r--r--src/tests/filesys/extended/grow-create-persistence.ck6
-rw-r--r--src/tests/filesys/extended/grow-create.c4
-rw-r--r--src/tests/filesys/extended/grow-create.ck13
-rw-r--r--src/tests/filesys/extended/grow-dir-lg-persistence.ck9
-rw-r--r--src/tests/filesys/extended/grow-dir-lg.c6
-rw-r--r--src/tests/filesys/extended/grow-dir-lg.ck61
-rw-r--r--src/tests/filesys/extended/grow-dir.inc41
-rw-r--r--src/tests/filesys/extended/grow-file-size-persistence.ck7
-rw-r--r--src/tests/filesys/extended/grow-file-size.c33
-rw-r--r--src/tests/filesys/extended/grow-file-size.ck17
-rw-r--r--src/tests/filesys/extended/grow-root-lg-persistence.ck9
-rw-r--r--src/tests/filesys/extended/grow-root-lg.c4
-rw-r--r--src/tests/filesys/extended/grow-root-lg.ck60
-rw-r--r--src/tests/filesys/extended/grow-root-sm-persistence.ck9
-rw-r--r--src/tests/filesys/extended/grow-root-sm.c4
-rw-r--r--src/tests/filesys/extended/grow-root-sm.ck30
-rw-r--r--src/tests/filesys/extended/grow-seq-lg-persistence.ck7
-rw-r--r--src/tests/filesys/extended/grow-seq-lg.c5
-rw-r--r--src/tests/filesys/extended/grow-seq-lg.ck17
-rw-r--r--src/tests/filesys/extended/grow-seq-sm-persistence.ck7
-rw-r--r--src/tests/filesys/extended/grow-seq-sm.c5
-rw-r--r--src/tests/filesys/extended/grow-seq-sm.ck17
-rw-r--r--src/tests/filesys/extended/grow-seq.inc20
-rw-r--r--src/tests/filesys/extended/grow-sparse-persistence.ck6
-rw-r--r--src/tests/filesys/extended/grow-sparse.c25
-rw-r--r--src/tests/filesys/extended/grow-sparse.ck17
-rw-r--r--src/tests/filesys/extended/grow-tell-persistence.ck7
-rw-r--r--src/tests/filesys/extended/grow-tell.c32
-rw-r--r--src/tests/filesys/extended/grow-tell.ck17
-rw-r--r--src/tests/filesys/extended/grow-two-files-persistence.ck9
-rw-r--r--src/tests/filesys/extended/grow-two-files.c62
-rw-r--r--src/tests/filesys/extended/grow-two-files.ck23
-rw-r--r--src/tests/filesys/extended/mk-tree.c67
-rw-r--r--src/tests/filesys/extended/mk-tree.h6
-rw-r--r--src/tests/filesys/extended/syn-rw-persistence.ck8
-rw-r--r--src/tests/filesys/extended/syn-rw.c35
-rw-r--r--src/tests/filesys/extended/syn-rw.ck20
-rw-r--r--src/tests/filesys/extended/syn-rw.h9
-rw-r--r--src/tests/filesys/extended/tar.c250
-rw-r--r--src/tests/filesys/seq-test.c37
-rw-r--r--src/tests/filesys/seq-test.h11
121 files changed, 2554 insertions, 0 deletions
diff --git a/src/tests/filesys/Grading.no-vm b/src/tests/filesys/Grading.no-vm
new file mode 100644
index 0000000..ee98fc1
--- /dev/null
+++ b/src/tests/filesys/Grading.no-vm
@@ -0,0 +1,18 @@
+# Percentage of the testing point total designated for each set of
+# tests.
+
+# This project is primarily about implementing the file system, but
+# all the previous functionality should work too. It's not too easy
+# to screw it up, thus the emphasis.
+
+# 65% for extended file system features.
+30% tests/filesys/extended/Rubric.functionality
+15% tests/filesys/extended/Rubric.robustness
+20% tests/filesys/extended/Rubric.persistence
+
+# 20% to not break the provided file system features.
+20% tests/filesys/base/Rubric
+
+# 15% for the rest.
+10% tests/userprog/Rubric.functionality
+5% tests/userprog/Rubric.robustness
diff --git a/src/tests/filesys/Grading.with-vm b/src/tests/filesys/Grading.with-vm
new file mode 100644
index 0000000..e7c041e
--- /dev/null
+++ b/src/tests/filesys/Grading.with-vm
@@ -0,0 +1,22 @@
+# Percentage of the testing point total designated for each set of
+# tests.
+
+# This project is primarily about implementing the file system, but
+# all the previous functionality should work too. It's not too easy
+# to screw it up, thus the emphasis.
+
+# 65% for extended file system features.
+30% tests/filesys/extended/Rubric.functionality
+15% tests/filesys/extended/Rubric.robustness
+20% tests/filesys/extended/Rubric.persistence
+
+# 20% to not break the provided file system features.
+20% tests/filesys/base/Rubric
+
+# 15% for the rest.
+10% tests/userprog/Rubric.functionality
+5% tests/userprog/Rubric.robustness
+
+# Up to 10% bonus for working VM functionality.
+8% tests/vm/Rubric.functionality
+2% tests/vm/Rubric.robustness
diff --git a/src/tests/filesys/base/Make.tests b/src/tests/filesys/base/Make.tests
new file mode 100644
index 0000000..e475222
--- /dev/null
+++ b/src/tests/filesys/base/Make.tests
@@ -0,0 +1,18 @@
+# -*- makefile -*-
+
+tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create \
+lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full \
+sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write)
+
+tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix \
+tests/filesys/base/,child-syn-read child-syn-wrt)
+
+$(foreach prog,$(tests/filesys/base_PROGS), \
+ $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
+$(foreach prog,$(tests/filesys/base_TESTS), \
+ $(eval $(prog)_SRC += tests/main.c))
+
+tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read
+tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt
+
+tests/filesys/base/syn-read.output: TIMEOUT = 300
diff --git a/src/tests/filesys/base/Rubric b/src/tests/filesys/base/Rubric
new file mode 100644
index 0000000..49a9d15
--- /dev/null
+++ b/src/tests/filesys/base/Rubric
@@ -0,0 +1,19 @@
+Functionality of base file system:
+- Test basic support for small files.
+1 sm-create
+2 sm-full
+2 sm-random
+2 sm-seq-block
+3 sm-seq-random
+
+- Test basic support for large files.
+1 lg-create
+2 lg-full
+2 lg-random
+2 lg-seq-block
+3 lg-seq-random
+
+- Test synchronized multiprogram access to files.
+4 syn-read
+4 syn-write
+2 syn-remove
diff --git a/src/tests/filesys/base/child-syn-read.c b/src/tests/filesys/base/child-syn-read.c
new file mode 100644
index 0000000..77a5e26
--- /dev/null
+++ b/src/tests/filesys/base/child-syn-read.c
@@ -0,0 +1,44 @@
+/* Child process for syn-read test.
+ Reads the contents of a test file a byte at a time, in the
+ hope that this will take long enough that we can get a
+ significant amount of contention in the kernel file system
+ code. */
+
+#include <random.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/filesys/base/syn-read.h"
+
+const char *test_name = "child-syn-read";
+
+static char buf[BUF_SIZE];
+
+int
+main (int argc, const char *argv[])
+{
+ int child_idx;
+ int fd;
+ size_t i;
+
+ quiet = true;
+
+ CHECK (argc == 2, "argc must be 2, actually %d", argc);
+ child_idx = atoi (argv[1]);
+
+ random_init (0);
+ random_bytes (buf, sizeof buf);
+
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ for (i = 0; i < sizeof buf; i++)
+ {
+ char c;
+ CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name);
+ compare_bytes (&c, buf + i, 1, i, file_name);
+ }
+ close (fd);
+
+ return child_idx;
+}
+
diff --git a/src/tests/filesys/base/child-syn-wrt.c b/src/tests/filesys/base/child-syn-wrt.c
new file mode 100644
index 0000000..1b52584
--- /dev/null
+++ b/src/tests/filesys/base/child-syn-wrt.c
@@ -0,0 +1,35 @@
+/* Child process for syn-read test.
+ Writes into part of a test file. Other processes will be
+ writing into other parts at the same time. */
+
+#include <random.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/filesys/base/syn-write.h"
+
+char buf[BUF_SIZE];
+
+int
+main (int argc, char *argv[])
+{
+ int child_idx;
+ int fd;
+
+ quiet = true;
+
+ CHECK (argc == 2, "argc must be 2, actually %d", argc);
+ child_idx = atoi (argv[1]);
+
+ random_init (0);
+ random_bytes (buf, sizeof buf);
+
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ seek (fd, CHUNK_SIZE * child_idx);
+ CHECK (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0,
+ "write \"%s\"", file_name);
+ msg ("close \"%s\"", file_name);
+ close (fd);
+
+ return child_idx;
+}
diff --git a/src/tests/filesys/base/full.inc b/src/tests/filesys/base/full.inc
new file mode 100644
index 0000000..38a0396
--- /dev/null
+++ b/src/tests/filesys/base/full.inc
@@ -0,0 +1,20 @@
+/* -*- c -*- */
+
+#include "tests/filesys/seq-test.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+static size_t
+return_test_size (void)
+{
+ return TEST_SIZE;
+}
+
+void
+test_main (void)
+{
+ seq_test ("quux",
+ buf, sizeof buf, sizeof buf,
+ return_test_size, NULL);
+}
diff --git a/src/tests/filesys/base/lg-create.c b/src/tests/filesys/base/lg-create.c
new file mode 100644
index 0000000..5c45eee
--- /dev/null
+++ b/src/tests/filesys/base/lg-create.c
@@ -0,0 +1,5 @@
+/* Tests that create properly zeros out the contents of a fairly
+ large file. */
+
+#define TEST_SIZE 75678
+#include "tests/filesys/create.inc"
diff --git a/src/tests/filesys/base/lg-create.ck b/src/tests/filesys/base/lg-create.ck
new file mode 100644
index 0000000..86b2c51
--- /dev/null
+++ b/src/tests/filesys/base/lg-create.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-create) begin
+(lg-create) create "blargle"
+(lg-create) open "blargle" for verification
+(lg-create) verified contents of "blargle"
+(lg-create) close "blargle"
+(lg-create) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/lg-full.c b/src/tests/filesys/base/lg-full.c
new file mode 100644
index 0000000..5f7234d
--- /dev/null
+++ b/src/tests/filesys/base/lg-full.c
@@ -0,0 +1,6 @@
+/* Writes out the contents of a fairly large file all at once,
+ and then reads it back to make sure that it was written
+ properly. */
+
+#define TEST_SIZE 75678
+#include "tests/filesys/base/full.inc"
diff --git a/src/tests/filesys/base/lg-full.ck b/src/tests/filesys/base/lg-full.ck
new file mode 100644
index 0000000..ee6c7f9
--- /dev/null
+++ b/src/tests/filesys/base/lg-full.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-full) begin
+(lg-full) create "quux"
+(lg-full) open "quux"
+(lg-full) writing "quux"
+(lg-full) close "quux"
+(lg-full) open "quux" for verification
+(lg-full) verified contents of "quux"
+(lg-full) close "quux"
+(lg-full) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/lg-random.c b/src/tests/filesys/base/lg-random.c
new file mode 100644
index 0000000..b6f8873
--- /dev/null
+++ b/src/tests/filesys/base/lg-random.c
@@ -0,0 +1,7 @@
+/* Writes out the content of a fairly large file in random order,
+ then reads it back in random order to verify that it was
+ written properly. */
+
+#define BLOCK_SIZE 512
+#define TEST_SIZE (512 * 150)
+#include "tests/filesys/base/random.inc"
diff --git a/src/tests/filesys/base/lg-random.ck b/src/tests/filesys/base/lg-random.ck
new file mode 100644
index 0000000..dd9f1dd
--- /dev/null
+++ b/src/tests/filesys/base/lg-random.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-random) begin
+(lg-random) create "bazzle"
+(lg-random) open "bazzle"
+(lg-random) write "bazzle" in random order
+(lg-random) read "bazzle" in random order
+(lg-random) close "bazzle"
+(lg-random) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/lg-seq-block.c b/src/tests/filesys/base/lg-seq-block.c
new file mode 100644
index 0000000..580c30b
--- /dev/null
+++ b/src/tests/filesys/base/lg-seq-block.c
@@ -0,0 +1,7 @@
+/* Writes out a fairly large file sequentially, one fixed-size
+ block at a time, then reads it back to verify that it was
+ written properly. */
+
+#define TEST_SIZE 75678
+#define BLOCK_SIZE 513
+#include "tests/filesys/base/seq-block.inc"
diff --git a/src/tests/filesys/base/lg-seq-block.ck b/src/tests/filesys/base/lg-seq-block.ck
new file mode 100644
index 0000000..b789081
--- /dev/null
+++ b/src/tests/filesys/base/lg-seq-block.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-seq-block) begin
+(lg-seq-block) create "noodle"
+(lg-seq-block) open "noodle"
+(lg-seq-block) writing "noodle"
+(lg-seq-block) close "noodle"
+(lg-seq-block) open "noodle" for verification
+(lg-seq-block) verified contents of "noodle"
+(lg-seq-block) close "noodle"
+(lg-seq-block) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/lg-seq-random.c b/src/tests/filesys/base/lg-seq-random.c
new file mode 100644
index 0000000..fbb6bba
--- /dev/null
+++ b/src/tests/filesys/base/lg-seq-random.c
@@ -0,0 +1,6 @@
+/* Writes out a fairly large file sequentially, one random-sized
+ block at a time, then reads it back to verify that it was
+ written properly. */
+
+#define TEST_SIZE 75678
+#include "tests/filesys/base/seq-random.inc"
diff --git a/src/tests/filesys/base/lg-seq-random.ck b/src/tests/filesys/base/lg-seq-random.ck
new file mode 100644
index 0000000..6b2dc82
--- /dev/null
+++ b/src/tests/filesys/base/lg-seq-random.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-seq-random) begin
+(lg-seq-random) create "nibble"
+(lg-seq-random) open "nibble"
+(lg-seq-random) writing "nibble"
+(lg-seq-random) close "nibble"
+(lg-seq-random) open "nibble" for verification
+(lg-seq-random) verified contents of "nibble"
+(lg-seq-random) close "nibble"
+(lg-seq-random) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/random.inc b/src/tests/filesys/base/random.inc
new file mode 100644
index 0000000..eeeea68
--- /dev/null
+++ b/src/tests/filesys/base/random.inc
@@ -0,0 +1,59 @@
+/* -*- c -*- */
+
+#include <random.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#if TEST_SIZE % BLOCK_SIZE != 0
+#error TEST_SIZE must be a multiple of BLOCK_SIZE
+#endif
+
+#define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE)
+
+char buf[TEST_SIZE];
+int order[BLOCK_CNT];
+
+void
+test_main (void)
+{
+ const char *file_name = "bazzle";
+ int fd;
+ size_t i;
+
+ random_init (57);
+ random_bytes (buf, sizeof buf);
+
+ for (i = 0; i < BLOCK_CNT; i++)
+ order[i] = i;
+
+ CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+
+ msg ("write \"%s\" in random order", file_name);
+ shuffle (order, BLOCK_CNT, sizeof *order);
+ for (i = 0; i < BLOCK_CNT; i++)
+ {
+ size_t ofs = BLOCK_SIZE * order[i];
+ seek (fd, ofs);
+ if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE)
+ fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
+ }
+
+ msg ("read \"%s\" in random order", file_name);
+ shuffle (order, BLOCK_CNT, sizeof *order);
+ for (i = 0; i < BLOCK_CNT; i++)
+ {
+ char block[BLOCK_SIZE];
+ size_t ofs = BLOCK_SIZE * order[i];
+ seek (fd, ofs);
+ if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE)
+ fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
+ compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name);
+ }
+
+ msg ("close \"%s\"", file_name);
+ close (fd);
+}
diff --git a/src/tests/filesys/base/seq-block.inc b/src/tests/filesys/base/seq-block.inc
new file mode 100644
index 0000000..d4c1f57
--- /dev/null
+++ b/src/tests/filesys/base/seq-block.inc
@@ -0,0 +1,20 @@
+/* -*- c -*- */
+
+#include "tests/filesys/seq-test.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+static size_t
+return_block_size (void)
+{
+ return BLOCK_SIZE;
+}
+
+void
+test_main (void)
+{
+ seq_test ("noodle",
+ buf, sizeof buf, sizeof buf,
+ return_block_size, NULL);
+}
diff --git a/src/tests/filesys/base/seq-random.inc b/src/tests/filesys/base/seq-random.inc
new file mode 100644
index 0000000..a4da4c5
--- /dev/null
+++ b/src/tests/filesys/base/seq-random.inc
@@ -0,0 +1,22 @@
+/* -*- c -*- */
+
+#include <random.h>
+#include "tests/filesys/seq-test.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+static size_t
+return_random (void)
+{
+ return random_ulong () % 1031 + 1;
+}
+
+void
+test_main (void)
+{
+ random_init (-1);
+ seq_test ("nibble",
+ buf, sizeof buf, sizeof buf,
+ return_random, NULL);
+}
diff --git a/src/tests/filesys/base/sm-create.c b/src/tests/filesys/base/sm-create.c
new file mode 100644
index 0000000..6b97ac1
--- /dev/null
+++ b/src/tests/filesys/base/sm-create.c
@@ -0,0 +1,5 @@
+/* Tests that create properly zeros out the contents of a fairly
+ small file. */
+
+#define TEST_SIZE 5678
+#include "tests/filesys/create.inc"
diff --git a/src/tests/filesys/base/sm-create.ck b/src/tests/filesys/base/sm-create.ck
new file mode 100644
index 0000000..8ca80dc
--- /dev/null
+++ b/src/tests/filesys/base/sm-create.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-create) begin
+(sm-create) create "blargle"
+(sm-create) open "blargle" for verification
+(sm-create) verified contents of "blargle"
+(sm-create) close "blargle"
+(sm-create) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/sm-full.c b/src/tests/filesys/base/sm-full.c
new file mode 100644
index 0000000..23ff3d4
--- /dev/null
+++ b/src/tests/filesys/base/sm-full.c
@@ -0,0 +1,6 @@
+/* Writes out the contents of a fairly small file all at once,
+ and then reads it back to make sure that it was written
+ properly. */
+
+#define TEST_SIZE 5678
+#include "tests/filesys/base/full.inc"
diff --git a/src/tests/filesys/base/sm-full.ck b/src/tests/filesys/base/sm-full.ck
new file mode 100644
index 0000000..2e0eb36
--- /dev/null
+++ b/src/tests/filesys/base/sm-full.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-full) begin
+(sm-full) create "quux"
+(sm-full) open "quux"
+(sm-full) writing "quux"
+(sm-full) close "quux"
+(sm-full) open "quux" for verification
+(sm-full) verified contents of "quux"
+(sm-full) close "quux"
+(sm-full) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/sm-random.c b/src/tests/filesys/base/sm-random.c
new file mode 100644
index 0000000..42d670f
--- /dev/null
+++ b/src/tests/filesys/base/sm-random.c
@@ -0,0 +1,7 @@
+/* Writes out the content of a fairly small file in random order,
+ then reads it back in random order to verify that it was
+ written properly. */
+
+#define BLOCK_SIZE 13
+#define TEST_SIZE (13 * 123)
+#include "tests/filesys/base/random.inc"
diff --git a/src/tests/filesys/base/sm-random.ck b/src/tests/filesys/base/sm-random.ck
new file mode 100644
index 0000000..bda049d
--- /dev/null
+++ b/src/tests/filesys/base/sm-random.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-random) begin
+(sm-random) create "bazzle"
+(sm-random) open "bazzle"
+(sm-random) write "bazzle" in random order
+(sm-random) read "bazzle" in random order
+(sm-random) close "bazzle"
+(sm-random) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/sm-seq-block.c b/src/tests/filesys/base/sm-seq-block.c
new file mode 100644
index 0000000..e368327
--- /dev/null
+++ b/src/tests/filesys/base/sm-seq-block.c
@@ -0,0 +1,7 @@
+/* Writes out a fairly small file sequentially, one fixed-size
+ block at a time, then reads it back to verify that it was
+ written properly. */
+
+#define TEST_SIZE 5678
+#define BLOCK_SIZE 513
+#include "tests/filesys/base/seq-block.inc"
diff --git a/src/tests/filesys/base/sm-seq-block.ck b/src/tests/filesys/base/sm-seq-block.ck
new file mode 100644
index 0000000..0e2939d
--- /dev/null
+++ b/src/tests/filesys/base/sm-seq-block.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-seq-block) begin
+(sm-seq-block) create "noodle"
+(sm-seq-block) open "noodle"
+(sm-seq-block) writing "noodle"
+(sm-seq-block) close "noodle"
+(sm-seq-block) open "noodle" for verification
+(sm-seq-block) verified contents of "noodle"
+(sm-seq-block) close "noodle"
+(sm-seq-block) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/sm-seq-random.c b/src/tests/filesys/base/sm-seq-random.c
new file mode 100644
index 0000000..89e5b71
--- /dev/null
+++ b/src/tests/filesys/base/sm-seq-random.c
@@ -0,0 +1,6 @@
+/* Writes out a fairly large file sequentially, one random-sized
+ block at a time, then reads it back to verify that it was
+ written properly. */
+
+#define TEST_SIZE 5678
+#include "tests/filesys/base/seq-random.inc"
diff --git a/src/tests/filesys/base/sm-seq-random.ck b/src/tests/filesys/base/sm-seq-random.ck
new file mode 100644
index 0000000..2fb368b
--- /dev/null
+++ b/src/tests/filesys/base/sm-seq-random.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-seq-random) begin
+(sm-seq-random) create "nibble"
+(sm-seq-random) open "nibble"
+(sm-seq-random) writing "nibble"
+(sm-seq-random) close "nibble"
+(sm-seq-random) open "nibble" for verification
+(sm-seq-random) verified contents of "nibble"
+(sm-seq-random) close "nibble"
+(sm-seq-random) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/syn-read.c b/src/tests/filesys/base/syn-read.c
new file mode 100644
index 0000000..7c36a42
--- /dev/null
+++ b/src/tests/filesys/base/syn-read.c
@@ -0,0 +1,31 @@
+/* Spawns 10 child processes, all of which read from the same
+ file and make sure that the contents are what they should
+ be. */
+
+#include <random.h>
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+#include "tests/filesys/base/syn-read.h"
+
+static char buf[BUF_SIZE];
+
+#define CHILD_CNT 10
+
+void
+test_main (void)
+{
+ pid_t children[CHILD_CNT];
+ int fd;
+
+ CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name);
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ random_bytes (buf, sizeof buf);
+ CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name);
+ msg ("close \"%s\"", file_name);
+ close (fd);
+
+ exec_children ("child-syn-read", children, CHILD_CNT);
+ wait_children (children, CHILD_CNT);
+}
diff --git a/src/tests/filesys/base/syn-read.ck b/src/tests/filesys/base/syn-read.ck
new file mode 100644
index 0000000..e2f68e8
--- /dev/null
+++ b/src/tests/filesys/base/syn-read.ck
@@ -0,0 +1,33 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(syn-read) begin
+(syn-read) create "data"
+(syn-read) open "data"
+(syn-read) write "data"
+(syn-read) close "data"
+(syn-read) exec child 1 of 10: "child-syn-read 0"
+(syn-read) exec child 2 of 10: "child-syn-read 1"
+(syn-read) exec child 3 of 10: "child-syn-read 2"
+(syn-read) exec child 4 of 10: "child-syn-read 3"
+(syn-read) exec child 5 of 10: "child-syn-read 4"
+(syn-read) exec child 6 of 10: "child-syn-read 5"
+(syn-read) exec child 7 of 10: "child-syn-read 6"
+(syn-read) exec child 8 of 10: "child-syn-read 7"
+(syn-read) exec child 9 of 10: "child-syn-read 8"
+(syn-read) exec child 10 of 10: "child-syn-read 9"
+(syn-read) wait for child 1 of 10 returned 0 (expected 0)
+(syn-read) wait for child 2 of 10 returned 1 (expected 1)
+(syn-read) wait for child 3 of 10 returned 2 (expected 2)
+(syn-read) wait for child 4 of 10 returned 3 (expected 3)
+(syn-read) wait for child 5 of 10 returned 4 (expected 4)
+(syn-read) wait for child 6 of 10 returned 5 (expected 5)
+(syn-read) wait for child 7 of 10 returned 6 (expected 6)
+(syn-read) wait for child 8 of 10 returned 7 (expected 7)
+(syn-read) wait for child 9 of 10 returned 8 (expected 8)
+(syn-read) wait for child 10 of 10 returned 9 (expected 9)
+(syn-read) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/syn-read.h b/src/tests/filesys/base/syn-read.h
new file mode 100644
index 0000000..bff8082
--- /dev/null
+++ b/src/tests/filesys/base/syn-read.h
@@ -0,0 +1,7 @@
+#ifndef TESTS_FILESYS_BASE_SYN_READ_H
+#define TESTS_FILESYS_BASE_SYN_READ_H
+
+#define BUF_SIZE 1024
+static const char file_name[] = "data";
+
+#endif /* tests/filesys/base/syn-read.h */
diff --git a/src/tests/filesys/base/syn-remove.c b/src/tests/filesys/base/syn-remove.c
new file mode 100644
index 0000000..c9ba110
--- /dev/null
+++ b/src/tests/filesys/base/syn-remove.c
@@ -0,0 +1,30 @@
+/* Verifies that a deleted file may still be written to and read
+ from. */
+
+#include <random.h>
+#include <string.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+char buf1[1234];
+char buf2[1234];
+
+void
+test_main (void)
+{
+ const char *file_name = "deleteme";
+ int fd;
+
+ CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ CHECK (remove (file_name), "remove \"%s\"", file_name);
+ random_bytes (buf1, sizeof buf1);
+ CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name);
+ msg ("seek \"%s\" to 0", file_name);
+ seek (fd, 0);
+ CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name);
+ compare_bytes (buf2, buf1, sizeof buf1, 0, file_name);
+ msg ("close \"%s\"", file_name);
+ close (fd);
+}
diff --git a/src/tests/filesys/base/syn-remove.ck b/src/tests/filesys/base/syn-remove.ck
new file mode 100644
index 0000000..16ff11e
--- /dev/null
+++ b/src/tests/filesys/base/syn-remove.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(syn-remove) begin
+(syn-remove) create "deleteme"
+(syn-remove) open "deleteme"
+(syn-remove) remove "deleteme"
+(syn-remove) write "deleteme"
+(syn-remove) seek "deleteme" to 0
+(syn-remove) read "deleteme"
+(syn-remove) close "deleteme"
+(syn-remove) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/syn-write.c b/src/tests/filesys/base/syn-write.c
new file mode 100644
index 0000000..1439862
--- /dev/null
+++ b/src/tests/filesys/base/syn-write.c
@@ -0,0 +1,31 @@
+/* Spawns several child processes to write out different parts of
+ the contents of a file and waits for them to finish. Then
+ reads back the file and verifies its contents. */
+
+#include <random.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+#include "tests/filesys/base/syn-write.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+char buf1[BUF_SIZE];
+char buf2[BUF_SIZE];
+
+void
+test_main (void)
+{
+ pid_t children[CHILD_CNT];
+ int fd;
+
+ CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
+
+ exec_children ("child-syn-wrt", children, CHILD_CNT);
+ wait_children (children, CHILD_CNT);
+
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name);
+ random_bytes (buf2, sizeof buf2);
+ compare_bytes (buf1, buf2, sizeof buf1, 0, file_name);
+}
diff --git a/src/tests/filesys/base/syn-write.ck b/src/tests/filesys/base/syn-write.ck
new file mode 100644
index 0000000..629a7a2
--- /dev/null
+++ b/src/tests/filesys/base/syn-write.ck
@@ -0,0 +1,32 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(syn-write) begin
+(syn-write) create "stuff"
+(syn-write) exec child 1 of 10: "child-syn-wrt 0"
+(syn-write) exec child 2 of 10: "child-syn-wrt 1"
+(syn-write) exec child 3 of 10: "child-syn-wrt 2"
+(syn-write) exec child 4 of 10: "child-syn-wrt 3"
+(syn-write) exec child 5 of 10: "child-syn-wrt 4"
+(syn-write) exec child 6 of 10: "child-syn-wrt 5"
+(syn-write) exec child 7 of 10: "child-syn-wrt 6"
+(syn-write) exec child 8 of 10: "child-syn-wrt 7"
+(syn-write) exec child 9 of 10: "child-syn-wrt 8"
+(syn-write) exec child 10 of 10: "child-syn-wrt 9"
+(syn-write) wait for child 1 of 10 returned 0 (expected 0)
+(syn-write) wait for child 2 of 10 returned 1 (expected 1)
+(syn-write) wait for child 3 of 10 returned 2 (expected 2)
+(syn-write) wait for child 4 of 10 returned 3 (expected 3)
+(syn-write) wait for child 5 of 10 returned 4 (expected 4)
+(syn-write) wait for child 6 of 10 returned 5 (expected 5)
+(syn-write) wait for child 7 of 10 returned 6 (expected 6)
+(syn-write) wait for child 8 of 10 returned 7 (expected 7)
+(syn-write) wait for child 9 of 10 returned 8 (expected 8)
+(syn-write) wait for child 10 of 10 returned 9 (expected 9)
+(syn-write) open "stuff"
+(syn-write) read "stuff"
+(syn-write) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/syn-write.h b/src/tests/filesys/base/syn-write.h
new file mode 100644
index 0000000..07a6d5a
--- /dev/null
+++ b/src/tests/filesys/base/syn-write.h
@@ -0,0 +1,9 @@
+#ifndef TESTS_FILESYS_BASE_SYN_WRITE_H
+#define TESTS_FILESYS_BASE_SYN_WRITE_H
+
+#define CHILD_CNT 10
+#define CHUNK_SIZE 512
+#define BUF_SIZE (CHILD_CNT * CHUNK_SIZE)
+static const char file_name[] = "stuff";
+
+#endif /* tests/filesys/base/syn-write.h */
diff --git a/src/tests/filesys/create.inc b/src/tests/filesys/create.inc
new file mode 100644
index 0000000..4baf771
--- /dev/null
+++ b/src/tests/filesys/create.inc
@@ -0,0 +1,15 @@
+/* -*- c -*- */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+void
+test_main (void)
+{
+ const char *file_name = "blargle";
+ CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
+ check_file (file_name, buf, TEST_SIZE);
+}
diff --git a/src/tests/filesys/extended/Make.tests b/src/tests/filesys/extended/Make.tests
new file mode 100644
index 0000000..75a872b
--- /dev/null
+++ b/src/tests/filesys/extended/Make.tests
@@ -0,0 +1,61 @@
+# -*- makefile -*-
+
+raw_tests = dir-empty-name dir-mk-tree dir-mkdir dir-open \
+dir-over-file dir-rm-cwd dir-rm-parent dir-rm-root dir-rm-tree \
+dir-rmdir dir-under-file dir-vine grow-create grow-dir-lg \
+grow-file-size grow-root-lg grow-root-sm grow-seq-lg grow-seq-sm \
+grow-sparse grow-tell grow-two-files syn-rw
+
+tests/filesys/extended_TESTS = $(patsubst %,tests/filesys/extended/%,$(raw_tests))
+tests/filesys/extended_EXTRA_GRADES = $(patsubst %,tests/filesys/extended/%-persistence,$(raw_tests))
+
+tests/filesys/extended_PROGS = $(tests/filesys/extended_TESTS) \
+tests/filesys/extended/child-syn-rw tests/filesys/extended/tar
+
+$(foreach prog,$(tests/filesys/extended_PROGS), \
+ $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
+$(foreach prog,$(tests/filesys/extended_TESTS), \
+ $(eval $(prog)_SRC += tests/main.c))
+$(foreach prog,$(tests/filesys/extended_TESTS), \
+ $(eval $(prog)_PUTFILES += tests/filesys/extended/tar))
+# The version of GNU make 3.80 on vine barfs if this is split at
+# the last comma.
+$(foreach test,$(tests/filesys/extended_TESTS),$(eval $(test).output: FSDISK = tmp.dsk))
+
+tests/filesys/extended/dir-mk-tree_SRC += tests/filesys/extended/mk-tree.c
+tests/filesys/extended/dir-rm-tree_SRC += tests/filesys/extended/mk-tree.c
+
+tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw
+
+tests/filesys/extended/dir-vine.output: TIMEOUT = 150
+
+GETTIMEOUT = 60
+
+GETCMD = pintos -v -k -T $(GETTIMEOUT)
+GETCMD += $(PINTOSOPTS)
+GETCMD += $(SIMULATOR)
+GETCMD += --fs-disk=$(FSDISK)
+GETCMD += -g fs.tar -a $(TEST).tar
+ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
+GETCMD += --swap-disk=4
+endif
+GETCMD += -- -q
+GETCMD += $(KERNELFLAGS)
+GETCMD += run 'tar fs.tar /'
+GETCMD += < /dev/null
+GETCMD += 2> $(TEST)-persistence.errors $(if $(VERBOSE),|tee,>) $(TEST)-persistence.output
+
+tests/filesys/extended/%.output: os.dsk
+ rm -f tmp.dsk
+ pintos-mkdisk tmp.dsk 2
+ $(TESTCMD)
+ $(GETCMD)
+ rm -f tmp.dsk
+$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.output: tests/filesys/extended/$(raw_test).output))
+$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.result: tests/filesys/extended/$(raw_test).result))
+
+TARS = $(addsuffix .tar,$(tests/filesys/extended_TESTS))
+
+clean::
+ rm -f $(TARS)
+ rm -f tests/filesys/extended/can-rmdir-cwd
diff --git a/src/tests/filesys/extended/Rubric.functionality b/src/tests/filesys/extended/Rubric.functionality
new file mode 100644
index 0000000..91ed6f0
--- /dev/null
+++ b/src/tests/filesys/extended/Rubric.functionality
@@ -0,0 +1,26 @@
+Functionality of extended file system:
+- Test directory support.
+1 dir-mkdir
+3 dir-mk-tree
+
+1 dir-rmdir
+3 dir-rm-tree
+
+5 dir-vine
+
+- Test file growth.
+1 grow-create
+1 grow-seq-sm
+3 grow-seq-lg
+3 grow-sparse
+3 grow-two-files
+1 grow-tell
+1 grow-file-size
+
+- Test directory growth.
+1 grow-dir-lg
+1 grow-root-sm
+1 grow-root-lg
+
+- Test writing from multiple processes.
+5 syn-rw
diff --git a/src/tests/filesys/extended/Rubric.persistence b/src/tests/filesys/extended/Rubric.persistence
new file mode 100644
index 0000000..405620a
--- /dev/null
+++ b/src/tests/filesys/extended/Rubric.persistence
@@ -0,0 +1,24 @@
+Persistence of file system:
+1 dir-empty-name-persistence
+1 dir-mk-tree-persistence
+1 dir-mkdir-persistence
+1 dir-open-persistence
+1 dir-over-file-persistence
+1 dir-rm-cwd-persistence
+1 dir-rm-parent-persistence
+1 dir-rm-root-persistence
+1 dir-rm-tree-persistence
+1 dir-rmdir-persistence
+1 dir-under-file-persistence
+1 dir-vine-persistence
+1 grow-create-persistence
+1 grow-dir-lg-persistence
+1 grow-file-size-persistence
+1 grow-root-lg-persistence
+1 grow-root-sm-persistence
+1 grow-seq-lg-persistence
+1 grow-seq-sm-persistence
+1 grow-sparse-persistence
+1 grow-tell-persistence
+1 grow-two-files-persistence
+1 syn-rw-persistence
diff --git a/src/tests/filesys/extended/Rubric.robustness b/src/tests/filesys/extended/Rubric.robustness
new file mode 100644
index 0000000..fb9f32f
--- /dev/null
+++ b/src/tests/filesys/extended/Rubric.robustness
@@ -0,0 +1,9 @@
+Robustness of file system:
+1 dir-empty-name
+1 dir-open
+1 dir-over-file
+1 dir-under-file
+
+3 dir-rm-cwd
+2 dir-rm-parent
+1 dir-rm-root
diff --git a/src/tests/filesys/extended/child-syn-rw.c b/src/tests/filesys/extended/child-syn-rw.c
new file mode 100644
index 0000000..0e2217d
--- /dev/null
+++ b/src/tests/filesys/extended/child-syn-rw.c
@@ -0,0 +1,53 @@
+/* Child process for syn-rw.
+ Reads from a file created by our parent process, which is
+ growing it. We loop until we've read the whole file
+ successfully. Many iterations through the loop will return 0
+ bytes, because the file has not grown in the meantime. That
+ is, we are "busy waiting" for the file to grow.
+ (This test could be improved by adding a "yield" system call
+ and calling yield whenever we receive a 0-byte read.) */
+
+#include <random.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include "tests/filesys/extended/syn-rw.h"
+#include "tests/lib.h"
+
+const char *test_name = "child-syn-rw";
+
+static char buf1[BUF_SIZE];
+static char buf2[BUF_SIZE];
+
+int
+main (int argc, const char *argv[])
+{
+ int child_idx;
+ int fd;
+ size_t ofs;
+
+ quiet = true;
+
+ CHECK (argc == 2, "argc must be 2, actually %d", argc);
+ child_idx = atoi (argv[1]);
+
+ random_init (0);
+ random_bytes (buf1, sizeof buf1);
+
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ ofs = 0;
+ while (ofs < sizeof buf2)
+ {
+ int bytes_read = read (fd, buf2 + ofs, sizeof buf2 - ofs);
+ CHECK (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs),
+ "%zu-byte read on \"%s\" returned invalid value of %d",
+ sizeof buf2 - ofs, file_name, bytes_read);
+ if (bytes_read > 0)
+ {
+ compare_bytes (buf2 + ofs, buf1 + ofs, bytes_read, ofs, file_name);
+ ofs += bytes_read;
+ }
+ }
+ close (fd);
+
+ return child_idx;
+}
diff --git a/src/tests/filesys/extended/dir-empty-name-persistence.ck b/src/tests/filesys/extended/dir-empty-name-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/src/tests/filesys/extended/dir-empty-name-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({});
+pass;
diff --git a/src/tests/filesys/extended/dir-empty-name.c b/src/tests/filesys/extended/dir-empty-name.c
new file mode 100644
index 0000000..c4859d2
--- /dev/null
+++ b/src/tests/filesys/extended/dir-empty-name.c
@@ -0,0 +1,12 @@
+/* Tries to create a directory named as the empty string,
+ which must return failure. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ CHECK (!mkdir (""), "mkdir \"\" (must return false)");
+}
diff --git a/src/tests/filesys/extended/dir-empty-name.ck b/src/tests/filesys/extended/dir-empty-name.ck
new file mode 100644
index 0000000..d6c5621
--- /dev/null
+++ b/src/tests/filesys/extended/dir-empty-name.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-empty-name) begin
+(dir-empty-name) mkdir "" (must return false)
+(dir-empty-name) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-mk-tree-persistence.ck b/src/tests/filesys/extended/dir-mk-tree-persistence.ck
new file mode 100644
index 0000000..fb16afd
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mk-tree-persistence.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+my ($tree);
+for my $a (0...3) {
+ for my $b (0...2) {
+ for my $c (0...2) {
+ for my $d (0...3) {
+ $tree->{$a}{$b}{$c}{$d} = [''];
+ }
+ }
+ }
+}
+check_archive ($tree);
+pass;
diff --git a/src/tests/filesys/extended/dir-mk-tree.c b/src/tests/filesys/extended/dir-mk-tree.c
new file mode 100644
index 0000000..a714ff3
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mk-tree.c
@@ -0,0 +1,12 @@
+/* Creates directories /0/0/0 through /3/2/2 and creates files in
+ the leaf directories. */
+
+#include "tests/filesys/extended/mk-tree.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ make_tree (4, 3, 3, 4);
+}
+
diff --git a/src/tests/filesys/extended/dir-mk-tree.ck b/src/tests/filesys/extended/dir-mk-tree.ck
new file mode 100644
index 0000000..a8507e2
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mk-tree.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-mk-tree) begin
+(dir-mk-tree) creating /0/0/0/0 through /3/2/2/3...
+(dir-mk-tree) open "/0/2/0/3"
+(dir-mk-tree) close "/0/2/0/3"
+(dir-mk-tree) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-mkdir-persistence.ck b/src/tests/filesys/extended/dir-mkdir-persistence.ck
new file mode 100644
index 0000000..7682900
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mkdir-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({'a' => {'b' => ["\0" x 512]}});
+pass;
diff --git a/src/tests/filesys/extended/dir-mkdir.c b/src/tests/filesys/extended/dir-mkdir.c
new file mode 100644
index 0000000..994f41c
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mkdir.c
@@ -0,0 +1,15 @@
+/* Tests mkdir(). */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ CHECK (mkdir ("a"), "mkdir \"a\"");
+ CHECK (create ("a/b", 512), "create \"a/b\"");
+ CHECK (chdir ("a"), "chdir \"a\"");
+ CHECK (open ("b") > 1, "open \"b\"");
+}
+
diff --git a/src/tests/filesys/extended/dir-mkdir.ck b/src/tests/filesys/extended/dir-mkdir.ck
new file mode 100644
index 0000000..4644f80
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mkdir.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-mkdir) begin
+(dir-mkdir) mkdir "a"
+(dir-mkdir) create "a/b"
+(dir-mkdir) chdir "a"
+(dir-mkdir) open "b"
+(dir-mkdir) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-open-persistence.ck b/src/tests/filesys/extended/dir-open-persistence.ck
new file mode 100644
index 0000000..26ff2f1
--- /dev/null
+++ b/src/tests/filesys/extended/dir-open-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"xyzzy" => {}});
+pass;
diff --git a/src/tests/filesys/extended/dir-open.c b/src/tests/filesys/extended/dir-open.c
new file mode 100644
index 0000000..29d18b8
--- /dev/null
+++ b/src/tests/filesys/extended/dir-open.c
@@ -0,0 +1,21 @@
+/* Opens a directory, then tries to write to it, which must
+ fail. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ int fd;
+ int retval;
+
+ CHECK (mkdir ("xyzzy"), "mkdir \"xyzzy\"");
+ CHECK ((fd = open ("xyzzy")) > 1, "open \"xyzzy\"");
+
+ msg ("write \"xyzzy\"");
+ retval = write (fd, "foobar", 6);
+ CHECK (retval == -1,
+ "write \"xyzzy\" (must return -1, actually %d)", retval);
+}
diff --git a/src/tests/filesys/extended/dir-open.ck b/src/tests/filesys/extended/dir-open.ck
new file mode 100644
index 0000000..fccc563
--- /dev/null
+++ b/src/tests/filesys/extended/dir-open.ck
@@ -0,0 +1,20 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(dir-open) begin
+(dir-open) mkdir "xyzzy"
+(dir-open) open "xyzzy"
+(dir-open) write "xyzzy"
+(dir-open) write "xyzzy" (must return -1, actually -1)
+(dir-open) end
+dir-open: exit(0)
+EOF
+(dir-open) begin
+(dir-open) mkdir "xyzzy"
+(dir-open) open "xyzzy"
+(dir-open) write "xyzzy"
+dir-open: exit(-1)
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-over-file-persistence.ck b/src/tests/filesys/extended/dir-over-file-persistence.ck
new file mode 100644
index 0000000..56b4ed2
--- /dev/null
+++ b/src/tests/filesys/extended/dir-over-file-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"abc" => {}});
+pass;
diff --git a/src/tests/filesys/extended/dir-over-file.c b/src/tests/filesys/extended/dir-over-file.c
new file mode 100644
index 0000000..cdd2c62
--- /dev/null
+++ b/src/tests/filesys/extended/dir-over-file.c
@@ -0,0 +1,13 @@
+/* Tries to create a file with the same name as an existing
+ directory, which must return failure. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ CHECK (mkdir ("abc"), "mkdir \"abc\"");
+ CHECK (!create ("abc", 0), "create \"abc\" (must return false)");
+}
diff --git a/src/tests/filesys/extended/dir-over-file.ck b/src/tests/filesys/extended/dir-over-file.ck
new file mode 100644
index 0000000..aae1c1e
--- /dev/null
+++ b/src/tests/filesys/extended/dir-over-file.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-over-file) begin
+(dir-over-file) mkdir "abc"
+(dir-over-file) create "abc" (must return false)
+(dir-over-file) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-cwd-persistence.ck b/src/tests/filesys/extended/dir-rm-cwd-persistence.ck
new file mode 100644
index 0000000..7533570
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-cwd-persistence.ck
@@ -0,0 +1,8 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+my ($cwd_removable) = read_text_file ("tests/filesys/extended/can-rmdir-cwd");
+$cwd_removable eq 'YES' || $cwd_removable eq 'NO' or die;
+check_archive ($cwd_removable eq 'YES' ? {} : {"a" => {}});
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-cwd.c b/src/tests/filesys/extended/dir-rm-cwd.c
new file mode 100644
index 0000000..78e13de
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-cwd.c
@@ -0,0 +1,75 @@
+/* Tries to remove the current directory, which may succeed or
+ fail. The requirements in each case are different; refer to
+ the assignment for details. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static int
+wrap_open (const char *name)
+{
+ static int fds[8], fd_cnt;
+ int fd, i;
+
+ CHECK ((fd = open (name)) > 1, "open \"%s\"", name);
+ for (i = 0; i < fd_cnt; i++)
+ if (fds[i] == fd)
+ fail ("fd returned is not unique");
+ fds[fd_cnt++] = fd;
+ return fd;
+}
+
+void
+test_main (void)
+{
+ int root_fd, a_fd0;
+ char name[READDIR_MAX_LEN + 1];
+
+ root_fd = wrap_open ("/");
+ CHECK (mkdir ("a"), "mkdir \"a\"");
+
+ a_fd0 = wrap_open ("/a");
+ CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
+ CHECK (inumber (root_fd) != inumber (a_fd0),
+ "\"/\" and \"/a\" must have different inumbers");
+
+ CHECK (chdir ("a"), "chdir \"a\"");
+
+ msg ("try to remove \"/a\"");
+ if (remove ("/a"))
+ {
+ msg ("remove successful");
+
+ CHECK (open ("/a") == -1, "open \"/a\" (must fail)");
+ CHECK (open (".") == -1, "open \".\" (must fail)");
+ CHECK (open ("..") == -1, "open \"..\" (must fail)");
+ CHECK (!create ("x", 512), "create \"x\" (must fail)");
+ }
+ else
+ {
+ int a_fd1, a_fd2, a_fd3;
+
+ msg ("remove failed");
+
+ CHECK (!remove ("../a"), "try to remove \"../a\" (must fail)");
+ CHECK (!remove (".././a"), "try to remove \".././a\" (must fail)");
+ CHECK (!remove ("/./a"), "try to remove \"/./a\" (must fail)");
+
+ a_fd1 = wrap_open ("/a");
+ a_fd2 = wrap_open (".");
+ CHECK (inumber (a_fd1) == inumber (a_fd2),
+ "\"/a\" and \".\" must have same inumber");
+ CHECK (inumber (root_fd) != inumber (a_fd1),
+ "\"/\" and \"/a\" must have different inumbers");
+
+ CHECK (chdir ("/a"), "chdir \"/a\"");
+ a_fd3 = wrap_open (".");
+ CHECK (inumber (a_fd3) == inumber (a_fd1),
+ "\".\" must have same inumber as before");
+
+ CHECK (chdir ("/"), "chdir \"/\"");
+ CHECK (!remove ("a"), "try to remove \"a\" (must fail: still open)");
+ }
+ CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
+}
diff --git a/src/tests/filesys/extended/dir-rm-cwd.ck b/src/tests/filesys/extended/dir-rm-cwd.ck
new file mode 100644
index 0000000..6fa4739
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-cwd.ck
@@ -0,0 +1,51 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+my ($cwd_removable) = check_expected (IGNORE_EXIT_CODES => 1,
+ {NO => <<'EOF', YES => <<'EOF'});
+(dir-rm-cwd) begin
+(dir-rm-cwd) open "/"
+(dir-rm-cwd) mkdir "a"
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) "/" and "/a" must have different inumbers
+(dir-rm-cwd) chdir "a"
+(dir-rm-cwd) try to remove "/a"
+(dir-rm-cwd) remove failed
+(dir-rm-cwd) try to remove "../a" (must fail)
+(dir-rm-cwd) try to remove ".././a" (must fail)
+(dir-rm-cwd) try to remove "/./a" (must fail)
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) open "."
+(dir-rm-cwd) "/a" and "." must have same inumber
+(dir-rm-cwd) "/" and "/a" must have different inumbers
+(dir-rm-cwd) chdir "/a"
+(dir-rm-cwd) open "."
+(dir-rm-cwd) "." must have same inumber as before
+(dir-rm-cwd) chdir "/"
+(dir-rm-cwd) try to remove "a" (must fail: still open)
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) end
+EOF
+(dir-rm-cwd) begin
+(dir-rm-cwd) open "/"
+(dir-rm-cwd) mkdir "a"
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) "/" and "/a" must have different inumbers
+(dir-rm-cwd) chdir "a"
+(dir-rm-cwd) try to remove "/a"
+(dir-rm-cwd) remove successful
+(dir-rm-cwd) open "/a" (must fail)
+(dir-rm-cwd) open "." (must fail)
+(dir-rm-cwd) open ".." (must fail)
+(dir-rm-cwd) create "x" (must fail)
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) end
+EOF
+open (CAN_RMDIR_CWD, ">tests/filesys/extended/can-rmdir-cwd")
+ or die "tests/filesys/extended/can-rmdir-cwd: create: $!\n";
+print CAN_RMDIR_CWD "$cwd_removable";
+close (CAN_RMDIR_CWD);
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-parent-persistence.ck b/src/tests/filesys/extended/dir-rm-parent-persistence.ck
new file mode 100644
index 0000000..f30b04a
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-parent-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"a" => {"b" => {}}});
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-parent.c b/src/tests/filesys/extended/dir-rm-parent.c
new file mode 100644
index 0000000..eb43f5b
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-parent.c
@@ -0,0 +1,16 @@
+/* Tries to remove a parent of the current directory. This must
+ fail, because that directory is non-empty. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ CHECK (mkdir ("a"), "mkdir \"a\"");
+ CHECK (chdir ("a"), "chdir \"a\"");
+ CHECK (mkdir ("b"), "mkdir \"b\"");
+ CHECK (chdir ("b"), "chdir \"b\"");
+ CHECK (!remove ("/a"), "remove \"/a\" (must fail)");
+}
diff --git a/src/tests/filesys/extended/dir-rm-parent.ck b/src/tests/filesys/extended/dir-rm-parent.ck
new file mode 100644
index 0000000..9fea8f2
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-parent.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-rm-parent) begin
+(dir-rm-parent) mkdir "a"
+(dir-rm-parent) chdir "a"
+(dir-rm-parent) mkdir "b"
+(dir-rm-parent) chdir "b"
+(dir-rm-parent) remove "/a" (must fail)
+(dir-rm-parent) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-root-persistence.ck b/src/tests/filesys/extended/dir-rm-root-persistence.ck
new file mode 100644
index 0000000..6315107
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-root-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"a" => ["\0" x 243]});
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-root.c b/src/tests/filesys/extended/dir-rm-root.c
new file mode 100644
index 0000000..c47f1eb
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-root.c
@@ -0,0 +1,13 @@
+/* Try to remove the root directory.
+ This must fail. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ CHECK (!remove ("/"), "remove \"/\" (must fail)");
+ CHECK (create ("/a", 243), "create \"/a\"");
+}
diff --git a/src/tests/filesys/extended/dir-rm-root.ck b/src/tests/filesys/extended/dir-rm-root.ck
new file mode 100644
index 0000000..8a69ff3
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-root.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-rm-root) begin
+(dir-rm-root) remove "/" (must fail)
+(dir-rm-root) create "/a"
+(dir-rm-root) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-tree-persistence.ck b/src/tests/filesys/extended/dir-rm-tree-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-tree-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({});
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-tree.c b/src/tests/filesys/extended/dir-rm-tree.c
new file mode 100644
index 0000000..bab41a6
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-tree.c
@@ -0,0 +1,62 @@
+/* Creates directories /0/0/0 through /3/2/2 and files in the
+ leaf directories, then removes them. */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/filesys/extended/mk-tree.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static void remove_tree (int at, int bt, int ct, int dt);
+
+void
+test_main (void)
+{
+ make_tree (4, 3, 3, 4);
+ remove_tree (4, 3, 3, 4);
+}
+
+static void do_remove (const char *format, ...) PRINTF_FORMAT (1, 2);
+
+static void
+remove_tree (int at, int bt, int ct, int dt)
+{
+ char try[128];
+ int a, b, c, d;
+
+ msg ("removing /0/0/0/0 through /%d/%d/%d/%d...",
+ at - 1, bt - 1, ct - 1, dt - 1);
+ quiet = true;
+ for (a = 0; a < at; a++)
+ {
+ for (b = 0; b < bt; b++)
+ {
+ for (c = 0; c < ct; c++)
+ {
+ for (d = 0; d < dt; d++)
+ do_remove ("/%d/%d/%d/%d", a, b, c, d);
+ do_remove ("/%d/%d/%d", a, b, c);
+ }
+ do_remove ("/%d/%d", a, b);
+ }
+ do_remove ("/%d", a);
+ }
+ quiet = false;
+
+ snprintf (try, sizeof (try), "/%d/%d/%d/%d", at - 1, 0, ct - 1, 0);
+ CHECK (open (try) == -1, "open \"%s\" (must return -1)", try);
+}
+
+static void
+do_remove (const char *format, ...)
+{
+ char name[128];
+ va_list args;
+
+ va_start (args, format);
+ vsnprintf (name, sizeof name, format, args);
+ va_end (args);
+
+ CHECK (remove (name), "remove \"%s\"", name);
+}
diff --git a/src/tests/filesys/extended/dir-rm-tree.ck b/src/tests/filesys/extended/dir-rm-tree.ck
new file mode 100644
index 0000000..587b493
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-tree.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-rm-tree) begin
+(dir-rm-tree) creating /0/0/0/0 through /3/2/2/3...
+(dir-rm-tree) open "/0/2/0/3"
+(dir-rm-tree) close "/0/2/0/3"
+(dir-rm-tree) removing /0/0/0/0 through /3/2/2/3...
+(dir-rm-tree) open "/3/0/2/0" (must return -1)
+(dir-rm-tree) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-rmdir-persistence.ck b/src/tests/filesys/extended/dir-rmdir-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rmdir-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({});
+pass;
diff --git a/src/tests/filesys/extended/dir-rmdir.c b/src/tests/filesys/extended/dir-rmdir.c
new file mode 100644
index 0000000..b3cbc6f
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rmdir.c
@@ -0,0 +1,14 @@
+/* Creates and removes a directory, then makes sure that it's
+ really gone. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ CHECK (mkdir ("a"), "mkdir \"a\"");
+ CHECK (remove ("a"), "rmdir \"a\"");
+ CHECK (!chdir ("a"), "chdir \"a\" (must return false)");
+}
diff --git a/src/tests/filesys/extended/dir-rmdir.ck b/src/tests/filesys/extended/dir-rmdir.ck
new file mode 100644
index 0000000..e0d8922
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rmdir.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-rmdir) begin
+(dir-rmdir) mkdir "a"
+(dir-rmdir) rmdir "a"
+(dir-rmdir) chdir "a" (must return false)
+(dir-rmdir) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-under-file-persistence.ck b/src/tests/filesys/extended/dir-under-file-persistence.ck
new file mode 100644
index 0000000..67ca528
--- /dev/null
+++ b/src/tests/filesys/extended/dir-under-file-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"abc" => ['']});
+pass;
diff --git a/src/tests/filesys/extended/dir-under-file.c b/src/tests/filesys/extended/dir-under-file.c
new file mode 100644
index 0000000..973a8b1
--- /dev/null
+++ b/src/tests/filesys/extended/dir-under-file.c
@@ -0,0 +1,13 @@
+/* Tries to create a directory with the same name as an existing
+ file, which must return failure. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ CHECK (create ("abc", 0), "create \"abc\"");
+ CHECK (!mkdir ("abc"), "mkdir \"abc\" (must return false)");
+}
diff --git a/src/tests/filesys/extended/dir-under-file.ck b/src/tests/filesys/extended/dir-under-file.ck
new file mode 100644
index 0000000..cce23b4
--- /dev/null
+++ b/src/tests/filesys/extended/dir-under-file.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-under-file) begin
+(dir-under-file) create "abc"
+(dir-under-file) mkdir "abc" (must return false)
+(dir-under-file) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-vine-persistence.ck b/src/tests/filesys/extended/dir-vine-persistence.ck
new file mode 100644
index 0000000..698ef01
--- /dev/null
+++ b/src/tests/filesys/extended/dir-vine-persistence.ck
@@ -0,0 +1,37 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+# The archive should look like this:
+#
+# 40642 dir-vine
+# 42479 tar
+# 0 start
+# 11 start/file0
+# 0 start/dir0
+# 11 start/dir0/file1
+# 0 start/dir0/dir1
+# 11 start/dir0/dir1/file2
+# 0 start/dir0/dir1/dir2
+# 11 start/dir0/dir1/dir2/file3
+# 0 start/dir0/dir1/dir2/dir3
+# 11 start/dir0/dir1/dir2/dir3/file4
+# 0 start/dir0/dir1/dir2/dir3/dir4
+# 11 start/dir0/dir1/dir2/dir3/dir4/file5
+# 0 start/dir0/dir1/dir2/dir3/dir4/dir5
+# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/file6
+# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6
+# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/file7
+# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7
+# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file8
+# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8
+# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file9
+# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9
+my ($dir) = {};
+my ($root) = {"start" => $dir};
+for (my ($i) = 0; $i < 10; $i++) {
+ $dir->{"file$i"} = ["contents $i\n"];
+ $dir = $dir->{"dir$i"} = {};
+}
+check_archive ($root);
+pass;
diff --git a/src/tests/filesys/extended/dir-vine.c b/src/tests/filesys/extended/dir-vine.c
new file mode 100644
index 0000000..8a31c38
--- /dev/null
+++ b/src/tests/filesys/extended/dir-vine.c
@@ -0,0 +1,85 @@
+/* Create a very deep "vine" of directories: /dir0/dir1/dir2/...
+ and an ordinary file in each of them, until we fill up the
+ disk.
+
+ Then delete most of them, for two reasons. First, "tar"
+ limits file names to 100 characters (which could be extended
+ to 256 without much trouble). Second, a full disk has no room
+ for the tar archive. */
+
+#include <string.h>
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ int i;
+
+ msg ("creating many levels of files and directories...");
+ quiet = true;
+ CHECK (mkdir ("start"), "mkdir \"start\"");
+ CHECK (chdir ("start"), "chdir \"start\"");
+ for (i = 0; ; i++)
+ {
+ char name[3][READDIR_MAX_LEN + 1];
+ char file_name[16], dir_name[16];
+ char contents[128];
+ int fd;
+
+ /* Create file. */
+ snprintf (file_name, sizeof file_name, "file%d", i);
+ if (!create (file_name, 0))
+ break;
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ snprintf (contents, sizeof contents, "contents %d\n", i);
+ if (write (fd, contents, strlen (contents)) != (int) strlen (contents))
+ {
+ CHECK (remove (file_name), "remove \"%s\"", file_name);
+ close (fd);
+ break;
+ }
+ close (fd);
+
+ /* Create directory. */
+ snprintf (dir_name, sizeof dir_name, "dir%d", i);
+ if (!mkdir (dir_name))
+ {
+ CHECK (remove (file_name), "remove \"%s\"", file_name);
+ break;
+ }
+
+ /* Check for file and directory. */
+ CHECK ((fd = open (".")) > 1, "open \".\"");
+ CHECK (readdir (fd, name[0]), "readdir \".\"");
+ CHECK (readdir (fd, name[1]), "readdir \".\"");
+ CHECK (!readdir (fd, name[2]), "readdir \".\" (should fail)");
+ CHECK ((!strcmp (name[0], dir_name) && !strcmp (name[1], file_name))
+ || (!strcmp (name[1], dir_name) && !strcmp (name[0], file_name)),
+ "names should be \"%s\" and \"%s\", "
+ "actually \"%s\" and \"%s\"",
+ file_name, dir_name, name[0], name[1]);
+ close (fd);
+
+ /* Descend into directory. */
+ CHECK (chdir (dir_name), "chdir \"%s\"", dir_name);
+ }
+ CHECK (i > 200, "created files and directories only to level %d", i);
+ quiet = false;
+
+ msg ("removing all but top 10 levels of files and directories...");
+ quiet = true;
+ while (i-- > 10)
+ {
+ char file_name[16], dir_name[16];
+
+ snprintf (file_name, sizeof file_name, "file%d", i);
+ snprintf (dir_name, sizeof dir_name, "dir%d", i);
+ CHECK (chdir (".."), "chdir \"..\"");
+ CHECK (remove (dir_name), "remove \"%s\"", dir_name);
+ CHECK (remove (file_name), "remove \"%s\"", file_name);
+ }
+ quiet = false;
+}
diff --git a/src/tests/filesys/extended/dir-vine.ck b/src/tests/filesys/extended/dir-vine.ck
new file mode 100644
index 0000000..db452b0
--- /dev/null
+++ b/src/tests/filesys/extended/dir-vine.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-vine) begin
+(dir-vine) creating many levels of files and directories...
+(dir-vine) removing all but top 10 levels of files and directories...
+(dir-vine) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-create-persistence.ck b/src/tests/filesys/extended/grow-create-persistence.ck
new file mode 100644
index 0000000..bbcb24f
--- /dev/null
+++ b/src/tests/filesys/extended/grow-create-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"blargle" => ['']});
+pass;
diff --git a/src/tests/filesys/extended/grow-create.c b/src/tests/filesys/extended/grow-create.c
new file mode 100644
index 0000000..9ccc4ea
--- /dev/null
+++ b/src/tests/filesys/extended/grow-create.c
@@ -0,0 +1,4 @@
+/* Create a file of size 0. */
+
+#define TEST_SIZE 0
+#include "tests/filesys/create.inc"
diff --git a/src/tests/filesys/extended/grow-create.ck b/src/tests/filesys/extended/grow-create.ck
new file mode 100644
index 0000000..b2e69d1
--- /dev/null
+++ b/src/tests/filesys/extended/grow-create.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-create) begin
+(grow-create) create "blargle"
+(grow-create) open "blargle" for verification
+(grow-create) verified contents of "blargle"
+(grow-create) close "blargle"
+(grow-create) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-dir-lg-persistence.ck b/src/tests/filesys/extended/grow-dir-lg-persistence.ck
new file mode 100644
index 0000000..989a322
--- /dev/null
+++ b/src/tests/filesys/extended/grow-dir-lg-persistence.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+my ($fs);
+$fs->{'x'}{"file$_"} = [random_bytes (512)] foreach 0...49;
+check_archive ($fs);
+pass;
diff --git a/src/tests/filesys/extended/grow-dir-lg.c b/src/tests/filesys/extended/grow-dir-lg.c
new file mode 100644
index 0000000..20a194b
--- /dev/null
+++ b/src/tests/filesys/extended/grow-dir-lg.c
@@ -0,0 +1,6 @@
+/* Creates a directory,
+ then creates 50 files in that directory. */
+
+#define FILE_CNT 50
+#define DIRECTORY "/x"
+#include "tests/filesys/extended/grow-dir.inc"
diff --git a/src/tests/filesys/extended/grow-dir-lg.ck b/src/tests/filesys/extended/grow-dir-lg.ck
new file mode 100644
index 0000000..ec58bd3
--- /dev/null
+++ b/src/tests/filesys/extended/grow-dir-lg.ck
@@ -0,0 +1,61 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-dir-lg) begin
+(grow-dir-lg) mkdir /x
+(grow-dir-lg) creating and checking "/x/file0"
+(grow-dir-lg) creating and checking "/x/file1"
+(grow-dir-lg) creating and checking "/x/file2"
+(grow-dir-lg) creating and checking "/x/file3"
+(grow-dir-lg) creating and checking "/x/file4"
+(grow-dir-lg) creating and checking "/x/file5"
+(grow-dir-lg) creating and checking "/x/file6"
+(grow-dir-lg) creating and checking "/x/file7"
+(grow-dir-lg) creating and checking "/x/file8"
+(grow-dir-lg) creating and checking "/x/file9"
+(grow-dir-lg) creating and checking "/x/file10"
+(grow-dir-lg) creating and checking "/x/file11"
+(grow-dir-lg) creating and checking "/x/file12"
+(grow-dir-lg) creating and checking "/x/file13"
+(grow-dir-lg) creating and checking "/x/file14"
+(grow-dir-lg) creating and checking "/x/file15"
+(grow-dir-lg) creating and checking "/x/file16"
+(grow-dir-lg) creating and checking "/x/file17"
+(grow-dir-lg) creating and checking "/x/file18"
+(grow-dir-lg) creating and checking "/x/file19"
+(grow-dir-lg) creating and checking "/x/file20"
+(grow-dir-lg) creating and checking "/x/file21"
+(grow-dir-lg) creating and checking "/x/file22"
+(grow-dir-lg) creating and checking "/x/file23"
+(grow-dir-lg) creating and checking "/x/file24"
+(grow-dir-lg) creating and checking "/x/file25"
+(grow-dir-lg) creating and checking "/x/file26"
+(grow-dir-lg) creating and checking "/x/file27"
+(grow-dir-lg) creating and checking "/x/file28"
+(grow-dir-lg) creating and checking "/x/file29"
+(grow-dir-lg) creating and checking "/x/file30"
+(grow-dir-lg) creating and checking "/x/file31"
+(grow-dir-lg) creating and checking "/x/file32"
+(grow-dir-lg) creating and checking "/x/file33"
+(grow-dir-lg) creating and checking "/x/file34"
+(grow-dir-lg) creating and checking "/x/file35"
+(grow-dir-lg) creating and checking "/x/file36"
+(grow-dir-lg) creating and checking "/x/file37"
+(grow-dir-lg) creating and checking "/x/file38"
+(grow-dir-lg) creating and checking "/x/file39"
+(grow-dir-lg) creating and checking "/x/file40"
+(grow-dir-lg) creating and checking "/x/file41"
+(grow-dir-lg) creating and checking "/x/file42"
+(grow-dir-lg) creating and checking "/x/file43"
+(grow-dir-lg) creating and checking "/x/file44"
+(grow-dir-lg) creating and checking "/x/file45"
+(grow-dir-lg) creating and checking "/x/file46"
+(grow-dir-lg) creating and checking "/x/file47"
+(grow-dir-lg) creating and checking "/x/file48"
+(grow-dir-lg) creating and checking "/x/file49"
+(grow-dir-lg) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-dir.inc b/src/tests/filesys/extended/grow-dir.inc
new file mode 100644
index 0000000..bee0ba0
--- /dev/null
+++ b/src/tests/filesys/extended/grow-dir.inc
@@ -0,0 +1,41 @@
+/* -*- c -*- */
+
+#include <syscall.h>
+#include <stdio.h>
+#include "tests/filesys/seq-test.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[512];
+
+static size_t
+return_block_size (void)
+{
+ return sizeof buf;
+}
+
+void
+test_main (void)
+{
+ size_t i;
+
+#ifdef DIRECTORY
+ CHECK (mkdir (DIRECTORY), "mkdir %s", DIRECTORY);
+#define DIR_PREFIX DIRECTORY "/"
+#else
+#define DIR_PREFIX ""
+#endif
+ for (i = 0; i < FILE_CNT; i++)
+ {
+ char file_name[128];
+ snprintf (file_name, sizeof file_name, "%sfile%zu", DIR_PREFIX, i);
+
+ msg ("creating and checking \"%s\"", file_name);
+
+ quiet = true;
+ seq_test (file_name,
+ buf, sizeof buf, sizeof buf,
+ return_block_size, NULL);
+ quiet = false;
+ }
+}
diff --git a/src/tests/filesys/extended/grow-file-size-persistence.ck b/src/tests/filesys/extended/grow-file-size-persistence.ck
new file mode 100644
index 0000000..150f383
--- /dev/null
+++ b/src/tests/filesys/extended/grow-file-size-persistence.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"testfile" => [random_bytes (2134)]});
+pass;
diff --git a/src/tests/filesys/extended/grow-file-size.c b/src/tests/filesys/extended/grow-file-size.c
new file mode 100644
index 0000000..3ce8588
--- /dev/null
+++ b/src/tests/filesys/extended/grow-file-size.c
@@ -0,0 +1,33 @@
+/* Grows a file from 0 bytes to 2,134 bytes, 37 bytes at a time,
+ and checks that the file's size is reported correctly at each
+ step. */
+
+#include <syscall.h>
+#include "tests/filesys/seq-test.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[2134];
+
+static size_t
+return_block_size (void)
+{
+ return 37;
+}
+
+static void
+check_file_size (int fd, long ofs)
+{
+ long size = filesize (fd);
+ if (size != ofs)
+ fail ("filesize not updated properly: should be %ld, actually %ld",
+ ofs, size);
+}
+
+void
+test_main (void)
+{
+ seq_test ("testfile",
+ buf, sizeof buf, 0,
+ return_block_size, check_file_size);
+}
diff --git a/src/tests/filesys/extended/grow-file-size.ck b/src/tests/filesys/extended/grow-file-size.ck
new file mode 100644
index 0000000..d81feff
--- /dev/null
+++ b/src/tests/filesys/extended/grow-file-size.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-file-size) begin
+(grow-file-size) create "testfile"
+(grow-file-size) open "testfile"
+(grow-file-size) writing "testfile"
+(grow-file-size) close "testfile"
+(grow-file-size) open "testfile" for verification
+(grow-file-size) verified contents of "testfile"
+(grow-file-size) close "testfile"
+(grow-file-size) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-root-lg-persistence.ck b/src/tests/filesys/extended/grow-root-lg-persistence.ck
new file mode 100644
index 0000000..1692f46
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-lg-persistence.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+my ($fs);
+$fs->{"file$_"} = [random_bytes (512)] foreach 0...49;
+check_archive ($fs);
+pass;
diff --git a/src/tests/filesys/extended/grow-root-lg.c b/src/tests/filesys/extended/grow-root-lg.c
new file mode 100644
index 0000000..d8d6c09
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-lg.c
@@ -0,0 +1,4 @@
+/* Creates 50 files in the root directory. */
+
+#define FILE_CNT 50
+#include "tests/filesys/extended/grow-dir.inc"
diff --git a/src/tests/filesys/extended/grow-root-lg.ck b/src/tests/filesys/extended/grow-root-lg.ck
new file mode 100644
index 0000000..b174bc9
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-lg.ck
@@ -0,0 +1,60 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-root-lg) begin
+(grow-root-lg) creating and checking "file0"
+(grow-root-lg) creating and checking "file1"
+(grow-root-lg) creating and checking "file2"
+(grow-root-lg) creating and checking "file3"
+(grow-root-lg) creating and checking "file4"
+(grow-root-lg) creating and checking "file5"
+(grow-root-lg) creating and checking "file6"
+(grow-root-lg) creating and checking "file7"
+(grow-root-lg) creating and checking "file8"
+(grow-root-lg) creating and checking "file9"
+(grow-root-lg) creating and checking "file10"
+(grow-root-lg) creating and checking "file11"
+(grow-root-lg) creating and checking "file12"
+(grow-root-lg) creating and checking "file13"
+(grow-root-lg) creating and checking "file14"
+(grow-root-lg) creating and checking "file15"
+(grow-root-lg) creating and checking "file16"
+(grow-root-lg) creating and checking "file17"
+(grow-root-lg) creating and checking "file18"
+(grow-root-lg) creating and checking "file19"
+(grow-root-lg) creating and checking "file20"
+(grow-root-lg) creating and checking "file21"
+(grow-root-lg) creating and checking "file22"
+(grow-root-lg) creating and checking "file23"
+(grow-root-lg) creating and checking "file24"
+(grow-root-lg) creating and checking "file25"
+(grow-root-lg) creating and checking "file26"
+(grow-root-lg) creating and checking "file27"
+(grow-root-lg) creating and checking "file28"
+(grow-root-lg) creating and checking "file29"
+(grow-root-lg) creating and checking "file30"
+(grow-root-lg) creating and checking "file31"
+(grow-root-lg) creating and checking "file32"
+(grow-root-lg) creating and checking "file33"
+(grow-root-lg) creating and checking "file34"
+(grow-root-lg) creating and checking "file35"
+(grow-root-lg) creating and checking "file36"
+(grow-root-lg) creating and checking "file37"
+(grow-root-lg) creating and checking "file38"
+(grow-root-lg) creating and checking "file39"
+(grow-root-lg) creating and checking "file40"
+(grow-root-lg) creating and checking "file41"
+(grow-root-lg) creating and checking "file42"
+(grow-root-lg) creating and checking "file43"
+(grow-root-lg) creating and checking "file44"
+(grow-root-lg) creating and checking "file45"
+(grow-root-lg) creating and checking "file46"
+(grow-root-lg) creating and checking "file47"
+(grow-root-lg) creating and checking "file48"
+(grow-root-lg) creating and checking "file49"
+(grow-root-lg) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-root-sm-persistence.ck b/src/tests/filesys/extended/grow-root-sm-persistence.ck
new file mode 100644
index 0000000..2b0b8ab
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-sm-persistence.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+my ($fs);
+$fs->{"file$_"} = [random_bytes (512)] foreach 0...19;
+check_archive ($fs);
+pass;
diff --git a/src/tests/filesys/extended/grow-root-sm.c b/src/tests/filesys/extended/grow-root-sm.c
new file mode 100644
index 0000000..ee375d5
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-sm.c
@@ -0,0 +1,4 @@
+/* Creates 20 files in the root directory. */
+
+#define FILE_CNT 20
+#include "tests/filesys/extended/grow-dir.inc"
diff --git a/src/tests/filesys/extended/grow-root-sm.ck b/src/tests/filesys/extended/grow-root-sm.ck
new file mode 100644
index 0000000..1aac7e9
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-sm.ck
@@ -0,0 +1,30 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-root-sm) begin
+(grow-root-sm) creating and checking "file0"
+(grow-root-sm) creating and checking "file1"
+(grow-root-sm) creating and checking "file2"
+(grow-root-sm) creating and checking "file3"
+(grow-root-sm) creating and checking "file4"
+(grow-root-sm) creating and checking "file5"
+(grow-root-sm) creating and checking "file6"
+(grow-root-sm) creating and checking "file7"
+(grow-root-sm) creating and checking "file8"
+(grow-root-sm) creating and checking "file9"
+(grow-root-sm) creating and checking "file10"
+(grow-root-sm) creating and checking "file11"
+(grow-root-sm) creating and checking "file12"
+(grow-root-sm) creating and checking "file13"
+(grow-root-sm) creating and checking "file14"
+(grow-root-sm) creating and checking "file15"
+(grow-root-sm) creating and checking "file16"
+(grow-root-sm) creating and checking "file17"
+(grow-root-sm) creating and checking "file18"
+(grow-root-sm) creating and checking "file19"
+(grow-root-sm) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-seq-lg-persistence.ck b/src/tests/filesys/extended/grow-seq-lg-persistence.ck
new file mode 100644
index 0000000..41aaae0
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-lg-persistence.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"testme" => [random_bytes (72943)]});
+pass;
diff --git a/src/tests/filesys/extended/grow-seq-lg.c b/src/tests/filesys/extended/grow-seq-lg.c
new file mode 100644
index 0000000..3108d17
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-lg.c
@@ -0,0 +1,5 @@
+/* Grows a file from 0 bytes to 72,943 bytes, 1,234 bytes at a
+ time. */
+
+#define TEST_SIZE 72943
+#include "tests/filesys/extended/grow-seq.inc"
diff --git a/src/tests/filesys/extended/grow-seq-lg.ck b/src/tests/filesys/extended/grow-seq-lg.ck
new file mode 100644
index 0000000..90fcd8c
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-lg.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-seq-lg) begin
+(grow-seq-lg) create "testme"
+(grow-seq-lg) open "testme"
+(grow-seq-lg) writing "testme"
+(grow-seq-lg) close "testme"
+(grow-seq-lg) open "testme" for verification
+(grow-seq-lg) verified contents of "testme"
+(grow-seq-lg) close "testme"
+(grow-seq-lg) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-seq-sm-persistence.ck b/src/tests/filesys/extended/grow-seq-sm-persistence.ck
new file mode 100644
index 0000000..6cb0bd8
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-sm-persistence.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"testme" => [random_bytes (5678)]});
+pass;
diff --git a/src/tests/filesys/extended/grow-seq-sm.c b/src/tests/filesys/extended/grow-seq-sm.c
new file mode 100644
index 0000000..3656e2e
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-sm.c
@@ -0,0 +1,5 @@
+/* Grows a file from 0 bytes to 5,678 bytes, 1,234 bytes at a
+ time. */
+
+#define TEST_SIZE 5678
+#include "tests/filesys/extended/grow-seq.inc"
diff --git a/src/tests/filesys/extended/grow-seq-sm.ck b/src/tests/filesys/extended/grow-seq-sm.ck
new file mode 100644
index 0000000..5cf4518
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-sm.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-seq-sm) begin
+(grow-seq-sm) create "testme"
+(grow-seq-sm) open "testme"
+(grow-seq-sm) writing "testme"
+(grow-seq-sm) close "testme"
+(grow-seq-sm) open "testme" for verification
+(grow-seq-sm) verified contents of "testme"
+(grow-seq-sm) close "testme"
+(grow-seq-sm) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-seq.inc b/src/tests/filesys/extended/grow-seq.inc
new file mode 100644
index 0000000..1b7710c
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq.inc
@@ -0,0 +1,20 @@
+/* -*- c -*- */
+
+#include "tests/filesys/seq-test.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+static size_t
+return_block_size (void)
+{
+ return 1234;
+}
+
+void
+test_main (void)
+{
+ seq_test ("testme",
+ buf, sizeof buf, 0,
+ return_block_size, NULL);
+}
diff --git a/src/tests/filesys/extended/grow-sparse-persistence.ck b/src/tests/filesys/extended/grow-sparse-persistence.ck
new file mode 100644
index 0000000..3f06a5b
--- /dev/null
+++ b/src/tests/filesys/extended/grow-sparse-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"testfile" => ["\0" x 76543]});
+pass;
diff --git a/src/tests/filesys/extended/grow-sparse.c b/src/tests/filesys/extended/grow-sparse.c
new file mode 100644
index 0000000..6eab210
--- /dev/null
+++ b/src/tests/filesys/extended/grow-sparse.c
@@ -0,0 +1,25 @@
+/* Tests that seeking past the end of a file and writing will
+ properly zero out the region in between. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[76543];
+
+void
+test_main (void)
+{
+ const char *file_name = "testfile";
+ char zero = 0;
+ int fd;
+
+ CHECK (create (file_name, 0), "create \"%s\"", file_name);
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ msg ("seek \"%s\"", file_name);
+ seek (fd, sizeof buf - 1);
+ CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", file_name);
+ msg ("close \"%s\"", file_name);
+ close (fd);
+ check_file (file_name, buf, sizeof buf);
+}
diff --git a/src/tests/filesys/extended/grow-sparse.ck b/src/tests/filesys/extended/grow-sparse.ck
new file mode 100644
index 0000000..379ba2c
--- /dev/null
+++ b/src/tests/filesys/extended/grow-sparse.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-sparse) begin
+(grow-sparse) create "testfile"
+(grow-sparse) open "testfile"
+(grow-sparse) seek "testfile"
+(grow-sparse) write "testfile"
+(grow-sparse) close "testfile"
+(grow-sparse) open "testfile" for verification
+(grow-sparse) verified contents of "testfile"
+(grow-sparse) close "testfile"
+(grow-sparse) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-tell-persistence.ck b/src/tests/filesys/extended/grow-tell-persistence.ck
new file mode 100644
index 0000000..d93a422
--- /dev/null
+++ b/src/tests/filesys/extended/grow-tell-persistence.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"foobar" => [random_bytes (2134)]});
+pass;
diff --git a/src/tests/filesys/extended/grow-tell.c b/src/tests/filesys/extended/grow-tell.c
new file mode 100644
index 0000000..5f5da5b
--- /dev/null
+++ b/src/tests/filesys/extended/grow-tell.c
@@ -0,0 +1,32 @@
+/* Checks that growing a file updates the file position
+ correctly. */
+
+#include <syscall.h>
+#include "tests/filesys/seq-test.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[2134];
+
+static size_t
+return_block_size (void)
+{
+ return 37;
+}
+
+static void
+check_tell (int fd, long ofs)
+{
+ long pos = tell (fd);
+ if (pos != ofs)
+ fail ("file position not updated properly: should be %ld, actually %ld",
+ ofs, pos);
+}
+
+void
+test_main (void)
+{
+ seq_test ("foobar",
+ buf, sizeof buf, 0,
+ return_block_size, check_tell);
+}
diff --git a/src/tests/filesys/extended/grow-tell.ck b/src/tests/filesys/extended/grow-tell.ck
new file mode 100644
index 0000000..fe94707
--- /dev/null
+++ b/src/tests/filesys/extended/grow-tell.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-tell) begin
+(grow-tell) create "foobar"
+(grow-tell) open "foobar"
+(grow-tell) writing "foobar"
+(grow-tell) close "foobar"
+(grow-tell) open "foobar" for verification
+(grow-tell) verified contents of "foobar"
+(grow-tell) close "foobar"
+(grow-tell) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-two-files-persistence.ck b/src/tests/filesys/extended/grow-two-files-persistence.ck
new file mode 100644
index 0000000..1c4ced1
--- /dev/null
+++ b/src/tests/filesys/extended/grow-two-files-persistence.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+my ($a) = random_bytes (8143);
+my ($b) = random_bytes (8143);
+check_archive ({"a" => [$a], "b" => [$b]});
+pass;
diff --git a/src/tests/filesys/extended/grow-two-files.c b/src/tests/filesys/extended/grow-two-files.c
new file mode 100644
index 0000000..6a8fb1c
--- /dev/null
+++ b/src/tests/filesys/extended/grow-two-files.c
@@ -0,0 +1,62 @@
+/* Grows two files in parallel and checks that their contents are
+ correct. */
+
+#include <random.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define FILE_SIZE 8143
+static char buf_a[FILE_SIZE];
+static char buf_b[FILE_SIZE];
+
+static void
+write_some_bytes (const char *file_name, int fd, const char *buf, size_t *ofs)
+{
+ if (*ofs < FILE_SIZE)
+ {
+ size_t block_size = random_ulong () % (FILE_SIZE / 8) + 1;
+ size_t ret_val;
+ if (block_size > FILE_SIZE - *ofs)
+ block_size = FILE_SIZE - *ofs;
+
+ ret_val = write (fd, buf + *ofs, block_size);
+ if (ret_val != block_size)
+ fail ("write %zu bytes at offset %zu in \"%s\" returned %zu",
+ block_size, *ofs, file_name, ret_val);
+ *ofs += block_size;
+ }
+}
+
+void
+test_main (void)
+{
+ int fd_a, fd_b;
+ size_t ofs_a = 0, ofs_b = 0;
+
+ random_init (0);
+ random_bytes (buf_a, sizeof buf_a);
+ random_bytes (buf_b, sizeof buf_b);
+
+ CHECK (create ("a", 0), "create \"a\"");
+ CHECK (create ("b", 0), "create \"b\"");
+
+ CHECK ((fd_a = open ("a")) > 1, "open \"a\"");
+ CHECK ((fd_b = open ("b")) > 1, "open \"b\"");
+
+ msg ("write \"a\" and \"b\" alternately");
+ while (ofs_a < FILE_SIZE || ofs_b < FILE_SIZE)
+ {
+ write_some_bytes ("a", fd_a, buf_a, &ofs_a);
+ write_some_bytes ("b", fd_b, buf_b, &ofs_b);
+ }
+
+ msg ("close \"a\"");
+ close (fd_a);
+
+ msg ("close \"b\"");
+ close (fd_b);
+
+ check_file ("a", buf_a, FILE_SIZE);
+ check_file ("b", buf_b, FILE_SIZE);
+}
diff --git a/src/tests/filesys/extended/grow-two-files.ck b/src/tests/filesys/extended/grow-two-files.ck
new file mode 100644
index 0000000..b5e754a
--- /dev/null
+++ b/src/tests/filesys/extended/grow-two-files.ck
@@ -0,0 +1,23 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-two-files) begin
+(grow-two-files) create "a"
+(grow-two-files) create "b"
+(grow-two-files) open "a"
+(grow-two-files) open "b"
+(grow-two-files) write "a" and "b" alternately
+(grow-two-files) close "a"
+(grow-two-files) close "b"
+(grow-two-files) open "a" for verification
+(grow-two-files) verified contents of "a"
+(grow-two-files) close "a"
+(grow-two-files) open "b" for verification
+(grow-two-files) verified contents of "b"
+(grow-two-files) close "b"
+(grow-two-files) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/mk-tree.c b/src/tests/filesys/extended/mk-tree.c
new file mode 100644
index 0000000..a36bb88
--- /dev/null
+++ b/src/tests/filesys/extended/mk-tree.c
@@ -0,0 +1,67 @@
+/* Library function for creating a tree of directories. */
+
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/filesys/extended/mk-tree.h"
+#include "tests/lib.h"
+
+static void do_mkdir (const char *format, ...) PRINTF_FORMAT (1, 2);
+static void do_touch (const char *format, ...) PRINTF_FORMAT (1, 2);
+
+void
+make_tree (int at, int bt, int ct, int dt)
+{
+ char try[128];
+ int a, b, c, d;
+ int fd;
+
+ msg ("creating /0/0/0/0 through /%d/%d/%d/%d...",
+ at - 1, bt - 1, ct - 1, dt - 1);
+ quiet = true;
+ for (a = 0; a < at; a++)
+ {
+ do_mkdir ("/%d", a);
+ for (b = 0; b < bt; b++)
+ {
+ do_mkdir ("/%d/%d", a, b);
+ for (c = 0; c < ct; c++)
+ {
+ do_mkdir ("/%d/%d/%d", a, b, c);
+ for (d = 0; d < dt; d++)
+ do_touch ("/%d/%d/%d/%d", a, b, c, d);
+ }
+ }
+ }
+ quiet = false;
+
+ snprintf (try, sizeof try, "/%d/%d/%d/%d", 0, bt - 1, 0, dt - 1);
+ CHECK ((fd = open (try)) > 1, "open \"%s\"", try);
+ msg ("close \"%s\"", try);
+ close (fd);
+}
+
+static void
+do_mkdir (const char *format, ...)
+{
+ char dir[128];
+ va_list args;
+
+ va_start (args, format);
+ vsnprintf (dir, sizeof dir, format, args);
+ va_end (args);
+
+ CHECK (mkdir (dir), "mkdir \"%s\"", dir);
+}
+
+static void
+do_touch (const char *format, ...)
+{
+ char file[128];
+ va_list args;
+
+ va_start (args, format);
+ vsnprintf (file, sizeof file, format, args);
+ va_end (args);
+
+ CHECK (create (file, 0), "create \"%s\"", file);
+}
diff --git a/src/tests/filesys/extended/mk-tree.h b/src/tests/filesys/extended/mk-tree.h
new file mode 100644
index 0000000..df0d5a6
--- /dev/null
+++ b/src/tests/filesys/extended/mk-tree.h
@@ -0,0 +1,6 @@
+#ifndef TESTS_FILESYS_EXTENDED_MK_TREE_H
+#define TESTS_FILESYS_EXTENDED_MK_TREE_H
+
+void make_tree (int at, int bt, int ct, int dt);
+
+#endif /* tests/filesys/extended/mk-tree.h */
diff --git a/src/tests/filesys/extended/syn-rw-persistence.ck b/src/tests/filesys/extended/syn-rw-persistence.ck
new file mode 100644
index 0000000..62d57ee
--- /dev/null
+++ b/src/tests/filesys/extended/syn-rw-persistence.ck
@@ -0,0 +1,8 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"child-syn-rw" => "tests/filesys/extended/child-syn-rw",
+ "logfile" => [random_bytes (8 * 512)]});
+pass;
diff --git a/src/tests/filesys/extended/syn-rw.c b/src/tests/filesys/extended/syn-rw.c
new file mode 100644
index 0000000..657dfb5
--- /dev/null
+++ b/src/tests/filesys/extended/syn-rw.c
@@ -0,0 +1,35 @@
+/* Grows a file in chunks while subprocesses read the growing
+ file. */
+
+#include <random.h>
+#include <syscall.h>
+#include "tests/filesys/extended/syn-rw.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+char buf[BUF_SIZE];
+
+#define CHILD_CNT 4
+
+void
+test_main (void)
+{
+ pid_t children[CHILD_CNT];
+ size_t ofs;
+ int fd;
+
+ CHECK (create (file_name, 0), "create \"%s\"", file_name);
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+
+ exec_children ("child-syn-rw", children, CHILD_CNT);
+
+ random_bytes (buf, sizeof buf);
+ quiet = true;
+ for (ofs = 0; ofs < BUF_SIZE; ofs += CHUNK_SIZE)
+ CHECK (write (fd, buf + ofs, CHUNK_SIZE) > 0,
+ "write %d bytes at offset %zu in \"%s\"",
+ (int) CHUNK_SIZE, ofs, file_name);
+ quiet = false;
+
+ wait_children (children, CHILD_CNT);
+}
diff --git a/src/tests/filesys/extended/syn-rw.ck b/src/tests/filesys/extended/syn-rw.ck
new file mode 100644
index 0000000..ac82aa8
--- /dev/null
+++ b/src/tests/filesys/extended/syn-rw.ck
@@ -0,0 +1,20 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(syn-rw) begin
+(syn-rw) create "logfile"
+(syn-rw) open "logfile"
+(syn-rw) exec child 1 of 4: "child-syn-rw 0"
+(syn-rw) exec child 2 of 4: "child-syn-rw 1"
+(syn-rw) exec child 3 of 4: "child-syn-rw 2"
+(syn-rw) exec child 4 of 4: "child-syn-rw 3"
+(syn-rw) wait for child 1 of 4 returned 0 (expected 0)
+(syn-rw) wait for child 2 of 4 returned 1 (expected 1)
+(syn-rw) wait for child 3 of 4 returned 2 (expected 2)
+(syn-rw) wait for child 4 of 4 returned 3 (expected 3)
+(syn-rw) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/syn-rw.h b/src/tests/filesys/extended/syn-rw.h
new file mode 100644
index 0000000..170aeb4
--- /dev/null
+++ b/src/tests/filesys/extended/syn-rw.h
@@ -0,0 +1,9 @@
+#ifndef TESTS_FILESYS_EXTENDED_SYN_RW_H
+#define TESTS_FILESYS_EXTENDED_SYN_RW_H
+
+#define CHUNK_SIZE 8
+#define CHUNK_CNT 512
+#define BUF_SIZE (CHUNK_SIZE * CHUNK_CNT)
+static const char file_name[] = "logfile";
+
+#endif /* tests/filesys/extended/syn-rw.h */
diff --git a/src/tests/filesys/extended/tar.c b/src/tests/filesys/extended/tar.c
new file mode 100644
index 0000000..6f70d97
--- /dev/null
+++ b/src/tests/filesys/extended/tar.c
@@ -0,0 +1,250 @@
+/* tar.c
+
+ Creates a tar archive. */
+
+#include <syscall.h>
+#include <stdio.h>
+#include <string.h>
+
+static void usage (void);
+static bool make_tar_archive (const char *archive_name,
+ char *files[], size_t file_cnt);
+
+int
+main (int argc, char *argv[])
+{
+ if (argc < 3)
+ usage ();
+
+ return (make_tar_archive (argv[1], argv + 2, argc - 2)
+ ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void
+usage (void)
+{
+ printf ("tar, tar archive creator\n"
+ "Usage: tar ARCHIVE FILE...\n"
+ "where ARCHIVE is the tar archive to create\n"
+ " and FILE... is a list of files or directories to put into it.\n"
+ "(ARCHIVE itself will not be included in the archive, even if it\n"
+ "is in a directory to be archived.)\n");
+ exit (EXIT_FAILURE);
+}
+
+static bool archive_file (char file_name[], size_t file_name_size,
+ int archive_fd, bool *write_error);
+
+static bool archive_ordinary_file (const char *file_name, int file_fd,
+ int archive_fd, bool *write_error);
+static bool archive_directory (char file_name[], size_t file_name_size,
+ int file_fd, int archive_fd, bool *write_error);
+static bool write_header (const char *file_name,
+ char type_flag, int size, unsigned mode,
+ int archive_fd, bool *write_error);
+
+static bool do_write (int fd, const char *buffer, int size, bool *write_error);
+
+static bool
+make_tar_archive (const char *archive_name, char *files[], size_t file_cnt)
+{
+ static const char zeros[512];
+ int archive_fd;
+ bool success = true;
+ bool write_error = false;
+ size_t i;
+
+ if (!create (archive_name, 0))
+ {
+ printf ("%s: create failed\n", archive_name);
+ return false;
+ }
+ archive_fd = open (archive_name);
+ if (archive_fd < 0)
+ {
+ printf ("%s: open failed\n", archive_name);
+ return false;
+ }
+
+ for (i = 0; i < file_cnt; i++)
+ {
+ char file_name[128];
+
+ strlcpy (file_name, files[i], sizeof file_name);
+ if (!archive_file (file_name, sizeof file_name,
+ archive_fd, &write_error))
+ success = false;
+ }
+
+ if (!do_write (archive_fd, zeros, 512, &write_error)
+ || !do_write (archive_fd, zeros, 512, &write_error))
+ success = false;
+
+ close (archive_fd);
+
+ return success;
+}
+
+static bool
+archive_file (char file_name[], size_t file_name_size,
+ int archive_fd, bool *write_error)
+{
+ int file_fd = open (file_name);
+ if (file_fd >= 0)
+ {
+ bool success;
+
+ if (inumber (file_fd) != inumber (archive_fd))
+ {
+ if (!isdir (file_fd))
+ success = archive_ordinary_file (file_name, file_fd,
+ archive_fd, write_error);
+ else
+ success = archive_directory (file_name, file_name_size, file_fd,
+ archive_fd, write_error);
+ }
+ else
+ {
+ /* Nothing to do: don't try to archive the archive file. */
+ success = true;
+ }
+
+ close (file_fd);
+
+ return success;
+ }
+ else
+ {
+ printf ("%s: open failed\n", file_name);
+ return false;
+ }
+}
+
+static bool
+archive_ordinary_file (const char *file_name, int file_fd,
+ int archive_fd, bool *write_error)
+{
+ bool read_error = false;
+ bool success = true;
+ int file_size = filesize (file_fd);
+
+ if (!write_header (file_name, '0', file_size, 0644, archive_fd, write_error))
+ return false;
+
+ while (file_size > 0)
+ {
+ static char buf[512];
+ int chunk_size = file_size > 512 ? 512 : file_size;
+ int read_retval = read (file_fd, buf, chunk_size);
+ int bytes_read = read_retval > 0 ? read_retval : 0;
+
+ if (bytes_read != chunk_size && !read_error)
+ {
+ printf ("%s: read error\n", file_name);
+ read_error = true;
+ success = false;
+ }
+
+ memset (buf + bytes_read, 0, 512 - bytes_read);
+ if (!do_write (archive_fd, buf, 512, write_error))
+ success = false;
+
+ file_size -= chunk_size;
+ }
+
+ return success;
+}
+
+static bool
+archive_directory (char file_name[], size_t file_name_size, int file_fd,
+ int archive_fd, bool *write_error)
+{
+ size_t dir_len;
+ bool success = true;
+
+ dir_len = strlen (file_name);
+ if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size)
+ {
+ printf ("%s: file name too long\n", file_name);
+ return false;
+ }
+
+ if (!write_header (file_name, '5', 0, 0755, archive_fd, write_error))
+ return false;
+
+ file_name[dir_len] = '/';
+ while (readdir (file_fd, &file_name[dir_len + 1]))
+ if (!archive_file (file_name, file_name_size, archive_fd, write_error))
+ success = false;
+ file_name[dir_len] = '\0';
+
+ return success;
+}
+
+static bool
+write_header (const char *file_name,
+ char type_flag, int size, unsigned mode,
+ int archive_fd, bool *write_error)
+{
+ static char header[512];
+ unsigned chksum;
+ size_t i;
+
+ memset (header, 0, sizeof header);
+
+ /* Drop confusing and possibly dangerous prefixes from
+ FILE_NAME. */
+ while (*file_name == '/'
+ || !memcmp (file_name, "./", 2)
+ || !memcmp (file_name, "../", 3))
+ file_name = strchr (file_name, '/') + 1;
+ if (*file_name == '\0')
+ {
+ /* Dropped *everything* from FILE_NAME.
+ Should only be possible for a directory. */
+ ASSERT (type_flag == '5');
+ return true;
+ }
+ else if (strlen (file_name) > 99)
+ {
+ printf ("%s: file name too long\n", file_name);
+ return false;
+ }
+
+ /* Fill in header except for final checksum. */
+ strlcpy (header, file_name, 100); /* name */
+ snprintf (header + 100, 8, "%07o", mode); /* mode */
+ strlcpy (header + 108, "0000000", 8); /* uid */
+ strlcpy (header + 116, "0000000", 8); /* gid */
+ snprintf (header + 124, 12, "%011o", size); /* size */
+ snprintf (header + 136, 12, "%011o", 1136102400); /* mtime (2006-01-01) */
+ memset (header + 148, ' ', 8); /* chksum */
+ header[156] = type_flag; /* typeflag */
+ strlcpy (header + 257, "ustar", 6); /* magic */
+ strlcpy (header + 263, "00", 3); /* version */
+
+ /* Compute and fill in final checksum. */
+ chksum = 0;
+ for (i = 0; i < 512; i++)
+ chksum += (uint8_t) header[i];
+ snprintf (header + 148, 8, "%07o", chksum);
+
+ /* Write header. */
+ return do_write (archive_fd, header, 512, write_error);
+}
+
+static bool
+do_write (int fd, const char *buffer, int size, bool *write_error)
+{
+ if (write (fd, buffer, size) == size)
+ return true;
+ else
+ {
+ if (!*write_error)
+ {
+ printf ("error writing archive\n");
+ *write_error = true;
+ }
+ return false;
+ }
+}
diff --git a/src/tests/filesys/seq-test.c b/src/tests/filesys/seq-test.c
new file mode 100644
index 0000000..8ce222c
--- /dev/null
+++ b/src/tests/filesys/seq-test.c
@@ -0,0 +1,37 @@
+#include "tests/filesys/seq-test.h"
+#include <random.h>
+#include <syscall.h>
+#include "tests/lib.h"
+
+void
+seq_test (const char *file_name, void *buf, size_t size, size_t initial_size,
+ size_t (*block_size_func) (void),
+ void (*check_func) (int fd, long ofs))
+{
+ size_t ofs;
+ int fd;
+
+ random_bytes (buf, size);
+ CHECK (create (file_name, initial_size), "create \"%s\"", file_name);
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+
+ ofs = 0;
+ msg ("writing \"%s\"", file_name);
+ while (ofs < size)
+ {
+ size_t block_size = block_size_func ();
+ if (block_size > size - ofs)
+ block_size = size - ofs;
+
+ if (write (fd, buf + ofs, block_size) != (int) block_size)
+ fail ("write %zu bytes at offset %zu in \"%s\" failed",
+ block_size, ofs, file_name);
+
+ ofs += block_size;
+ if (check_func != NULL)
+ check_func (fd, ofs);
+ }
+ msg ("close \"%s\"", file_name);
+ close (fd);
+ check_file (file_name, buf, size);
+}
diff --git a/src/tests/filesys/seq-test.h b/src/tests/filesys/seq-test.h
new file mode 100644
index 0000000..0697381
--- /dev/null
+++ b/src/tests/filesys/seq-test.h
@@ -0,0 +1,11 @@
+#ifndef TESTS_FILESYS_SEQ_TEST_H
+#define TESTS_FILESYS_SEQ_TEST_H
+
+#include <stddef.h>
+
+void seq_test (const char *file_name,
+ void *buf, size_t size, size_t initial_size,
+ size_t (*block_size_func) (void),
+ void (*check_func) (int fd, long ofs));
+
+#endif /* tests/filesys/seq-test.h */