* [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