* [PATCH 0/9] cr_tests: Filesystem tests
@ 2010-02-20 2:18 Matt Helsley
[not found] ` <1266632337-13792-1-git-send-email-matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
0 siblings, 1 reply; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
These filesystem-related tests for unlinked file support and
dnotify are expected to fail so long as those features are
unsupported by c/r.
PATCH 1/9 Add tests for checkpoint/restart of unlinked files.
PATCH 2/9 Add a do_ckpt utility (for python scripts)
PATCH 3/9 A tool to create a random tree of dirs and files
PATCH 4/9 A tool to re-fill a tree of dirs and files
PATCH 5/9 A tool to check a tree of files and dirs
PATCH 6/9 Add script to hold an unlinked tree
PATCH 7/9 Add random tree test
PATCH 8/9 Add unlinked fifo test
PATCH 9/9 Add fsnotify test for dnotify
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/9] cr_tests: fs: Add tests for checkpoint/restart of unlinked files.
[not found] ` <1266632337-13792-1-git-send-email-matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2010-02-20 2:18 ` Matt Helsley
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2010-02-22 18:02 ` [PATCH 0/9] cr_tests: Filesystem tests Serge E. Hallyn
1 sibling, 1 reply; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
These tests create unlinked files and produce conflicting file system
objects which complicate restart for naieve implementations of
checkpoint/restart. It further complicates matters by allowing test
driver scripts to set special inode flags (append, immutable, undelete).
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/dir.c | 134 ++++++++++++++++++++++++++++++++
fs/file.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/libfstest.c | 12 +++
fs/libfstest.h | 34 ++++++++
fs/module.mk | 12 +++
5 files changed, 428 insertions(+), 0 deletions(-)
create mode 100644 fs/dir.c
create mode 100644 fs/file.c
create mode 100644 fs/libfstest.c
create mode 100644 fs/libfstest.h
create mode 100644 fs/module.mk
diff --git a/fs/dir.c b/fs/dir.c
new file mode 100644
index 0000000..daa85fc
--- /dev/null
+++ b/fs/dir.c
@@ -0,0 +1,134 @@
+/*
+ * Test checkpoint/restart of unlinked files.
+ * Currently expected to fail.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/syscall.h> /* SYS_getdents */
+
+/* open() */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* waitpid() and W* status macros */
+#include <sys/wait.h>
+
+#define LOG_FILE "log.unlinked.dir"
+#include "libfstest.h"
+
+const char *descr = "Create an unlinked file to checkpoint/restore.";
+
+void usage(FILE *pout, const char *prog)
+{
+ fprintf(pout, "\n%s [-L] [-N] [-h|--help] [-l LABEL] [-n NUM]\n"
+"%s\n"
+"\n"
+"\t-L\tPrint the valid LABELs in order and exit.\n"
+"\t-l\tWait for checkpoint at LABEL.\n"
+"\t-N\tPrint the maximum label number and exit.\n"
+"\t-n\tWait for checkpoint at NUM.\n"
+"\n"
+"You may only specify one LABEL or NUM and you may not specify both.\n"
+"Label numbers are integers in the range 0-%d\n"
+"Valid label numbers and their corresponding LABELs are:\n", prog,
+ descr, num_labels - 1);
+ print_labels(pout);
+}
+
+const struct option long_options[] = {
+ { "print-labels", 0, 0, 'L'},
+ { "print-max-label-no", 0, 0, 'N'},
+ { "help", 0, 0, 'h'},
+ { "label", 1, 0, 'l'},
+ { "num", 1, 0, 'n'},
+ {0, 0, 0, 0},
+};
+
+void parse_args(int argc, char **argv)
+{
+ ckpt_op_num = num_labels - 1;
+ ckpt_label = labels[ckpt_op_num];
+ while (1) {
+ char c;
+ c = getopt_long(argc, argv, "LNhl:n:", long_options, NULL);
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'L':
+ print_labels(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'N':
+ printf("%d\n", num_labels - 1);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'h':
+ usage(stdout, argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'l':
+ ckpt_label = optarg;
+ break;
+ case 'n':
+ if ((sscanf(optarg, "%d", &ckpt_op_num) < 1) ||
+ (ckpt_op_num < 0) ||
+ (ckpt_op_num >= num_labels)) {
+ fprintf(stderr, "Option -n requires an argument in the range 0-%d. Got %d\n", num_labels - 1, ckpt_op_num);
+ usage(stderr, argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ default: /* unknown option */
+ break;
+ }
+ }
+}
+
+/*
+ * A LABEL is a point in the program we can goto where it's interesting to
+ * checkpoint. These enable us to have a set of labels that can be specified
+ * on the commandline.
+ */
+int main (int argc, char **argv)
+{
+ const char *pathname = "trash";
+ const char buffer[] = "hello world!\n";
+ char fdcontents[sizeof(buffer)];
+ int fd, ret, op_num = 0;
+
+ parse_args(argc, argv);
+ /* FIXME eventually stdio streams should be harmless */
+ close(0);
+ logfp = fopen(LOG_FILE, "w");
+ if (!logfp) {
+ perror("could not open logfile");
+ exit(1);
+ }
+ dup2(fileno(logfp), 1); /* redirect stdout and stderr to the log file */
+ dup2(fileno(logfp), 2);
+ if (!move_to_cgroup("freezer", "1", getpid())) {
+ log_error("move_to_cgroup");
+ exit(2);
+ }
+
+label(mkdir, ret, mkdir(pathname, S_IRWXU));
+label(open, fd, open(pathname, O_RDONLY|O_DIRECTORY));
+label(rmdir, ret, rmdir(pathname));
+label(getdent,ret, syscall(SYS_getdents, fd, buffer, sizeof(buffer)));
+label(close, ret, close(fd));
+
+ if (strcmp(buffer, fdcontents) != 0) {
+ log("FAIL", "contents don't match.");
+ ret = EXIT_FAILURE;
+ } else
+ ret = EXIT_SUCCESS;
+out:
+ close(fd);
+ fclose(logfp);
+ exit(ret);
+}
diff --git a/fs/file.c b/fs/file.c
new file mode 100644
index 0000000..bb17058
--- /dev/null
+++ b/fs/file.c
@@ -0,0 +1,236 @@
+/*
+ * Test checkpoint/restart of unlinked files.
+ *
+ * TODO with append extended attribute, with immutable extended attribute,
+ * with undelete extended attribute (not honored by extX)
+ * Currently expected to fail.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+
+/* open() */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* set inode flags */
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+/* waitpid() and W* status macros */
+#include <sys/wait.h>
+
+#define LOG_FILE "log.unlinked.file"
+#include "libfstest.h"
+
+const char *descr = "Create an unlinked file to checkpoint/restore.";
+
+void usage(FILE *pout, const char *prog)
+{
+ fprintf(pout, "\n%s [-L] [-N] [-h|--help] [-l LABEL] [-n NUM]\n"
+"%s\n"
+"\n"
+"\t-L\tPrint the valid LABELs in order and exit.\n"
+"\t-l\tWait for checkpoint at LABEL.\n"
+"\t-N\tPrint the maximum label number and exit.\n"
+"\t-n\tWait for checkpoint at NUM.\n"
+"\t-a\tSet the append-only flag (extX filesystems, needs cap_linux_immutable).\n"
+"\t-i\tSet the immutable flag (extX filesystems, needs cap_linux_immutable).\n"
+"\t-u\tSet the undeleteable (recoverable) flag (extX filesystems).\n"
+"\n"
+"You may only specify one LABEL or NUM and you may not specify both.\n"
+"Label numbers are integers in the range 0-%d\n"
+"Valid label numbers and their corresponding LABELs are:\n", prog,
+ descr, num_labels - 1);
+ print_labels(pout);
+}
+
+const struct option long_options[] = {
+ { "print-labels", 0, 0, 'L'},
+ { "print-max-label-no", 0, 0, 'N'},
+ { "help", 0, 0, 'h'},
+ { "append", 0, 0, 'a'}, /* Need CAP_LINUX_IMMUTABLE */
+ { "immutable", 0, 0, 'i'}, /* Need CAP_LINUX_IMMUTABLE */
+ { "undelete", 0, 0, 'u'}, /* unsupported */
+ { "label", 1, 0, 'l'},
+ { "num", 1, 0, 'n'},
+ {0, 0, 0, 0},
+};
+
+static int inode_flags = 0;
+static int oflags = O_RDWR;
+
+void parse_args(int argc, char **argv)
+{
+ ckpt_op_num = num_labels - 1;
+ ckpt_label = labels[ckpt_op_num];
+ while (1) {
+ char c;
+ c = getopt_long(argc, argv, "LNhl:n:aiu", long_options, NULL);
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'L':
+ print_labels(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'N':
+ printf("%d\n", num_labels - 1);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'h':
+ usage(stdout, argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'a':
+ oflags |= O_APPEND;
+ inode_flags |= FS_APPEND_FL;
+ break;
+ case 'i':
+ inode_flags |= FS_IMMUTABLE_FL;
+ break;
+ case 'u':
+ inode_flags |= FS_UNRM_FL;
+ break;
+ case 'l':
+ ckpt_label = optarg;
+ break;
+ case 'n':
+ if ((sscanf(optarg, "%d", &ckpt_op_num) < 1) ||
+ (ckpt_op_num < 0) ||
+ (ckpt_op_num >= num_labels)) {
+ fprintf(stderr, "Option -n requires an argument in the range 0-%d. Got %d\n", num_labels - 1, ckpt_op_num);
+ usage(stderr, argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ default: /* unknown option */
+ break;
+ }
+ }
+}
+
+static int setflags(int fd)
+{
+ int flags = inode_flags;
+
+ if (!flags)
+ return 0;
+#ifdef FS_IOC_SETFLAGS
+ return ioctl(fd, FS_IOC_SETFLAGS, &flags);
+#else
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
+int main (int argc, char **argv)
+{
+ const char *pathname = "trash";
+ const char buffer[] = "hello world!\n";
+ const char buffer2[] = "goodbye old world, hello new world!\n";
+ char fdcontents[sizeof(buffer)];
+ char fdcontents2[sizeof(buffer2)];
+ int fd, fd2 = -1, ret, op_num = 0;
+
+ parse_args(argc, argv);
+ /* FIXME eventually stdio streams should be harmless */
+ close(0);
+ logfp = fopen(LOG_FILE, "w");
+ if (!logfp) {
+ perror("could not open logfile");
+ exit(1);
+ }
+ dup2(fileno(logfp), 1); /* redirect stdout and stderr to the log file */
+ dup2(fileno(logfp), 2);
+ if (!move_to_cgroup("freezer", "1", getpid())) {
+ log_error("move_to_cgroup");
+ exit(2);
+ }
+
+label(creat1,fd, creat(pathname, oflags));
+ if (fd < 0)
+ exit(EXIT_FAILURE);
+label(setflags1, ret, setflags(fd));
+ if (ret < 0)
+ goto out;
+label(write1, ret, write(fd, buffer, sizeof(buffer)));
+ if (ret < 0)
+ goto out;
+label(unlink, ret, unlink(pathname));
+ if (ret < 0)
+ goto out;
+
+ /* Simple unlinked file */
+ /* This is a good time to do_ckpt(); */
+
+ /* Check file contents */
+label(lseek, ret, lseek(fd, 0, SEEK_SET));
+ if (ret < 0)
+ goto out;
+label(read, ret, read(fd, fdcontents, sizeof(fdcontents)));
+ if (ret < 0)
+ goto out;
+ if (strcmp(buffer, fdcontents) != 0) {
+ log("FAIL", "original file contents don't match.");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ /*
+ * Create a second file where the first used to be but do not
+ * destroy the data of the original.
+ */
+label(creat2,fd2, creat(pathname, O_RDWR));
+ ret = fd2;
+ if (fd2 < 0)
+ goto out;
+label(setflags2, ret, setflags(fd));
+ if (ret < 0)
+ goto out;
+label(write2, ret, write(fd2, buffer2, sizeof(buffer2)));
+ if (ret < 0)
+ goto out;
+
+ /* This is a good time to do_ckpt(); */
+
+ /* Check file contents (both this time) */
+label(lseek2, ret, lseek(fd, 0, SEEK_SET));
+ if (ret < 0)
+ goto out;
+label(read2, ret, read(fd, fdcontents, sizeof(fdcontents)));
+ if (ret < 0)
+ goto out;
+
+ if (strcmp(buffer, fdcontents) != 0) {
+ log("FAIL", "original file contents don't match.");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+label(lseek3, ret, lseek(fd2, 0, SEEK_SET));
+ if (ret < 0)
+ goto out;
+label(read3, ret, read(fd2, fdcontents2, sizeof(fdcontents2)));
+ if (ret < 0)
+ goto out;
+
+ if (strcmp(buffer2, fdcontents2) != 0) {
+ log("FAIL", "second file contents don't match.");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ ret = EXIT_SUCCESS;
+out:
+ if (ret != EXIT_SUCCESS)
+ perror("ERROR");
+ close(fd);
+ if (fd2 > -1)
+ close(fd2);
+ unlink(pathname);
+ fclose(logfp);
+ exit(ret);
+}
diff --git a/fs/libfstest.c b/fs/libfstest.c
new file mode 100644
index 0000000..200dc23
--- /dev/null
+++ b/fs/libfstest.c
@@ -0,0 +1,12 @@
+#include "libfstest.h"
+
+FILE *logfp = NULL;
+
+/* Signal ready for and await the checkpoint */
+void do_ckpt(void)
+{
+ set_checkpoint_ready();
+ while (!test_checkpoint_done())
+ usleep(10000);
+
+}
diff --git a/fs/libfstest.h b/fs/libfstest.h
new file mode 100644
index 0000000..1ef3c5c
--- /dev/null
+++ b/fs/libfstest.h
@@ -0,0 +1,34 @@
+/* epoll syscalls */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <sys/epoll.h>
+
+#include "libcrtest/libcrtest.h"
+#include "libcrtest/labels.h"
+
+extern FILE *logfp;
+
+/*
+ * Log output with a tag (INFO, WARN, FAIL, PASS) and a format.
+ * Adds information about the thread originating the message.
+ *
+ * Flush the log after every write to make sure we get consistent, and
+ * complete logs.
+ */
+#define log(tag, fmt, ...) \
+do { \
+ pid_t __tid = getpid(); \
+ fprintf(logfp, ("%s: thread %d: " fmt), (tag), __tid, ##__VA_ARGS__ ); \
+ fflush(logfp); \
+ fsync(fileno(logfp)); \
+} while(0)
+
+/* like perror() except to the log */
+#define log_error(s) log("FAIL", "%s: %s\n", (s), strerror(errno))
+
+/* Signal ready for and await the checkpoint */
+void do_ckpt(void);
+
+#define HELLO "Hello world!\n"
diff --git a/fs/module.mk b/fs/module.mk
new file mode 100644
index 0000000..d0a9f26
--- /dev/null
+++ b/fs/module.mk
@@ -0,0 +1,12 @@
+local_dir := fs
+local_progs := $(addprefix $(local_dir)/, file dir)
+
+sources += $(addprefix $(local_dir)/,libeptest.c)
+progs += $(local_progs)
+test_clean += $(local_dir)/cr_fs*
+
+# fs tests include libcrtest/libcrtest.h
+$(local_progs): CFLAGS += -D_GNU_SOURCE=1
+$(local_progs): CPPFLAGS += -I .
+$(local_progs): LDFLAGS += -Xlinker -dT -Xlinker libcrtest/labels.lds
+$(local_progs): $(local_dir)/libfstest.o libcrtest/libcrtest.a
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/9] cr_tests: fs: Add a do_ckpt utility
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2010-02-20 2:18 ` Matt Helsley
2010-02-20 2:18 ` [PATCH 3/9] cr_tests: fs: A tool to create a random tree of dirs and files Matt Helsley
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
Makes ./checkpoint-ready, waits for ./checkpoint-done, and exits.
This utility will be useful in a python test script to ensure that
we're not testing the checkpointability of python scripts and
instead we're checkpointing simple C tasks.
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/do_ckpt.c | 34 ++++++++++++++++++++++++++++++++++
fs/module.mk | 2 +-
2 files changed, 35 insertions(+), 1 deletions(-)
create mode 100644 fs/do_ckpt.c
diff --git a/fs/do_ckpt.c b/fs/do_ckpt.c
new file mode 100644
index 0000000..6c96cae
--- /dev/null
+++ b/fs/do_ckpt.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+
+/* open() */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define LOG_FILE "log.do_ckpt"
+#include "libfstest.h"
+
+int main(int argc, char** argv)
+{
+ /* FIXME eventually stdio streams should be harmless */
+ close(0);
+ logfp = fopen(LOG_FILE, "w");
+ if (!logfp) {
+ perror("could not open logfile");
+ exit(1);
+ }
+ dup2(fileno(logfp), 1); /* redirect stdout and stderr to the log file */
+ dup2(fileno(logfp), 2);
+ if (!move_to_cgroup("freezer", "1", getpid())) {
+ log_error("move_to_cgroup");
+ exit(2);
+ }
+
+ do_ckpt();
+ exit(EXIT_SUCCESS);
+}
diff --git a/fs/module.mk b/fs/module.mk
index d0a9f26..1af5815 100644
--- a/fs/module.mk
+++ b/fs/module.mk
@@ -1,5 +1,5 @@
local_dir := fs
-local_progs := $(addprefix $(local_dir)/, file dir)
+local_progs := $(addprefix $(local_dir)/, file dir do_ckpt)
sources += $(addprefix $(local_dir)/,libeptest.c)
progs += $(local_progs)
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/9] cr_tests: fs: A tool to create a random tree of dirs and files
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2010-02-20 2:18 ` [PATCH 2/9] cr_tests: fs: Add a do_ckpt utility Matt Helsley
@ 2010-02-20 2:18 ` Matt Helsley
2010-02-20 2:18 ` [PATCH 4/9] cr_tests: fs: A tool to re-fill a " Matt Helsley
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/rand_tree.py | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 113 insertions(+), 0 deletions(-)
create mode 100755 fs/rand_tree.py
diff --git a/fs/rand_tree.py b/fs/rand_tree.py
new file mode 100755
index 0000000..54db43f
--- /dev/null
+++ b/fs/rand_tree.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+#
+# Generate a random tree of files and dirs with known mode bits and
+# contents.
+#
+import optparse
+import sys
+import random
+import cPickle
+
+# for S_I* (e.g. S_IRWXU)
+from stat import *
+
+# For walk, path.join, open, unlink, close, and O_*
+from os import *
+
+# Root of the tree to generate
+root = None
+
+# Like os.walk() but assumes root and followlinks = False
+# (which prevents likely-unwanted removal of files for the
+# purposes of this test script)
+def rwalk(**kw):
+ kw['followlinks'] = False
+ yield walk(root, **kw)
+
+# Alias path.join -> join
+def join(*args):
+ return path.join(*args)
+
+n_dirs = 10
+n_files = 100
+
+all_dir_paths = []
+
+# Get a random path from root
+def random_walk():
+ wchoice = random.choice(all_dir_paths)
+ return wchoice
+
+#
+# Create a random directory path in the tree that is, at most,
+# one level deeper than the depth of the tree.
+# NOTE: Naming relies on creating the dirs between each call!
+#
+def random_dir():
+ parent_dir = random_walk()
+ dname = str(len(listdir(parent_dir)) + 1)
+ dpath = join(parent_dir, dname)
+ all_dir_paths.append(dpath)
+ mkdir(dpath, S_IRWXU)
+
+# Create a random file path in the tree
+def random_file():
+ parent_dir = random_walk()
+ fname = str(len(listdir(parent_dir)) + 1)
+ return join(parent_dir, fname)
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser()
+ parser.add_option("-r", "--root", dest="root", type="string",
+ action="store", help="root of tree to hold and unlink")
+ parser.add_option("-c", "--content-tag", dest="content", type="string",
+ default="original", action="store",
+ help="tag for contents of the files (e.g. \"original\", \"dummy\")")
+ parser.add_option("-d", "--num-directories", type="int", dest="n_dirs",
+ default=random.randint(0, n_dirs),
+ action="store", help="number of directories to create")
+ parser.add_option("-f", "--num-files", type="int", dest="n_files",
+ default=random.randint(0, n_files),
+ action="store", help="number of files to create")
+ parser.add_option("-s", "--seed", type="int", dest="random_seed",
+ default=None,
+ action="store", help="random seed to use")
+ parser.add_option("-S", "--random-state", type="string",
+ dest="random_state", default=None,
+ action="store", help="random state to use")
+ (options, args) = parser.parse_args()
+ root = path.abspath(options.root)
+ all_dir_paths.append(root)
+ n_dirs = options.n_dirs
+ n_files = options.n_files
+ content = options.content
+
+ # Set the random seed or random state
+ if options.random_seed is None and options.random_state is None:
+ random.seed()
+
+ # Output the random state so, presumably, we can recreate this
+ # same test tree.
+ sys.stdout.write(cPickle.dumps(random.getstate()))
+ elif not options.random_seed is None:
+ random.seed(options.random_seed)
+ elif not options.random_state is None:
+ random_state = cPickle.loads(options.random_state)
+ random.setstate(random_state)
+
+ # Ensure that the tree root exists
+ try:
+ makedirs(root, S_IRWXU)
+ except:
+ pass
+
+ while n_dirs > 0:
+ random_dir()
+ n_dirs = n_dirs - 1
+
+ while n_files > 0:
+ f = random_file()
+ fd = open(f, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR)
+ write(fd, "%s contents of %s\n" % (content, f))
+ close(fd)
+ n_files = n_files - 1
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/9] cr_tests: fs: A tool to re-fill a tree of dirs and files
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2010-02-20 2:18 ` [PATCH 2/9] cr_tests: fs: Add a do_ckpt utility Matt Helsley
2010-02-20 2:18 ` [PATCH 3/9] cr_tests: fs: A tool to create a random tree of dirs and files Matt Helsley
@ 2010-02-20 2:18 ` Matt Helsley
2010-02-20 2:18 ` [PATCH 5/9] cr_tests: fs: A tool to check a tree of files and dirs Matt Helsley
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/fill_tree.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 47 insertions(+), 0 deletions(-)
create mode 100755 fs/fill_tree.py
diff --git a/fs/fill_tree.py b/fs/fill_tree.py
new file mode 100755
index 0000000..28a3e55
--- /dev/null
+++ b/fs/fill_tree.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+#
+# fill a tree with known contents.
+#
+import optparse
+import sys
+
+# for S_I* (e.g. S_IRWXU)
+from stat import *
+
+# For walk, path.join, open, unlink, close, and O_*
+from os import *
+
+# Root of the tree to check
+root = None
+
+# Like os.walk() but assumes root and followlinks = False
+# (which prevents likely-unwanted removal of files for the
+# purposes of this test script)
+def rwalk(**kw):
+ kw['followlinks'] = False
+ return walk(root, **kw)
+
+# Alias path.join -> join
+def join(*args):
+ return path.join(*args)
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser()
+ parser.add_option("-r", "--root", dest="root", type="string",
+ action="store", help="root of tree to hold and unlink")
+ parser.add_option("-c", "--content-tag", dest="content", type="string",
+ default="dummy", action="store",
+ help="tag for contents of the files (e.g. \"original\", \"dummy\")")
+ (options, args) = parser.parse_args()
+ root = path.abspath(options.root)
+ content = options.content
+
+ for (dirpath, dirnames, filenames) in rwalk():
+ for dir in dirnames:
+ chmod(join(dirpath, dir), S_IRWXU)
+ for fname in filenames:
+ f = join(dirpath, fname)
+ chmod(f, S_IRUSR|S_IWUSR)
+ fd = open(f, O_WRONLY|O_TRUNC)
+ write(fd, "%s contents of %s\n" % (content, f))
+ close(fd)
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/9] cr_tests: fs: A tool to check a tree of files and dirs
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
` (2 preceding siblings ...)
2010-02-20 2:18 ` [PATCH 4/9] cr_tests: fs: A tool to re-fill a " Matt Helsley
@ 2010-02-20 2:18 ` Matt Helsley
2010-02-20 2:18 ` [PATCH 6/9] cr_tests: fs: Add script to hold an unlinked tree Matt Helsley
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/check_tree.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 60 insertions(+), 0 deletions(-)
create mode 100755 fs/check_tree.py
diff --git a/fs/check_tree.py b/fs/check_tree.py
new file mode 100755
index 0000000..d757f66
--- /dev/null
+++ b/fs/check_tree.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+#
+# Open everything in a tree and check for known contents.
+#
+# NOTE: Does not detect if there are more files and directories
+# beyond the "original" tree if the new ones follow the
+# scheme detected here.
+#
+import optparse
+import sys
+
+# for S_I* (e.g. S_IRWXU)
+from stat import *
+
+# For walk, path.join, open, unlink, close, and O_*
+from os import *
+
+# Root of the tree to check
+root = None
+
+# Like os.walk() but assumes root and followlinks = False
+# (which prevents likely-unwanted removal of files for the
+# purposes of this test script)
+def rwalk(**kw):
+ kw['followlinks'] = False
+ return walk(root, **kw)
+
+# Alias path.join -> join
+def join(*args):
+ return path.join(*args)
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser()
+ parser.add_option("-r", "--root", dest="root", type="string",
+ action="store", help="root of tree to hold and unlink")
+ parser.add_option("-c", "--content-tag", dest="content", type="string",
+ default="dummy", action="store",
+ help="tag for contents of the files (e.g. \"original\", \"dummy\")")
+ (options, args) = parser.parse_args()
+ root = path.abspath(options.root)
+ content = options.content
+
+ for (dirpath, dirnames, filenames) in rwalk():
+ for dir in dirnames:
+ if not access(join(dirpath, dir), R_OK|W_OK|X_OK):
+ exit(EX_NOPERM)
+ for fname in filenames:
+ f = join(dirpath, fname)
+ fd = open(f, O_RDONLY, S_IRUSR|S_IWUSR) # does access()
+ expected_contents = "%s contents of %s\n" % (content, f)
+ contents = read(fd, len(expected_contents))
+ if read(fd, 2) != '':
+ sys.stderr.write('Extra contents of file \"%s\": Got \"%s\" and more but expected \"%s\"\n' % (f, contents, expected_contents))
+ sys.exit(EX_DATAERR) # not at EOF
+ close(fd)
+ if expected_contents == contents:
+ continue
+ sys.stderr.write('Contents mismatch \"%s\": Got \"%s\" but expected \"%s\"\n' % (f, contents, expected_contents))
+ sys.exit(EX_DATAERR)
+ sys.exit(EX_OK)
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 6/9] cr_tests: fs: Add script to hold an unlinked tree
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
` (3 preceding siblings ...)
2010-02-20 2:18 ` [PATCH 5/9] cr_tests: fs: A tool to check a tree of files and dirs Matt Helsley
@ 2010-02-20 2:18 ` Matt Helsley
2010-02-20 2:18 ` [PATCH 7/9] cr_tests: fs: Add random tree test Matt Helsley
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
This python script opens a tree, unlinks the whole thing, and
waits for checkpoint. A subsequent patch will make a tree
and run this script on it as a test.
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/hold_unlinked_tree.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 85 insertions(+), 0 deletions(-)
create mode 100755 fs/hold_unlinked_tree.py
diff --git a/fs/hold_unlinked_tree.py b/fs/hold_unlinked_tree.py
new file mode 100755
index 0000000..c56222b
--- /dev/null
+++ b/fs/hold_unlinked_tree.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+#
+# Open everything in a tree, unlink the tree, and optional create new
+# things where the tree once was.
+#
+import optparse
+import sys
+
+# for S_I* (e.g. S_IRWXU)
+from stat import *
+
+# For walk, path.join, open, unlink, close, and O_*
+from os import *
+
+# List of open fds
+open_fds = []
+
+# Lists of dirs and files to "re-create"
+mkdirs = []
+mkfiles = []
+
+# Root of the tree to hold
+root = None
+
+# Like os.walk() but assumes root and followlinks = False
+# (which prevents likely-unwanted removal of files for the
+# purposes of this test script)
+def rwalk(**kw):
+ kw['followlinks'] = False
+ return walk(root, **kw)
+
+# Alias path.join -> join
+def join(*args):
+ return path.join(*args)
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser()
+ parser.add_option("-r", "--root", dest="root", type="string",
+ default="./hold_root",
+ action="store", help="root of tree to hold and unlink")
+ parser.add_option("-n", "--new-tree", dest="mknew",
+ action="store_true", help="remake tree")
+ (options, args) = parser.parse_args()
+ root = path.abspath(options.root)
+ do_mk = options.mknew
+ mkdirs.append(root)
+
+ # Hold open fds to everything in the tree
+ for (dirpath, dirnames, filenames) in rwalk():
+ for fname in filenames:
+ fpath = join(dirpath, fname)
+ fd = open(join(dirpath, fname), O_RDONLY)
+ open_fds.append(fd);
+ unlink(fpath);
+ # Record the files to re-create
+ mkfiles.append(fpath)
+ for dname in dirnames:
+ fd = open(join(dirpath, dname), O_RDONLY|O_DIRECTORY)
+ open_fds.append(fd);
+ # Can't unlink the dirs yet
+ # Record the directories to re-create, top-down
+ mkdirs.append(join(dirpath, dname))
+
+ # Unlink dirs, bottom up
+ for (dirpath, dirnames, filenames) in rwalk(topdown=False):
+ for dname in dirnames:
+ rmdir(join(dirpath, dname))
+ rmdir(root)
+
+ if do_mk:
+ # Make a new tree over the old one
+ for dir in mkdirs:
+ mkdir(dir, S_IRWXU)
+ for f in mkfiles:
+ fd = open(f, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR)
+ write(fd, "new contents of %s\n" % (f))
+ close(fd)
+ mkdirs = None
+ mkfiles = None
+
+ sys.stdout.flush()
+
+ # Sleep until signalled, then exit. NOTE: assumes
+ # O_CLOEXEC is not set on fds.
+ execv("./do_ckpt", [ "./do_ckpt" ])
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 7/9] cr_tests: fs: Add random tree test
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
` (4 preceding siblings ...)
2010-02-20 2:18 ` [PATCH 6/9] cr_tests: fs: Add script to hold an unlinked tree Matt Helsley
@ 2010-02-20 2:18 ` Matt Helsley
2010-02-20 2:18 ` [PATCH 8/9] cr_tests: fs: Add unlinked fifo test Matt Helsley
2010-02-20 2:18 ` [PATCH 9/9] cr_tests: fs: Add fsnotify test for dnotify Matt Helsley
7 siblings, 0 replies; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
This test creates a random tree of files and directories, opens them all,
unlinks them, "overwrites" their entries in the filesystem, and then awaits
checkpoint. Afterwards it checks the directories and file contents.
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/tree.sh | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)
create mode 100755 fs/tree.sh
diff --git a/fs/tree.sh b/fs/tree.sh
new file mode 100755
index 0000000..1323594
--- /dev/null
+++ b/fs/tree.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -e
+# Generate a random tree and check its contents
+./rand_tree.py -r ./tree -d 4 -f 10 && trap 'rm -rf ./tree' EXIT
+./check_tree.py -r ./tree -c original
+
+# Fill its contents with new dummy values
+./fill_tree.py -r ./tree -c dummy
+./check_tree.py -r ./tree -c dummy
+
+# Fill its contents with "original" values
+./fill_tree.py -r ./tree -c original
+./hold_unlinked_tree.py -r ./tree -n && \
+# Expect "new" contents
+./check_tree.py -r ./tree -c new
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 8/9] cr_tests: fs: Add unlinked fifo test
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
` (5 preceding siblings ...)
2010-02-20 2:18 ` [PATCH 7/9] cr_tests: fs: Add random tree test Matt Helsley
@ 2010-02-20 2:18 ` Matt Helsley
2010-02-20 2:18 ` [PATCH 9/9] cr_tests: fs: Add fsnotify test for dnotify Matt Helsley
7 siblings, 0 replies; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/fifo.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/module.mk | 2 +-
2 files changed, 191 insertions(+), 1 deletions(-)
create mode 100644 fs/fifo.c
diff --git a/fs/fifo.c b/fs/fifo.c
new file mode 100644
index 0000000..d0e08eb
--- /dev/null
+++ b/fs/fifo.c
@@ -0,0 +1,190 @@
+/*
+ * Test checkpoint/restart of unlinked fifos. fifos are an interesting
+ * case for unlinked file support because they offer limited buffer space
+ * but are not seekable.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+
+/* open() */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* set inode flags */
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+/* waitpid() and W* status macros */
+#include <sys/wait.h>
+
+#define LOG_FILE "log.unlinked.file"
+#include "libfstest.h"
+
+const char *descr = "Create an unlinked file to checkpoint/restore.";
+
+void usage(FILE *pout, const char *prog)
+{
+ fprintf(pout, "\n%s [-L] [-N] [-h|--help] [-l LABEL] [-n NUM]\n"
+"%s\n"
+"\n"
+"\t-L\tPrint the valid LABELs in order and exit.\n"
+"\t-l\tWait for checkpoint at LABEL.\n"
+"\t-N\tPrint the maximum label number and exit.\n"
+"\t-n\tWait for checkpoint at NUM.\n"
+"\t-a\tSet the append-only flag (extX filesystems, needs cap_linux_immutable).\n"
+"\t-i\tSet the immutable flag (extX filesystems, needs cap_linux_immutable).\n"
+"\t-u\tSet the undeleteable (recoverable) flag (extX filesystems).\n"
+"\n"
+"You may only specify one LABEL or NUM and you may not specify both.\n"
+"Label numbers are integers in the range 0-%d\n"
+"Valid label numbers and their corresponding LABELs are:\n", prog,
+ descr, num_labels - 1);
+ print_labels(pout);
+}
+
+const struct option long_options[] = {
+ { "print-labels", 0, 0, 'L'},
+ { "print-max-label-no", 0, 0, 'N'},
+ { "help", 0, 0, 'h'},
+ { "append", 0, 0, 'a'}, /* Need CAP_LINUX_IMMUTABLE */
+ { "immutable", 0, 0, 'i'}, /* Need CAP_LINUX_IMMUTABLE */
+ { "undelete", 0, 0, 'u'}, /* unsupported */
+ { "label", 1, 0, 'l'},
+ { "num", 1, 0, 'n'},
+ {0, 0, 0, 0},
+};
+
+static int inode_flags = 0;
+static int oflags = O_RDWR; /* rely on Linux-specific behavior (man 7 fifo) */
+
+void parse_args(int argc, char **argv)
+{
+ ckpt_op_num = num_labels - 1;
+ ckpt_label = labels[ckpt_op_num];
+ while (1) {
+ char c;
+ c = getopt_long(argc, argv, "LNhl:n:aiu", long_options, NULL);
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'L':
+ print_labels(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'N':
+ printf("%d\n", num_labels - 1);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'h':
+ usage(stdout, argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'a':
+ oflags |= O_APPEND;
+ inode_flags |= FS_APPEND_FL;
+ break;
+ case 'i':
+ inode_flags |= FS_IMMUTABLE_FL;
+ break;
+ case 'u':
+ inode_flags |= FS_UNRM_FL;
+ break;
+ case 'l':
+ ckpt_label = optarg;
+ break;
+ case 'n':
+ if ((sscanf(optarg, "%d", &ckpt_op_num) < 1) ||
+ (ckpt_op_num < 0) ||
+ (ckpt_op_num >= num_labels)) {
+ fprintf(stderr, "Option -n requires an argument in the range 0-%d. Got %d\n", num_labels - 1, ckpt_op_num);
+ usage(stderr, argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ default: /* unknown option */
+ break;
+ }
+ }
+}
+
+static int setflags(int fd)
+{
+ int flags = inode_flags;
+
+ if (!flags)
+ return 0;
+#ifdef FS_IOC_SETFLAGS
+ return ioctl(fd, FS_IOC_SETFLAGS, &flags);
+#else
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
+int main (int argc, char **argv)
+{
+ const char *pathname = "trash";
+ const char buffer1[] = "hello world!\n";
+ const char buffer2[] = "goodbye old world, hello new world!\n";
+ char fdcontents1[sizeof(buffer1)];
+ char fdcontents2[sizeof(buffer2)];
+ int fifo1 = -1, fifo2 = -1, ret, op_num = 0;
+
+ parse_args(argc, argv);
+ /* FIXME eventually stdio streams should be harmless */
+ close(0);
+ logfp = fopen(LOG_FILE, "w");
+ if (!logfp) {
+ perror("could not open logfile");
+ exit(1);
+ }
+ dup2(fileno(logfp), 1); /* redirect stdout and stderr to the log file */
+ dup2(fileno(logfp), 2);
+ if (!move_to_cgroup("freezer", "1", getpid())) {
+ log_error("move_to_cgroup");
+ exit(2);
+ }
+
+/* Open a first fifo, write to it, and unlink it */
+label(mkfifo1, ret, mkfifo(pathname, S_IRUSR|S_IWUSR));
+label(open1, fifo1, open(pathname, oflags));
+label(setflags1, ret, setflags(fifo1));
+label(write1, ret, write(fifo1, buffer1, sizeof(buffer1)));
+label(unlink, ret, unlink(pathname));
+
+/* Open a second fifo, write to it */
+label(mkfifo2, ret, mkfifo(pathname, S_IRUSR|S_IWUSR));
+label(open2, fifo2, open(pathname, oflags));
+label(setflags2, ret, setflags(fifo2));
+label(write2, ret, write(fifo2, buffer2, sizeof(buffer2)));
+
+/* Check fifo contents */
+label(read1, ret, read(fifo1, fdcontents1, sizeof(fdcontents1)));
+ if (strcmp(buffer1, fdcontents1) != 0) {
+ log("FAIL", "original fifo contents don't match.");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+label(read2, ret, read(fifo2, fdcontents2, sizeof(fdcontents2)));
+ if (strcmp(buffer2, fdcontents2) != 0) {
+ log("FAIL", "new fifo contents don't match.");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ ret = EXIT_SUCCESS;
+out:
+ if (ret != EXIT_SUCCESS)
+ perror("ERROR");
+ if (fifo1 > -1)
+ close(fifo1);
+ if (fifo2 > -1)
+ close(fifo2);
+ unlink(pathname);
+ fclose(logfp);
+ exit(ret);
+}
diff --git a/fs/module.mk b/fs/module.mk
index 1af5815..8aeaad2 100644
--- a/fs/module.mk
+++ b/fs/module.mk
@@ -1,5 +1,5 @@
local_dir := fs
-local_progs := $(addprefix $(local_dir)/, file dir do_ckpt)
+local_progs := $(addprefix $(local_dir)/, file dir do_ckpt fifo)
sources += $(addprefix $(local_dir)/,libeptest.c)
progs += $(local_progs)
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 9/9] cr_tests: fs: Add fsnotify test for dnotify
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
` (6 preceding siblings ...)
2010-02-20 2:18 ` [PATCH 8/9] cr_tests: fs: Add unlinked fifo test Matt Helsley
@ 2010-02-20 2:18 ` Matt Helsley
7 siblings, 0 replies; 11+ messages in thread
From: Matt Helsley @ 2010-02-20 2:18 UTC (permalink / raw)
To: Serge E. Hallyn; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
Test that all dnotify flags are saved and restored properly.
Signed-off-by: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
---
fs/notify/dnotify.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++
fs/notify/module.mk | 12 +++
fs/notify/run.sh | 146 +++++++++++++++++++++++++++++++++
3 files changed, 384 insertions(+), 0 deletions(-)
create mode 100644 fs/notify/dnotify.c
create mode 100644 fs/notify/module.mk
create mode 100755 fs/notify/run.sh
diff --git a/fs/notify/dnotify.c b/fs/notify/dnotify.c
new file mode 100644
index 0000000..9f7652d
--- /dev/null
+++ b/fs/notify/dnotify.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2009 IBM Corp.
+ * Author: Matt Helsley <matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h> /* also for dnotify definitions like DN_* */
+#include <signal.h>
+#include <dirent.h> /* for dirfd */
+#include <assert.h>
+#include <getopt.h>
+#include <poll.h> /* POLL* definitions */
+#include <string.h>
+#include <errno.h>
+
+#include <sys/stat.h> /* for mkdir */
+#include <sys/types.h>
+
+#include "libcrtest/libcrtest.h"
+#include "libcrtest/labels.h"
+
+#define LOG_FILE "log.dnotify"
+extern FILE *logfp;
+
+static void usage(FILE *pout)
+{
+ fprintf(pout, "\ndnotify [-L] [-N] [-h|--help] [-l LABEL] [-n NUM] [-f DIR]\n"
+"Create and monitor a directory with dnotify.\n"
+"\n"
+"\t-L\tPrint the valid LABELs in order and exit.\n"
+"\t-l\tWait for checkpoint at LABEL.\n"
+"\t-N\tPrint the maximum label number and exit.\n"
+"\t-n\tWait for checkpoint at NUM.\n"
+"\t-f\tUse cgroup specified by DIR to freeze.\n"
+"\n"
+"You may only specify one LABEL or NUM and you may not specify both.\n"
+"Label numbers are integers in the range 0-%d\n"
+"Valid label numbers and their corresponding LABELs are:\n", num_labels - 1);
+ print_labels(pout);
+}
+
+const struct option long_options[] = {
+ { "print-labels", 0, 0, 'L'},
+ { "print-max-label-no", 0, 0, 'N'},
+ { "help", 0, 0, 'h'},
+ { "label", 1, 0, 'l'},
+ { "num", 1, 0, 'n'},
+ { "freezer", 1, 0, 'f'},
+ {0, 0, 0, 0},
+};
+
+static char *freezer = "1";
+
+static void parse_args(int argc, char **argv)
+{
+ ckpt_op_num = num_labels - 1;
+ ckpt_label = labels[ckpt_op_num];
+ while (1) {
+ int c;
+ c = getopt_long(argc, argv, "f:LNhl:n:", long_options, NULL);
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'f':
+ freezer = optarg;
+ printf("Will enter freezer cgroup %s\n", freezer);
+ break;
+ case 'L':
+ print_labels(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'N':
+ printf("%d\n", num_labels - 1);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'h':
+ usage(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'l':
+ ckpt_label = optarg;
+ break;
+ case 'n':
+ if ((sscanf(optarg, "%d", &ckpt_op_num) < 1) ||
+ (ckpt_op_num < 0) ||
+ (ckpt_op_num >= num_labels)) {
+ fprintf(stderr, "Option -n requires an argument in the range 0-%d. Got %d\n", num_labels - 1, ckpt_op_num);
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ default: /* unknown option */
+ break;
+ }
+ }
+}
+
+#define eassert(cond) do { \
+ typeof(cond) ____cond = (cond); \
+ if (!____cond) \
+ perror(stringify(cond)); \
+ assert(!!____cond); \
+} while(0)
+
+#define DN_ALL (DN_CREATE|DN_DELETE|DN_RENAME|DN_ATTRIB|DN_MODIFY|DN_ACCESS)
+static int watchfd = -1;
+
+static void dnotify_sigio(int signo, siginfo_t *info, void *ucontext)
+{
+ assert(signo == SIGIO);
+ assert(info->si_signo == SIGIO);
+/* assert(info->si_fd == watchfd);
+ assert(info->si_code == POLLMSG);
+ assert(info->si_band == (POLLIN|POLLRDNORM|POLLMSG));*/
+ /* TODO find a way to double-check dn_events ?? siginfo doesn't give enough */ \
+}
+
+#define dn_expect_events(dn_events) do { \
+ struct timespec __timeout = { \
+ .tv_sec = 1, \
+ .tv_nsec = 0 \
+ }; \
+ siginfo_t __info; \
+label(dn_events ## _sigio_wait, ret, sigtimedwait(&sigio, &__info, &__timeout)); \
+ dnotify_sigio(ret, &__info, NULL); \
+} while (0)
+
+int main (int argc, char **argv)
+{
+ const char *writ_contents = "hello world";
+ char contents[16];
+ sigset_t sigio;
+ DIR *dir;
+ int ret = 0, fd = -1;
+ int op_num = 0;
+
+ parse_args(argc, argv);
+
+ /* FIXME eventually stdio streams should be harmless */
+ close(0);
+ logfp = fopen(LOG_FILE, "w+");
+ if (!logfp) {
+ perror("could not open logfile");
+ exit(1);
+ }
+ /* redirect stdout and stderr to the log file */
+ if (dup2(fileno(logfp), 1) < 0) {
+ fprintf(logfp, "dup2(logfp, 1)\n");
+ goto out;
+ }
+ if (dup2(fileno(logfp), 2) < 0) {
+ fprintf(logfp, "dup2(logfp, 2)\n");
+ goto out;
+ }
+ if (!move_to_cgroup("freezer", freezer, getpid())) {
+ fprintf(logfp, "move_to_cgroup\n");
+ goto out;
+ }
+ printf("entered cgroup %s\n", freezer);
+ ret = mkdir("./dwatch", S_IRWXU);
+ eassert(ret == 0);
+ dir = opendir("./dwatch");
+ eassert(dir);
+ watchfd = dirfd(dir);
+ eassert(watchfd >= 0);
+
+ /* Block SIGIO -- handle it synchronously with dn_expect_events() */
+ ret = sigemptyset(&sigio);
+ eassert(ret == 0);
+ ret = sigaddset(&sigio, SIGIO);
+ eassert(ret == 0);
+ ret = sigprocmask(SIG_BLOCK, &sigio, NULL);
+ eassert(ret == 0);
+
+label(watch_create, ret, fcntl(watchfd, F_NOTIFY, DN_CREATE));
+label(do_create, ret, open("./dwatch/create", O_CREAT|O_EXCL|O_RDWR));
+ fd = ret;
+ assert(fd != watchfd);
+ assert(fd >= 0);
+ dn_expect_events(DN_CREATE);
+
+label(watch_modify, ret, fcntl(watchfd, F_NOTIFY, DN_MODIFY));
+label(do_modify, ret, write(fd, writ_contents, strlen(writ_contents) + 1));
+ assert(ret == (strlen(writ_contents) + 1));
+ dn_expect_events(DN_MODIFY);
+
+ ret = lseek(fd, 0, SEEK_SET);
+ assert(ret == 0);
+label(watch_access, ret, fcntl(watchfd, F_NOTIFY, DN_ACCESS));
+label(do_access, ret, read(fd, contents, strlen(writ_contents) + 1));
+ assert(ret == (strlen(writ_contents) + 1));
+ assert(strcmp(contents, writ_contents) == 0);
+ dn_expect_events(DN_ACCESS);
+
+label(watch_attrib, ret, fcntl(watchfd, F_NOTIFY, DN_ATTRIB));
+label(do_attrib, ret, fchmod(fd, S_IRUSR));
+ assert(ret == 0);
+ ret = close(fd);
+ assert(ret == 0);
+ fd = 0;
+ dn_expect_events(DN_ATTRIB);
+
+label(watch_rename, ret, fcntl(watchfd, F_NOTIFY, DN_RENAME));
+label(do_rename, ret, rename("./dwatch/create", "./dwatch/rename"));
+ assert(ret == 0);
+ dn_expect_events(DN_RENAME);
+
+label(watch_delete, ret, fcntl(watchfd, F_NOTIFY, DN_DELETE|DN_MULTISHOT));
+label(do_delete, ret, unlink("./dwatch/rename"));
+ assert(ret == 0);
+ dn_expect_events(DN_DELETE);
+label(do_rmdir, ret, rmdir("./dwatch"));
+ assert(ret == 0);
+ ret = EXIT_SUCCESS;
+out:
+ fprintf(logfp, "\nerrno: %s\n", strerror(errno));
+ if (fd >= 0)
+ close(fd);
+ closedir(dir);
+ fclose(logfp);
+ if (ret != EXIT_SUCCESS)
+ ret = EXIT_FAILURE;
+ exit(ret);
+}
+
diff --git a/fs/notify/module.mk b/fs/notify/module.mk
new file mode 100644
index 0000000..6c2eb44
--- /dev/null
+++ b/fs/notify/module.mk
@@ -0,0 +1,12 @@
+local_dir := fs/notify
+local_progs := $(addprefix $(local_dir)/, dnotify)
+
+sources += $(addprefix $(local_dir)/,../libfstest.c)
+progs += $(local_progs)
+test_clean += $(local_dir)/cr_fsnotify*
+
+# fs tests include libcrtest/libcrtest.h
+$(local_progs): CFLAGS += -D_GNU_SOURCE=1
+$(local_progs): CPPFLAGS += -I .
+$(local_progs): LDFLAGS += -Xlinker -dT -Xlinker libcrtest/labels.lds
+$(local_progs): $(local_dir)/../libfstest.o libcrtest/libcrtest.a
diff --git a/fs/notify/run.sh b/fs/notify/run.sh
new file mode 100755
index 0000000..603d7df
--- /dev/null
+++ b/fs/notify/run.sh
@@ -0,0 +1,146 @@
+#!/bin/bash
+
+
+source ../../common.sh
+dir=`mktemp -p . -d -t cr_fsnotify_XXXXXXX` || (echo "mktemp failed"; exit 1)
+echo "Using output dir $dir"
+cd $dir
+
+#
+# Check if the running kernel supports fsnotify
+#
+( if [ -r /proc/config.gz ]; then
+ zcat /proc/config.gz
+elif [ -r /proc/config ]; then
+ cat /proc/config
+# elif TODO look for CONFIG_DNOTIFY=y in /boot/config-$(uname -r)
+else
+# There is no way to test CONFIG_DNOTIFY -- assume it is set =y
+ echo 'CONFIG_DNOTIFY=y'
+fi ) | grep -E '^[[:space:]]*CONFIG_[DI]NOTIFY=y' > /dev/null 2>&1
+[ $? ] || {
+ echo "WARNING: Kernel does not support fsnotify (dnotify, inotify, fanotify). Skipping tests."
+ exit 1
+}
+
+TESTS=( dnotify )
+
+#make ${TESTS[@]}
+
+# mount -t cgroup foo /cg
+# mkdir /cg/1
+# chown -R $(id --name -u).$(id --name -g) /cg/1
+err_msg="BROK"
+function do_err()
+{
+ if [ -n "${TEST_PID}" ]; then
+ local PIDLIST=( $(ps --ppid ${TEST_PID} -o pid=) ${TEST_PID} )
+ kill ${PIDLIST[@]} || kill -9 ${PIDLIST[@]}
+ fi
+ echo "${err_msg}"
+ ((failed++))
+ wait
+}
+
+failed=0
+
+NUMTESTS=${#TESTS[@]}
+for (( CURTEST = 0; CURTEST < NUMTESTS; CURTEST = CURTEST + 1 )); do
+ T=${TESTS[$CURTEST]}
+ ((IMAX = $(../${T} -N)))
+ echo "INFO: Test ${T} does:"
+ ../${T} -L | sed -e 's/^/INFO:/'
+
+ TEST_LABELS=( $(../${T} -L | tail -n '+2' | cut -f 3) )
+
+ # First we run the test taking checkpoints at all the labelled points
+ rm -f "./checkpoint-"{ready,done,skip}
+ echo "Running test: \"${T}\""
+ ../${T} &
+ TEST_PID=$!
+
+ trap 'do_err; break 2' ERR EXIT
+ for ((I = 0; I <= IMAX; I = I + 1)); do
+ TLABEL="${TEST_LABELS[$I]}"
+ while [ '!' -r "./checkpoint-ready" ]; do
+ sleep 0.1 # slow
+ done
+ echo "Taking checkpoint ${I}: ${TLABEL}."
+ freeze
+ trap 'thaw; do_err; break 2' ERR EXIT
+ sync # slow
+ cp log.${T} log.${T}.${I}.${TLABEL}
+ err_msg="FAIL"
+ ${CHECKPOINT} ${TEST_PID} > "checkpoint-${T}.${I}.${TLABEL}"
+ err_msg="BROK"
+
+ # Reset for the next iteration
+ touch "./checkpoint-done"
+ thaw
+ trap 'do_err; break 2' ERR EXIT
+ done
+
+ sleep 0.1
+ while [ '!' -r "./checkpoint-done" ]; do
+ touch "./checkpoint-done"
+ sleep 0.1
+ done
+
+ echo "Done taking checkpoints. Waiting for ${TEST_PID} to finish."
+
+ # FAIL if the status of the test program's original run isn't 0.
+ err_msg="FAIL"
+ wait ${TEST_PID} || /bin/true # FIXME why can't we wait for TEST_PID ?
+ err_msg="BROK"
+
+ echo "Original completed running."
+
+ # Save the original run's log for later comparison.
+ mv log.${T} log.${T}.orig
+
+ # Now that the original, non-restart run is complete let's restart
+ # each checkpoint and make sure they produce the same results.
+ trap 'do_err; break 2' EXIT
+ trap '' ERR
+ echo "Restarting checkpoints"
+ for ((I=0; I <= IMAX; I = I + 1)); do
+ touch "./checkpoint-ready" "./checkpoint-done" "./checkpoint-skip"
+ TLABEL="${TEST_LABELS[$I]}"
+
+ # now try restarting. restore log first
+ cp log.${T}.${I}.${TLABEL} log.${T}
+ echo "Restart ${I} ${TLABEL}"
+ err_msg="FAIL"
+ # --copy-status ensures that we trap on error.
+ trap 'do_err; break 2' ERR
+ ${RESTART} --copy-status -w < checkpoint-${T}.${I}.${TLABEL}
+ echo restart returned $?
+ trap '' ERR
+ err_msg="BROK"
+
+ # Now compare the logs. We can strip the thread id differences
+ # since all of these tests are single-threaded.
+ SEDEXPR='s/^INFO: thread [[:digit:]]\+: //'
+ sed -i -e "${SEDEXPR}" "log.${T}.orig"
+ sed -i -e "${SEDEXPR}" "log.${T}"
+
+ err_msg="FAIL"
+ diff -s -pu "log.${T}.orig" "log.${T}" > "log.${T}.${I}.${TLABEL}.diff"
+ err_msg="BROK"
+ echo "PASS \"${T}\""
+ rm -f "log.${T}.${I}.${TLABEL}"* "log.${T}" "checkpoint-${T}.${I}.${TLABEL}"
+ done
+ rm -f "log.${T}.orig"
+
+ trap '' EXIT
+ wait
+done
+trap '' EXIT
+
+rm -f "./checkpoint-"{ready,done,skip}
+
+
+# rmdir /cg/1
+# umount /cg
+
+exit ${failed}
--
1.6.3.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 0/9] cr_tests: Filesystem tests
[not found] ` <1266632337-13792-1-git-send-email-matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2010-02-20 2:18 ` [PATCH 1/9] cr_tests: fs: Add tests for checkpoint/restart of unlinked files Matt Helsley
@ 2010-02-22 18:02 ` Serge E. Hallyn
1 sibling, 0 replies; 11+ messages in thread
From: Serge E. Hallyn @ 2010-02-22 18:02 UTC (permalink / raw)
To: Matt Helsley; +Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
Quoting Matt Helsley (matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org):
> These filesystem-related tests for unlinked file support and
> dnotify are expected to fail so long as those features are
> unsupported by c/r.
>
> PATCH 1/9 Add tests for checkpoint/restart of unlinked files.
> PATCH 2/9 Add a do_ckpt utility (for python scripts)
> PATCH 3/9 A tool to create a random tree of dirs and files
> PATCH 4/9 A tool to re-fill a tree of dirs and files
> PATCH 5/9 A tool to check a tree of files and dirs
> PATCH 6/9 Add script to hold an unlinked tree
> PATCH 7/9 Add random tree test
> PATCH 8/9 Add unlinked fifo test
> PATCH 9/9 Add fsnotify test for dnotify
Ok, I went ahead and pushed them to branch master, since they
aren't hooked into cr_tests/runall.sh yet. But at the moment
I find that ./hold_unlinked_tree.py hangs, and notify/run.sh
reports failure when it should fail success for now.
thanks,
-serge
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2010-02-22 18:02 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-02-20 2:18 [PATCH 0/9] cr_tests: Filesystem tests Matt Helsley
[not found] ` <1266632337-13792-1-git-send-email-matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2010-02-20 2:18 ` [PATCH 1/9] cr_tests: fs: Add tests for checkpoint/restart of unlinked files Matt Helsley
[not found] ` <72d007906376ee6de7e97ed02f3d7aa814b8017b.1266632319.git.matthltc-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2010-02-20 2:18 ` [PATCH 2/9] cr_tests: fs: Add a do_ckpt utility Matt Helsley
2010-02-20 2:18 ` [PATCH 3/9] cr_tests: fs: A tool to create a random tree of dirs and files Matt Helsley
2010-02-20 2:18 ` [PATCH 4/9] cr_tests: fs: A tool to re-fill a " Matt Helsley
2010-02-20 2:18 ` [PATCH 5/9] cr_tests: fs: A tool to check a tree of files and dirs Matt Helsley
2010-02-20 2:18 ` [PATCH 6/9] cr_tests: fs: Add script to hold an unlinked tree Matt Helsley
2010-02-20 2:18 ` [PATCH 7/9] cr_tests: fs: Add random tree test Matt Helsley
2010-02-20 2:18 ` [PATCH 8/9] cr_tests: fs: Add unlinked fifo test Matt Helsley
2010-02-20 2:18 ` [PATCH 9/9] cr_tests: fs: Add fsnotify test for dnotify Matt Helsley
2010-02-22 18:02 ` [PATCH 0/9] cr_tests: Filesystem tests Serge E. Hallyn
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.