Linux Container Development
 help / color / mirror / Atom feed
* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox