Added experimental support for building with Meson+Ninja
authorNikolaus Rath <Nikolaus@rath.org>
Thu, 5 Jan 2017 17:37:00 +0000 (09:37 -0800)
committerNikolaus Rath <Nikolaus@rath.org>
Thu, 12 Jan 2017 23:19:04 +0000 (15:19 -0800)
24 files changed:
.gitignore
ChangeLog.rst
Makefile.am
README.md
doc/Makefile.am
doc/meson.build [new file with mode: 0644]
example/Makefile.am
example/meson.build [new file with mode: 0644]
include/Makefile.am
include/meson.build [new file with mode: 0644]
lib/Makefile.am
lib/meson.build [new file with mode: 0644]
meson.build [new file with mode: 0644]
meson_options.txt [new file with mode: 0644]
test/Makefile.am
test/meson.build [new file with mode: 0644]
test/test.c [deleted file]
test/test_examples.py
test/test_syscalls.c [new file with mode: 0644]
test/util.py
test/wrong_command.c [new file with mode: 0644]
util/Makefile.am
util/install_helper.sh [new file with mode: 0755]
util/meson.build [new file with mode: 0644]

index 619131a1355a6ed5e5ea9479ec9c2ea0c4f43090..0da305157a174d1f0141349050c29a6ff1a4484e 100644 (file)
@@ -40,3 +40,4 @@ TAGS
 /GSYMS
 /GTAGS
 /test/test_setattr
+/build/
index b8ad5e19596e3c6c4707279db6e36a865142d1ec..2cabd7d75f473820f53c93c436a84243df9be45f 100644 (file)
@@ -2,7 +2,7 @@ libfuse 3.1.0 (UNRELEASED)
 ==========================
 
 * Re-introduced examples/null.c.
-
+* Added experimental support for building with Meson.
 
 libfuse 3.0.0 (2016-12-08)
 ==========================
index 4e95a71532fc8cb1c089e16bf276d5be746ccc7a..b2ba4b26115ee0bff37eb788f8eab39b61b48cde 100644 (file)
@@ -8,26 +8,11 @@ EXTRA_DIST = \
        fuse3.pc.in \
        README* \
        test/*.py \
-       test/pytest.ini
+       test/pytest.ini \
+        meson.build \
+        meson_options.txt
 
 pkgconfigdir = @pkgconfigdir@
 pkgconfig_DATA = fuse3.pc
 
 $(pkgconfig_DATA): config.status
-
-.PHONY: setuid_fusermount
-setuid_fusermount:
-       @echo "Attempting to use sudo to make util/fusermount3 setuid root"
-       @echo "If this fails, set permissions manually and re-run make test"
-       test $$(ls -n util/fusermount3 | awk 'NR==1 {print $$3}') -eq 0 || \
-           sudo chown root util/fusermount3
-       test -u util/fusermount3 || \
-           sudo chmod u+s util/fusermount3
-
-# If we are not root, util/fusermount3 needs to be setuid root
-# for tests to work.
-
-test_deps = $(shell [ "$${UID}" -eq 0 ] || echo setuid_fusermount)
-.PHONY: test
-test: all $(test_deps)
-       python3 -m pytest test/
index 8201eeba35df8b343eaeb6929ee4e79df791b3f3..be2e52e96dc87a30305e31d14bde1442415478f6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -28,33 +28,58 @@ Installation
 ------------
 
 You can download libfuse from
-https://github.com/libfuse/libfuse/releases. After extracting the
-tarball, build and install with
+https://github.com/libfuse/libfuse/releases. To build and install, we
+recommend to use [Meson](http://mesonbuild.com/) and
+[Ninja](https://ninja-build.org).  After extracting the libfuse
+tarball, create a (temporary) build directory and run Meson:
 
-    ./configure
-    make -j8
-    make install
+    $ md build; cd build
+    $ meson ..
 
-To run some self tests, you need a Python 3 environment with the
-[py.test](http://www.pytest.org/) module installed. To run the tests,
-execute
+Normally, the default build options will work fine. If you
+nevertheless want to adjust them, you can do so with the *mesonconf*
+command:
 
-    python3 -m pytest test/
+    $ mesonconf # list options
+    $ mesonconf  -D disable-mtab=true # set an option
 
-You may also need to add `/usr/local/lib` to `/etc/ld.so.conf` and/or
-run *ldconfig*. If you're building from the git repository (instead of
-using a release tarball), you also need to run `./makeconf.sh` to
-create the `configure` script.
+To build, test and install libfuse, you then use Ninja:
+
+    $ ninja
+    $ sudo ninja tests # requires pytest, see below
+    $ sudo ninja install
+
+Running the tests requires the [py.test](http://www.pytest.org/)
+Python module. Instead of running the tests as root, the majority of
+tests can also be run as a regular user if *util/fusermount3* is
+made setuid root first:
+
+    $ sudo chown root:root util/fusermount3
+    $ sudo chmod 4755 util/fusermount3
+    $ ninja tests
+
+
+Alternate Installation
+----------------------
+
+If you are not able to use Meson and Ninja, please report this to the
+libfuse mailing list. Until the problem is resolved, you may fall back
+to an in-source build using autotools:
+
+    $ ./configure
+    $ make
+    $ sudo make install
+
+Note that support for building with autotools may disappear at some
+point, so if you depend on using autotools for some reason please let
+the libfuse developers know!
 
-You'll also need a fuse kernel module (Linux kernels 2.6.14 or later
-contain FUSE support).
 
 Security implications
 ---------------------
 
-If you run `make install`, the *fusermount3* program is installed
-set-user-id to root.  This is done to allow normal users to mount
-their own filesystem implementations.
+The *fusermount3* program is installed setuid root. This is done to
+allow normal users to mount their own filesystem implementations.
 
 To limit the harm that malicious users can do this way, *fusermount3*
 enforces the following limitations:
index 8801da2bfb2e4174b3beab060d8bec9a62b6e530..531a6c4ba1e76ea3fe230474f86ebcc703cad0f8 100644 (file)
@@ -2,4 +2,4 @@
 
 dist_man_MANS = fusermount3.1 mount.fuse.8
 
-EXTRA_DIST = kernel.txt Doxyfile html README.NFS
+EXTRA_DIST = kernel.txt Doxyfile html README.NFS meson.build
diff --git a/doc/meson.build b/doc/meson.build
new file mode 100644 (file)
index 0000000..eb81f3d
--- /dev/null
@@ -0,0 +1,5 @@
+# Attention, emacs, please use -*- mode: python -*-
+# (even though this isn't actually Python code)
+
+install_man('fusermount3.1', 'mount.fuse.8')
+
index 81b955563de0b0c33a401643ca113cd486c31442..c83c81f0c09568e000d61978d4de1aa85f6b5d26 100644 (file)
@@ -17,3 +17,5 @@ ioctl_client_LDADD =
 poll_client_CPPFLAGS =
 poll_client_LDFLAGS =
 poll_client_LDADD =
+
+EXTRA_DIST = meson.build
diff --git a/example/meson.build b/example/meson.build
new file mode 100644 (file)
index 0000000..4497288
--- /dev/null
@@ -0,0 +1,29 @@
+# Attention, emacs, please use -*- mode: python -*-
+# (even though this isn't actually Python code)
+
+examples = [ 'passthrough', 'passthrough_fh', 'null', 'hello', 'hello_ll',
+             'ioctl', 'ioctl_client', 'poll_client',
+             'passthrough_ll', 'cuse', 'cuse_client' ]
+
+threaded_examples = [ 'notify_inval_inode',
+                      'notify_store_retrieve',
+                      'notify_inval_entry',
+                      'poll' ]
+
+foreach ex : examples
+    executable(ex, ex + '.c',
+               include_directories: include_dirs,
+               link_with: [ libfuse ],
+               install: false)
+endforeach
+
+
+foreach ex : threaded_examples
+    executable(ex, ex + '.c',
+               include_directories: include_dirs,
+               link_with: [ libfuse ],
+               dependencies: thread_dep,
+               install: false)
+endforeach
+
+# TODO: Link passthrough_fh with ulockmgr if available
index ffbfafa95418beaaddc962dcce05891a5ebbf29d..5072cc3586c1ef3249f1c4a4df812a4ffb946b9b 100644 (file)
@@ -10,3 +10,5 @@ fuseinclude_HEADERS = \
        cuse_lowlevel.h
 
 noinst_HEADERS = fuse_kernel.h
+
+EXTRA_DIST = meson.build
diff --git a/include/meson.build b/include/meson.build
new file mode 100644 (file)
index 0000000..f1fa5f0
--- /dev/null
@@ -0,0 +1,8 @@
+# Attention, emacs, please use -*- mode: python -*-
+# (even though this isn't actually Python code)
+
+libfuse_headers = [ 'fuse.h', 'fuse_common.h', 'fuse_lowlevel.h',
+                   'fuse_opt.h', 'cuse_lowlevel.h' ]
+
+install_headers(libfuse_headers, subdir: 'fuse3')
+
index e7f6fd40a5a2a7118e3507396ee8aeb864812ab0..cdacfd5f5ac307e43c2b4f72f5ef6c351e2508d9 100644 (file)
@@ -40,4 +40,4 @@ if NETBSD
 libfuse3_la_LIBADD = -lperfuse -lpuffs
 endif
 
-EXTRA_DIST = fuse_versionscript
+EXTRA_DIST = fuse_versionscript meson.build
diff --git a/lib/meson.build b/lib/meson.build
new file mode 100644 (file)
index 0000000..287dc86
--- /dev/null
@@ -0,0 +1,47 @@
+# Attention, emacs, please use -*- mode: python -*-
+# (even though this isn't actually Python code)
+
+libfuse_sources = ['fuse.c', 'fuse_i.h', 'fuse_loop.c', 'fuse_loop_mt.c',
+                   'fuse_lowlevel.c', 'fuse_misc.h', 'fuse_opt.c',
+                   'fuse_signals.c', 'buffer.c', 'cuse_lowlevel.c',
+                  'helper.c', 'modules/subdir.c' ]
+
+if host_machine.system().startswith('linux')
+   libfuse_sources += [ 'mount.c', 'mount_util.c' ]
+else
+   libfuse_sources += [ 'mount_bsd.c' ]
+endif
+
+if cfg.has('HAVE_ICONV')
+   libfuse_sources += [ 'modules/iconv.c' ]
+endif
+
+deps = [ thread_dep ]
+libdl = meson.get_compiler('c').find_library('dl')
+if libdl.found()
+   deps += [ libdl ]
+endif
+
+if host_machine.system().startswith('netbsd')
+   deps += [ cc.find_library('perfuse'),
+             cc.find_library('puffs') ]
+endif
+
+fusermount_path = join_paths(get_option('prefix'), get_option('bindir'))
+libfuse = library('fuse3', libfuse_sources, version: '3.0.0', install: true,
+                  soversion: '3', include_directories: include_dirs,
+                  dependencies: deps,
+                  link_depends: 'fuse_versionscript',
+                  c_args: [ '-DFUSE_USE_VERSION=30',
+                            '-DFUSERMOUNT_DIR="{}"'.format(fusermount_path) ],
+                  link_args: ['-Wl,--version-script,' + meson.current_source_dir()
+                              + '/fuse_versionscript' ])
+
+pkg = import('pkgconfig')
+pkg.generate(libraries: [ libfuse, '-lpthread' ],
+             libraries_private: '-ldl',
+             version: meson.project_version(),
+             name: 'fuse3',
+             description: 'Filesystem in Userspace',
+             subdirs: 'fuse3')
+             
diff --git a/meson.build b/meson.build
new file mode 100644 (file)
index 0000000..0cce4af
--- /dev/null
@@ -0,0 +1,90 @@
+# Attention, emacs, please use -*- mode: python -*-
+# (even though this isn't actually Python code)
+
+project('libfuse3', 'c', version: '3.1.0',
+        default_options: [ 'buildtype=plain' ])
+
+#
+# Feature detection
+#
+cfg = configuration_data()
+cc = meson.get_compiler('c')
+
+# Default includes when checking for presence of functions and
+# struct members
+include_default = '
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+'
+
+cfg.set_quoted('PACKAGE_VERSION', meson.project_version())
+
+# Test for presence of some functions
+test_funcs = [ 'fork', 'fstatat', 'openat', 'readlinkat', 'pipe2',
+               'splice', 'vmsplice', 'posix_fallocate', 'fdatasync',
+               'utimensat' ]
+foreach func : test_funcs
+    cfg.set('HAVE_' + func.to_upper(),
+        cc.has_function(func, prefix: include_default))
+endforeach
+cfg.set('HAVE_SETXATTR', 
+        cc.has_function('setxattr', prefix: '#include <sys/xattr.h>'))
+cfg.set('HAVE_ICONV', 
+        cc.has_function('iconv', prefix: '#include <iconv.h>'))
+
+# Test if structs have specific member
+cfg.set('HAVE_STRUCT_STAT_ST_ATIM',
+         cc.has_member('struct stat', 'st_atim',
+                       prefix: include_default))
+cfg.set('HAVE_STRUCT_STAT_ST_ATIMESPEC',
+         cc.has_member('struct stat', 'st_atimespec',
+                       prefix: include_default))
+
+# Write the test results into config.h (stored in build directory)
+configure_file(output: 'config.h',
+               configuration : cfg)
+
+
+#
+# Compiler configuration
+#
+add_global_arguments('-D_REENTRANT', '-DHAVE_CONFIG_H', '-Wall', '-Wextra', '-Wno-sign-compare',
+                     '-Wstrict-prototypes', '-Wmissing-declarations', '-Wwrite-strings',
+                     '-O2', '-g', '-fno-strict-aliasing', language: 'c')
+
+# Some (stupid) GCC versions warn about unused return values even when they are
+# casted to void. This makes -Wunused-result pretty useless, since there is no
+# way to suppress the warning when we really *want* to ignore the value.
+code = '''
+__attribute__((warn_unused_result)) int get_4() {
+    return 4;
+}
+int main(void) {
+    (void) get_4();
+    return 0;
+}'''
+if not cc.compiles(code, args: [ '-O0', '-Werror=unused-result' ])
+     message('Compiler warns about unused result even when casting to void')
+     add_global_arguments('-Wno-unused-result', language: 'c')
+endif
+
+# current_build_dir() contains config.h
+include_dirs = include_directories('include', 'lib',
+                                   meson.current_build_dir())
+
+# Common dependencies
+thread_dep = dependency('threads') 
+
+#
+# Read build files from sub-directories
+#
+subdirs = [ 'lib', 'include', 'util', 'example', 'doc', 'test' ]
+foreach n : subdirs
+    subdir(n)
+endforeach
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644 (file)
index 0000000..e53bf5d
--- /dev/null
@@ -0,0 +1,2 @@
+option('disable-mtab', type : 'boolean', value : false,
+       description: 'Disable and ignore usage of /etc/mtab')
index 9eb0a73bea64254bb1f5f605b335771566cc58c5..6bcea4379387b9349e15d212eafa039dd0c5083f 100644 (file)
@@ -1,7 +1,10 @@
 ## Process this file with automake to produce Makefile.in
 
 AM_CPPFLAGS = -I$(top_srcdir)/include -D_REENTRANT
-noinst_PROGRAMS = test test_write_cache test_setattr
+noinst_PROGRAMS = test_syscalls test_write_cache test_setattr
 
 test_write_cache_LDADD = ../lib/libfuse3.la
 test_setattr_LDADD = ../lib/libfuse3.la
+
+EXTRA_DIST = meson.build wrong_command.c
+
diff --git a/test/meson.build b/test/meson.build
new file mode 100644 (file)
index 0000000..971c64a
--- /dev/null
@@ -0,0 +1,35 @@
+# Attention, emacs, please use -*- mode: python -*-
+# (even though this isn't actually Python code)
+
+# Compile helper programs
+td = []
+foreach prog: [ 'test_write_cache', 'test_setattr' ]
+    td += executable(prog, prog + '.c',
+                     include_directories: include_dirs,
+                     link_with: [ libfuse ],
+                     dependencies: thread_dep,
+                     install: false)
+endforeach
+td += executable('test_syscalls', 'test_syscalls.c',
+                 include_directories: include_dirs,
+                 install: false)
+
+# Actual tests are written in Python and can simply be copied.
+foreach fname : [ 'conftest.py', 'pytest.ini', 'test_examples.py',
+                  'util.py' ]
+    td += custom_target(fname, input: fname, output: fname,
+                        command: ['cp', '-fP', '--preserve=mode',
+                                  '@INPUT@', '@OUTPUT@'])
+endforeach
+
+# Create a new 'tests' target that we can run with Ninja
+run_target('tests', depends: td,
+           command: [ 'python3', '-m', 'pytest',
+                      meson.current_build_dir() ])
+
+
+# Provide something helpful when running 'ninja test'
+wrong_cmd = executable('wrong_command', 'wrong_command.c',
+                       install: false)
+test('wrong_cmd', wrong_cmd)
+
diff --git a/test/test.c b/test/test.c
deleted file mode 100644 (file)
index 281f218..0000000
+++ /dev/null
@@ -1,1522 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <utime.h>
-#include <errno.h>
-#include <assert.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-
-static char testfile[1024];
-static char testfile2[1024];
-static char testdir[1024];
-static char testdir2[1024];
-static char subfile[1024];
-
-static char testfile_r[1024];
-static char testfile2_r[1024];
-static char testdir_r[1024];
-static char testdir2_r[1024];
-static char subfile_r[1024];
-
-static char testname[256];
-static char testdata[] = "abcdefghijklmnopqrstuvwxyz";
-static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./";
-static const char *testdir_files[] = { "f1", "f2", NULL};
-static char zerodata[4096];
-static int testdatalen = sizeof(testdata) - 1;
-static int testdata2len = sizeof(testdata2) - 1;
-static unsigned int testnum = 1;
-static unsigned int select_test = 0;
-static unsigned int skip_test = 0;
-
-#define MAX_ENTRIES 1024
-
-static void test_perror(const char *func, const char *msg)
-{
-       fprintf(stderr, "%s %s() - %s: %s\n", testname, func, msg,
-               strerror(errno));
-}
-
-static void test_error(const char *func, const char *msg, ...)
-       __attribute__ ((format (printf, 2, 3)));
-
-static void __start_test(const char *fmt, ...)
-       __attribute__ ((format (printf, 1, 2)));
-
-static void test_error(const char *func, const char *msg, ...)
-{
-       va_list ap;
-       fprintf(stderr, "%s %s() - ", testname, func);
-       va_start(ap, msg);
-       vfprintf(stderr, msg, ap);
-       va_end(ap);
-       fprintf(stderr, "\n");
-}
-
-static void success(void)
-{
-       fprintf(stderr, "%s OK\n", testname);
-}
-
-static void __start_test(const char *fmt, ...)
-{
-       unsigned int n;
-       va_list ap;
-       n = sprintf(testname, "%3i [", testnum++);
-       va_start(ap, fmt);
-       n += vsprintf(testname + n, fmt, ap);
-       va_end(ap);
-       sprintf(testname + n, "]");
-}
-
-#define start_test(msg, args...) { \
-       if ((select_test && testnum != select_test) || \
-           (testnum == skip_test)) { \
-               testnum++; \
-               return 0; \
-       } \
-       __start_test(msg, ##args);              \
-}
-
-#define PERROR(msg) test_perror(__FUNCTION__, msg)
-#define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args)
-
-static int check_size(const char *path, int len)
-{
-       struct stat stbuf;
-       int res = stat(path, &stbuf);
-       if (res == -1) {
-               PERROR("stat");
-               return -1;
-       }
-       if (stbuf.st_size != len) {
-               ERROR("length %u instead of %u", (int) stbuf.st_size,
-                     (int) len);
-               return -1;
-       }
-       return 0;
-}
-
-static int fcheck_size(int fd, int len)
-{
-       struct stat stbuf;
-       int res = fstat(fd, &stbuf);
-       if (res == -1) {
-               PERROR("fstat");
-               return -1;
-       }
-       if (stbuf.st_size != len) {
-               ERROR("length %u instead of %u", (int) stbuf.st_size,
-                     (int) len);
-               return -1;
-       }
-       return 0;
-}
-
-static int check_type(const char *path, mode_t type)
-{
-       struct stat stbuf;
-       int res = lstat(path, &stbuf);
-       if (res == -1) {
-               PERROR("lstat");
-               return -1;
-       }
-       if ((stbuf.st_mode & S_IFMT) != type) {
-               ERROR("type 0%o instead of 0%o", stbuf.st_mode & S_IFMT, type);
-               return -1;
-       }
-       return 0;
-}
-
-static int fcheck_type(int fd, mode_t type)
-{
-       struct stat stbuf;
-       int res = fstat(fd, &stbuf);
-       if (res == -1) {
-               PERROR("fstat");
-               return -1;
-       }
-       if ((stbuf.st_mode & S_IFMT) != type) {
-               ERROR("type 0%o instead of 0%o", stbuf.st_mode & S_IFMT, type);
-               return -1;
-       }
-       return 0;
-}
-
-static int check_mode(const char *path, mode_t mode)
-{
-       struct stat stbuf;
-       int res = lstat(path, &stbuf);
-       if (res == -1) {
-               PERROR("lstat");
-               return -1;
-       }
-       if ((stbuf.st_mode & 07777) != mode) {
-               ERROR("mode 0%o instead of 0%o", stbuf.st_mode & 07777, mode);
-               return -1;
-       }
-       return 0;
-}
-
-static int fcheck_mode(int fd, mode_t mode)
-{
-       struct stat stbuf;
-       int res = fstat(fd, &stbuf);
-       if (res == -1) {
-               PERROR("fstat");
-               return -1;
-       }
-       if ((stbuf.st_mode & 07777) != mode) {
-               ERROR("mode 0%o instead of 0%o", stbuf.st_mode & 07777, mode);
-               return -1;
-       }
-       return 0;
-}
-
-static int check_times(const char *path, time_t atime, time_t mtime)
-{
-       int err = 0;
-       struct stat stbuf;
-       int res = lstat(path, &stbuf);
-       if (res == -1) {
-               PERROR("lstat");
-               return -1;
-       }
-       if (stbuf.st_atime != atime) {
-               ERROR("atime %li instead of %li", stbuf.st_atime, atime);
-               err--;
-       }
-       if (stbuf.st_mtime != mtime) {
-               ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
-               err--;
-       }
-       if (err)
-               return -1;
-
-       return 0;
-}
-
-#if 0
-static int fcheck_times(int fd, time_t atime, time_t mtime)
-{
-       int err = 0;
-       struct stat stbuf;
-       int res = fstat(fd, &stbuf);
-       if (res == -1) {
-               PERROR("fstat");
-               return -1;
-       }
-       if (stbuf.st_atime != atime) {
-               ERROR("atime %li instead of %li", stbuf.st_atime, atime);
-               err--;
-       }
-       if (stbuf.st_mtime != mtime) {
-               ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
-               err--;
-       }
-       if (err)
-               return -1;
-
-       return 0;
-}
-#endif
-
-static int check_nlink(const char *path, nlink_t nlink)
-{
-       struct stat stbuf;
-       int res = lstat(path, &stbuf);
-       if (res == -1) {
-               PERROR("lstat");
-               return -1;
-       }
-       if (stbuf.st_nlink != nlink) {
-               ERROR("nlink %li instead of %li", (long) stbuf.st_nlink,
-                     (long) nlink);
-               return -1;
-       }
-       return 0;
-}
-
-static int fcheck_nlink(int fd, nlink_t nlink)
-{
-       struct stat stbuf;
-       int res = fstat(fd, &stbuf);
-       if (res == -1) {
-               PERROR("fstat");
-               return -1;
-       }
-       if (stbuf.st_nlink != nlink) {
-               ERROR("nlink %li instead of %li", (long) stbuf.st_nlink,
-                     (long) nlink);
-               return -1;
-       }
-       return 0;
-}
-
-static int check_nonexist(const char *path)
-{
-       struct stat stbuf;
-       int res = lstat(path, &stbuf);
-       if (res == 0) {
-               ERROR("file should not exist");
-               return -1;
-       }
-       if (errno != ENOENT) {
-               ERROR("file should not exist: %s", strerror(errno));
-               return -1;
-       }
-       return 0;
-}
-
-static int check_buffer(const char *buf, const char *data, unsigned len)
-{
-       if (memcmp(buf, data, len) != 0) {
-               ERROR("data mismatch");
-               return -1;
-       }
-       return 0;
-}
-
-static int check_data(const char *path, const char *data, int offset,
-                     unsigned len)
-{
-       char buf[4096];
-       int res;
-       int fd = open(path, O_RDONLY);
-       if (fd == -1) {
-               PERROR("open");
-               return -1;
-       }
-       if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
-               PERROR("lseek");
-               close(fd);
-               return -1;
-       }
-       while (len) {
-               int rdlen = len < sizeof(buf) ? len : sizeof(buf);
-               res = read(fd, buf, rdlen);
-               if (res == -1) {
-                       PERROR("read");
-                       close(fd);
-                       return -1;
-               }
-               if (res != rdlen) {
-                       ERROR("short read: %u instead of %u", res, rdlen);
-                       close(fd);
-                       return -1;
-               }
-               if (check_buffer(buf, data, rdlen) != 0) {
-                       close(fd);
-                       return -1;
-               }
-               data += rdlen;
-               len -= rdlen;
-       }
-       res = close(fd);
-       if (res == -1) {
-               PERROR("close");
-               return -1;
-       }
-       return 0;
-}
-
-static int fcheck_data(int fd, const char *data, int offset,
-                      unsigned len)
-{
-       char buf[4096];
-       int res;
-       if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
-               PERROR("lseek");
-               return -1;
-       }
-       while (len) {
-               int rdlen = len < sizeof(buf) ? len : sizeof(buf);
-               res = read(fd, buf, rdlen);
-               if (res == -1) {
-                       PERROR("read");
-                       return -1;
-               }
-               if (res != rdlen) {
-                       ERROR("short read: %u instead of %u", res, rdlen);
-                       return -1;
-               }
-               if (check_buffer(buf, data, rdlen) != 0) {
-                       return -1;
-               }
-               data += rdlen;
-               len -= rdlen;
-       }
-       return 0;
-}
-
-static int check_dir_contents(const char *path, const char **contents)
-{
-       int i;
-       int res;
-       int err = 0;
-       int found[MAX_ENTRIES];
-       const char *cont[MAX_ENTRIES];
-       DIR *dp;
-
-       for (i = 0; contents[i]; i++) {
-               assert(i < MAX_ENTRIES - 3);
-               found[i] = 0;
-               cont[i] = contents[i];
-       }
-       found[i] = 0;
-       cont[i++] = ".";
-       found[i] = 0;
-       cont[i++] = "..";
-       cont[i] = NULL;
-
-       dp = opendir(path);
-       if (dp == NULL) {
-               PERROR("opendir");
-               return -1;
-       }
-       memset(found, 0, sizeof(found));
-       while(1) {
-               struct dirent *de;
-               errno = 0;
-               de = readdir(dp);
-               if (de == NULL) {
-                       if (errno) {
-                               PERROR("readdir");
-                               closedir(dp);
-                               return -1;
-                       }
-                       break;
-               }
-               for (i = 0; cont[i] != NULL; i++) {
-                       assert(i < MAX_ENTRIES);
-                       if (strcmp(cont[i], de->d_name) == 0) {
-                               if (found[i]) {
-                                       ERROR("duplicate entry <%s>",
-                                             de->d_name);
-                                       err--;
-                               } else
-                                       found[i] = 1;
-                               break;
-                       }
-               }
-               if (!cont[i]) {
-                       ERROR("unexpected entry <%s>", de->d_name);
-                       err --;
-               }
-       }
-       for (i = 0; cont[i] != NULL; i++) {
-               if (!found[i]) {
-                       ERROR("missing entry <%s>", cont[i]);
-                       err--;
-               }
-       }
-       res = closedir(dp);
-       if (res == -1) {
-               PERROR("closedir");
-               return -1;
-       }
-       if (err)
-               return -1;
-
-       return 0;
-}
-
-static int create_file(const char *path, const char *data, int len)
-{
-       int res;
-       int fd;
-
-       unlink(path);
-       fd = creat(path, 0644);
-       if (fd == -1) {
-               PERROR("creat");
-               return -1;
-       }
-       if (len) {
-               res = write(fd, data, len);
-               if (res == -1) {
-                       PERROR("write");
-                       close(fd);
-                       return -1;
-               }
-               if (res != len) {
-                       ERROR("write is short: %u instead of %u", res, len);
-                       close(fd);
-                       return -1;
-               }
-       }
-       res = close(fd);
-       if (res == -1) {
-               PERROR("close");
-               return -1;
-       }
-       res = check_type(path, S_IFREG);
-       if (res == -1)
-               return -1;
-       res = check_mode(path, 0644);
-       if (res == -1)
-               return -1;
-       res = check_nlink(path, 1);
-       if (res == -1)
-               return -1;
-       res = check_size(path, len);
-       if (res == -1)
-               return -1;
-
-       if (len) {
-               res = check_data(path, data, 0, len);
-               if (res == -1)
-                       return -1;
-       }
-
-       return 0;
-}
-
-static int cleanup_dir(const char *path, const char **dir_files, int quiet)
-{
-       int i;
-       int err = 0;
-
-       for (i = 0; dir_files[i]; i++) {
-               int res;
-               char fpath[1024];
-               sprintf(fpath, "%s/%s", path, dir_files[i]);
-               res = unlink(fpath);
-               if (res == -1 && !quiet) {
-                       PERROR("unlink");
-                       err --;
-               }
-       }
-       if (err)
-               return -1;
-
-       return 0;
-}
-
-static int create_dir(const char *path, const char **dir_files)
-{
-       int res;
-       int i;
-
-       rmdir(path);
-       res = mkdir(path, 0755);
-       if (res == -1) {
-               PERROR("mkdir");
-               return -1;
-       }
-       res = check_type(path, S_IFDIR);
-       if (res == -1)
-               return -1;
-       res = check_mode(path, 0755);
-       if (res == -1)
-               return -1;
-
-       for (i = 0; dir_files[i]; i++) {
-               char fpath[1024];
-               sprintf(fpath, "%s/%s", path, dir_files[i]);
-               res = create_file(fpath, "", 0);
-               if (res == -1) {
-                       cleanup_dir(path, dir_files, 1);
-                       return -1;
-               }
-       }
-       res = check_dir_contents(path, dir_files);
-       if (res == -1) {
-               cleanup_dir(path, dir_files, 1);
-               return -1;
-       }
-
-       return 0;
-}
-
-static int test_truncate(int len)
-{
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int res;
-
-       start_test("truncate(%u)", (int) len);
-       res = create_file(testfile, data, datalen);
-       if (res == -1)
-               return -1;
-
-       res = truncate(testfile, len);
-       if (res == -1) {
-               PERROR("truncate");
-               return -1;
-       }
-       res = check_size(testfile, len);
-       if (res == -1)
-               return -1;
-
-       if (len > 0) {
-               if (len <= datalen) {
-                       res = check_data(testfile, data, 0, len);
-                       if (res == -1)
-                               return -1;
-               } else {
-                       res = check_data(testfile, data, 0, datalen);
-                       if (res == -1)
-                               return -1;
-                       res = check_data(testfile, zerodata, datalen,
-                                        len - datalen);
-                       if (res == -1)
-                               return -1;
-               }
-       }
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_ftruncate(int len, int mode)
-{
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int res;
-       int fd;
-
-       start_test("ftruncate(%u) mode: 0%03o", len, mode);
-       res = create_file(testfile, data, datalen);
-       if (res == -1)
-               return -1;
-
-       fd = open(testfile, O_WRONLY);
-       if (fd == -1) {
-               PERROR("open");
-               return -1;
-       }
-
-       res = fchmod(fd, mode);
-       if (res == -1) {
-               PERROR("fchmod");
-               close(fd);
-               return -1;
-       }
-       res = check_mode(testfile, mode);
-       if (res == -1) {
-               close(fd);
-               return -1;
-       }
-       res = ftruncate(fd, len);
-       if (res == -1) {
-               PERROR("ftruncate");
-               close(fd);
-               return -1;
-       }
-       close(fd);
-       res = check_size(testfile, len);
-       if (res == -1)
-               return -1;
-
-       if (len > 0) {
-               if (len <= datalen) {
-                       res = check_data(testfile, data, 0, len);
-                       if (res == -1)
-                               return -1;
-               } else {
-                       res = check_data(testfile, data, 0, datalen);
-                       if (res == -1)
-                               return -1;
-                       res = check_data(testfile, zerodata, datalen,
-                                        len - datalen);
-                       if (res == -1)
-                               return -1;
-               }
-       }
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_utime(void)
-{
-       struct utimbuf utm;
-       time_t atime = 987631200;
-       time_t mtime = 123116400;
-       int res;
-
-       start_test("utime");
-       res = create_file(testfile, NULL, 0);
-       if (res == -1)
-               return -1;
-
-       utm.actime = atime;
-       utm.modtime = mtime;
-       res = utime(testfile, &utm);
-       if (res == -1) {
-               PERROR("utime");
-               return -1;
-       }
-       res = check_times(testfile, atime, mtime);
-       if (res == -1) {
-               return -1;
-       }
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_create(void)
-{
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int err = 0;
-       int res;
-       int fd;
-
-       start_test("create");
-       unlink(testfile);
-       fd = creat(testfile, 0644);
-       if (fd == -1) {
-               PERROR("creat");
-               return -1;
-       }
-       res = write(fd, data, datalen);
-       if (res == -1) {
-               PERROR("write");
-               close(fd);
-               return -1;
-       }
-       if (res != datalen) {
-               ERROR("write is short: %u instead of %u", res, datalen);
-               close(fd);
-               return -1;
-       }
-       res = close(fd);
-       if (res == -1) {
-               PERROR("close");
-               return -1;
-       }
-       res = check_type(testfile, S_IFREG);
-       if (res == -1)
-               return -1;
-       err += check_mode(testfile, 0644);
-       err += check_nlink(testfile, 1);
-       err += check_size(testfile, datalen);
-       err += check_data(testfile, data, 0, datalen);
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_create_unlink(void)
-{
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int err = 0;
-       int res;
-       int fd;
-
-       start_test("create+unlink");
-       unlink(testfile);
-       fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
-       if (fd == -1) {
-               PERROR("creat");
-               return -1;
-       }
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               close(fd);
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-       res = write(fd, data, datalen);
-       if (res == -1) {
-               PERROR("write");
-               close(fd);
-               return -1;
-       }
-       if (res != datalen) {
-               ERROR("write is short: %u instead of %u", res, datalen);
-               close(fd);
-               return -1;
-       }
-       err += fcheck_type(fd, S_IFREG);
-       err += fcheck_mode(fd, 0644);
-       err += fcheck_nlink(fd, 0);
-       err += fcheck_size(fd, datalen);
-       err += fcheck_data(fd, data, 0, datalen);
-       res = close(fd);
-       if (res == -1) {
-               PERROR("close");
-               err--;
-       }
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_mknod(void)
-{
-       int err = 0;
-       int res;
-
-       start_test("mknod");
-       unlink(testfile);
-       res = mknod(testfile, 0644, 0);
-       if (res == -1) {
-               PERROR("mknod");
-               return -1;
-       }
-       res = check_type(testfile, S_IFREG);
-       if (res == -1)
-               return -1;
-       err += check_mode(testfile, 0644);
-       err += check_nlink(testfile, 1);
-       err += check_size(testfile, 0);
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-#define test_open(exist, flags, mode)  do_test_open(exist, flags, #flags, mode)
-
-static int do_test_open(int exist, int flags, const char *flags_str, int mode)
-{
-       char buf[4096];
-       const char *data = testdata;
-       int datalen = testdatalen;
-       unsigned currlen = 0;
-       int err = 0;
-       int res;
-       int fd;
-       off_t off;
-
-       start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode);
-       unlink(testfile);
-       if (exist) {
-               res = create_file(testfile_r, testdata2, testdata2len);
-               if (res == -1)
-                       return -1;
-
-               currlen = testdata2len;
-       }
-
-       fd = open(testfile, flags, mode);
-       if ((flags & O_CREAT) && (flags & O_EXCL) && exist) {
-               if (fd != -1) {
-                       ERROR("open should have failed");
-                       close(fd);
-                       return -1;
-               } else if (errno == EEXIST)
-                       goto succ;
-       }
-       if (!(flags & O_CREAT) && !exist) {
-               if (fd != -1) {
-                       ERROR("open should have failed");
-                       close(fd);
-                       return -1;
-               } else if (errno == ENOENT)
-                       goto succ;
-       }
-       if (fd == -1) {
-               PERROR("open");
-               return -1;
-       }
-
-       if (flags & O_TRUNC)
-               currlen = 0;
-
-       err += check_type(testfile, S_IFREG);
-       if (exist)
-               err += check_mode(testfile, 0644);
-       else
-               err += check_mode(testfile, mode);
-       err += check_nlink(testfile, 1);
-       err += check_size(testfile, currlen);
-       if (exist && !(flags & O_TRUNC) && (mode & 0400))
-               err += check_data(testfile, testdata2, 0, testdata2len);
-
-       res = write(fd, data, datalen);
-       if ((flags & O_ACCMODE) != O_RDONLY) {
-               if (res == -1) {
-                       PERROR("write");
-                       err --;
-               } else if (res != datalen) {
-                       ERROR("write is short: %u instead of %u", res, datalen);
-                       err --;
-               } else {
-                       if (datalen > (int) currlen)
-                               currlen = datalen;
-
-                       err += check_size(testfile, currlen);
-
-                       if (mode & 0400) {
-                               err += check_data(testfile, data, 0, datalen);
-                               if (exist && !(flags & O_TRUNC) &&
-                                   testdata2len > datalen)
-                                       err += check_data(testfile,
-                                                         testdata2 + datalen,
-                                                         datalen,
-                                                         testdata2len - datalen);
-                       }
-               }
-       } else {
-               if (res != -1) {
-                       ERROR("write should have failed");
-                       err --;
-               } else if (errno != EBADF) {
-                       PERROR("write");
-                       err --;
-               }
-       }
-       off = lseek(fd, SEEK_SET, 0);
-       if (off == (off_t) -1) {
-               PERROR("lseek");
-               err--;
-       } else if (off != 0) {
-               ERROR("offset should have returned 0");
-               err --;
-       }
-       res = read(fd, buf, sizeof(buf));
-       if ((flags & O_ACCMODE) != O_WRONLY) {
-               if (res == -1) {
-                       PERROR("read");
-                       err--;
-               } else {
-                       int readsize =
-                               currlen < sizeof(buf) ? currlen : sizeof(buf);
-                       if (res != readsize) {
-                               ERROR("read is short: %i instead of %u",
-                                     res, readsize);
-                               err--;
-                       } else {
-                               if ((flags & O_ACCMODE) != O_RDONLY) {
-                                       err += check_buffer(buf, data, datalen);
-                                       if (exist && !(flags & O_TRUNC) &&
-                                           testdata2len > datalen)
-                                               err += check_buffer(buf + datalen,
-                                                                   testdata2 + datalen,
-                                                                   testdata2len - datalen);
-                               } else if (exist)
-                                       err += check_buffer(buf, testdata2,
-                                                           testdata2len);
-                       }
-               }
-       } else {
-               if (res != -1) {
-                       ERROR("read should have failed");
-                       err --;
-               } else if (errno != EBADF) {
-                       PERROR("read");
-                       err --;
-               }
-       }
-
-       res = close(fd);
-       if (res == -1) {
-               PERROR("close");
-               return -1;
-       }
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-       res = check_nonexist(testfile_r);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-succ:
-       success();
-       return 0;
-}
-
-#define test_open_acc(flags, mode, err)         \
-       do_test_open_acc(flags, #flags, mode, err)
-
-static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
-{
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int res;
-       int fd;
-
-       start_test("open_acc(%s) mode: 0%03o message: '%s'", flags_str, mode,
-                  strerror(err));
-       unlink(testfile);
-       res = create_file(testfile, data, datalen);
-       if (res == -1)
-               return -1;
-
-       res = chmod(testfile, mode);
-       if (res == -1) {
-               PERROR("chmod");
-               return -1;
-       }
-
-       res = check_mode(testfile, mode);
-       if (res == -1)
-               return -1;
-
-       fd = open(testfile, flags);
-       if (fd == -1) {
-               if (err != errno) {
-                       PERROR("open");
-                       return -1;
-               }
-       } else {
-               if (err) {
-                       ERROR("open should have failed");
-                       close(fd);
-                       return -1;
-               }
-               close(fd);
-       }
-       success();
-       return 0;
-}
-
-static int test_symlink(void)
-{
-       char buf[1024];
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int linklen = strlen(testfile);
-       int err = 0;
-       int res;
-
-       start_test("symlink");
-       res = create_file(testfile, data, datalen);
-       if (res == -1)
-               return -1;
-
-       unlink(testfile2);
-       res = symlink(testfile, testfile2);
-       if (res == -1) {
-               PERROR("symlink");
-               return -1;
-       }
-       res = check_type(testfile2, S_IFLNK);
-       if (res == -1)
-               return -1;
-       err += check_mode(testfile2, 0777);
-       err += check_nlink(testfile2, 1);
-       res = readlink(testfile2, buf, sizeof(buf));
-       if (res == -1) {
-               PERROR("readlink");
-               err--;
-       }
-       if (res != linklen) {
-               ERROR("short readlink: %u instead of %u", res, linklen);
-               err--;
-       }
-       if (memcmp(buf, testfile, linklen) != 0) {
-               ERROR("link mismatch");
-               err--;
-       }
-       err += check_size(testfile2, datalen);
-       err += check_data(testfile2, data, 0, datalen);
-       res = unlink(testfile2);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile2);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_link(void)
-{
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int err = 0;
-       int res;
-
-       start_test("link");
-       res = create_file(testfile, data, datalen);
-       if (res == -1)
-               return -1;
-
-       unlink(testfile2);
-       res = link(testfile, testfile2);
-       if (res == -1) {
-               PERROR("link");
-               return -1;
-       }
-       res = check_type(testfile2, S_IFREG);
-       if (res == -1)
-               return -1;
-       err += check_mode(testfile2, 0644);
-       err += check_nlink(testfile2, 2);
-       err += check_size(testfile2, datalen);
-       err += check_data(testfile2, data, 0, datalen);
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-
-       err += check_nlink(testfile2, 1);
-       res = unlink(testfile2);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile2);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_link2(void)
-{
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int err = 0;
-       int res;
-
-       start_test("link-unlink-link");
-       res = create_file(testfile, data, datalen);
-       if (res == -1)
-               return -1;
-
-       unlink(testfile2);
-       res = link(testfile, testfile2);
-       if (res == -1) {
-               PERROR("link");
-               return -1;
-       }
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-       res = link(testfile2, testfile);
-       if (res == -1) {
-               PERROR("link");
-       }
-       res = check_type(testfile, S_IFREG);
-       if (res == -1)
-               return -1;
-       err += check_mode(testfile, 0644);
-       err += check_nlink(testfile, 2);
-       err += check_size(testfile, datalen);
-       err += check_data(testfile, data, 0, datalen);
-
-       res = unlink(testfile2);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       err += check_nlink(testfile, 1);
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_rename_file(void)
-{
-       const char *data = testdata;
-       int datalen = testdatalen;
-       int err = 0;
-       int res;
-
-       start_test("rename file");
-       res = create_file(testfile, data, datalen);
-       if (res == -1)
-               return -1;
-
-       unlink(testfile2);
-       res = rename(testfile, testfile2);
-       if (res == -1) {
-               PERROR("rename");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-       res = check_type(testfile2, S_IFREG);
-       if (res == -1)
-               return -1;
-       err += check_mode(testfile2, 0644);
-       err += check_nlink(testfile2, 1);
-       err += check_size(testfile2, datalen);
-       err += check_data(testfile2, data, 0, datalen);
-       res = unlink(testfile2);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile2);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_rename_dir(void)
-{
-       int err = 0;
-       int res;
-
-       start_test("rename dir");
-       res = create_dir(testdir, testdir_files);
-       if (res == -1)
-               return -1;
-
-       rmdir(testdir2);
-       res = rename(testdir, testdir2);
-       if (res == -1) {
-               PERROR("rename");
-               cleanup_dir(testdir, testdir_files, 1);
-               return -1;
-       }
-       res = check_nonexist(testdir);
-       if (res == -1) {
-               cleanup_dir(testdir, testdir_files, 1);
-               return -1;
-       }
-       res = check_type(testdir2, S_IFDIR);
-       if (res == -1) {
-               cleanup_dir(testdir2, testdir_files, 1);
-               return -1;
-       }
-       err += check_mode(testdir2, 0755);
-       err += check_dir_contents(testdir2, testdir_files);
-       err += cleanup_dir(testdir2, testdir_files, 0);
-       res = rmdir(testdir2);
-       if (res == -1) {
-               PERROR("rmdir");
-               return -1;
-       }
-       res = check_nonexist(testdir2);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_mkfifo(void)
-{
-       int res;
-       int err = 0;
-
-       start_test("mkfifo");
-       unlink(testfile);
-       res = mkfifo(testfile, 0644);
-       if (res == -1) {
-               PERROR("mkfifo");
-               return -1;
-       }
-       res = check_type(testfile, S_IFIFO);
-       if (res == -1)
-               return -1;
-       err += check_mode(testfile, 0644);
-       err += check_nlink(testfile, 1);
-       res = unlink(testfile);
-       if (res == -1) {
-               PERROR("unlink");
-               return -1;
-       }
-       res = check_nonexist(testfile);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-static int test_mkdir(void)
-{
-       int res;
-       int err = 0;
-       const char *dir_contents[] = {NULL};
-
-       start_test("mkdir");
-       rmdir(testdir);
-       res = mkdir(testdir, 0755);
-       if (res == -1) {
-               PERROR("mkdir");
-               return -1;
-       }
-       res = check_type(testdir, S_IFDIR);
-       if (res == -1)
-               return -1;
-       err += check_mode(testdir, 0755);
-       err += check_nlink(testdir, 2);
-       err += check_dir_contents(testdir, dir_contents);
-       res = rmdir(testdir);
-       if (res == -1) {
-               PERROR("rmdir");
-               return -1;
-       }
-       res = check_nonexist(testdir);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-#define test_create_ro_dir(flags)       \
-       do_test_create_ro_dir(flags, #flags)
-
-static int do_test_create_ro_dir(int flags, const char *flags_str)
-{
-       int res;
-       int err = 0;
-       int fd;
-
-       start_test("open(%s) in read-only directory", flags_str);
-       rmdir(testdir);
-       res = mkdir(testdir, 0555);
-       if (res == -1) {
-               PERROR("mkdir");
-               return -1;
-       }
-       fd = open(subfile, flags, 0644);
-       if (fd != -1) {
-               close(fd);
-               unlink(subfile);
-               ERROR("open should have failed");
-               err--;
-       } else {
-               res = check_nonexist(subfile);
-               if (res == -1)
-                       err--;
-       }
-       unlink(subfile);
-       res = rmdir(testdir);
-       if (res == -1) {
-               PERROR("rmdir");
-               return -1;
-       }
-       res = check_nonexist(testdir);
-       if (res == -1)
-               return -1;
-       if (err)
-               return -1;
-
-       success();
-       return 0;
-}
-
-int main(int argc, char *argv[])
-{
-       const char *basepath;
-       const char *realpath;
-       int err = 0;
-       int a;
-       int is_root;
-
-       umask(0);
-       if (argc < 2 || argc > 4) {
-               fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#]\n", argv[0]);
-               return 1;
-       }
-       basepath = argv[1];
-       realpath = basepath;
-       for (a = 2; a < argc; a++) {
-               char *endptr;
-               char *arg = argv[a];
-               if (arg[0] == ':') {
-                       realpath = arg + 1;
-               } else {
-                       if (arg[0] == '-') {
-                               arg++;
-                               skip_test = strtoul(arg, &endptr, 10);
-                       } else {
-                               select_test = strtoul(arg, &endptr, 10);
-                       }
-                       if (arg[0] == '\0' || *endptr != '\0') {
-                               fprintf(stderr, "invalid number: '%s'\n", arg);
-                               return 1;
-                       }
-               }
-       }
-       assert(strlen(basepath) < 512);
-       assert(strlen(realpath) < 512);
-       if (basepath[0] != '/') {
-               fprintf(stderr, "testdir must be an absolute path\n");
-               return 1;
-       }
-
-       sprintf(testfile, "%s/testfile", basepath);
-       sprintf(testfile2, "%s/testfile2", basepath);
-       sprintf(testdir, "%s/testdir", basepath);
-       sprintf(testdir2, "%s/testdir2", basepath);
-       sprintf(subfile, "%s/subfile", testdir2);
-
-       sprintf(testfile_r, "%s/testfile", realpath);
-       sprintf(testfile2_r, "%s/testfile2", realpath);
-       sprintf(testdir_r, "%s/testdir", realpath);
-       sprintf(testdir2_r, "%s/testdir2", realpath);
-       sprintf(subfile_r, "%s/subfile", testdir2_r);
-
-       is_root = (geteuid() == 0);
-
-       err += test_create();
-       err += test_create_unlink();
-       err += test_mknod();
-       err += test_symlink();
-       err += test_link();
-       err += test_link2();
-       err += test_mkfifo();
-       err += test_mkdir();
-       err += test_rename_file();
-       err += test_rename_dir();
-       err += test_utime();
-       err += test_truncate(0);
-       err += test_truncate(testdatalen / 2);
-       err += test_truncate(testdatalen);
-       err += test_truncate(testdatalen + 100);
-       err += test_ftruncate(0, 0600);
-       err += test_ftruncate(testdatalen / 2, 0600);
-       err += test_ftruncate(testdatalen, 0600);
-       err += test_ftruncate(testdatalen + 100, 0600);
-       err += test_ftruncate(0, 0400);
-       err += test_ftruncate(0, 0200);
-       err += test_ftruncate(0, 0000);
-       err += test_open(0, O_RDONLY, 0);
-       err += test_open(1, O_RDONLY, 0);
-       err += test_open(1, O_RDWR, 0);
-       err += test_open(1, O_WRONLY, 0);
-       err += test_open(0, O_RDWR | O_CREAT, 0600);
-       err += test_open(1, O_RDWR | O_CREAT, 0600);
-       err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600);
-       err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600);
-       err += test_open(0, O_RDONLY | O_CREAT, 0600);
-       err += test_open(0, O_RDONLY | O_CREAT, 0400);
-       err += test_open(0, O_RDONLY | O_CREAT, 0200);
-       err += test_open(0, O_RDONLY | O_CREAT, 0000);
-       err += test_open(0, O_WRONLY | O_CREAT, 0600);
-       err += test_open(0, O_WRONLY | O_CREAT, 0400);
-       err += test_open(0, O_WRONLY | O_CREAT, 0200);
-       err += test_open(0, O_WRONLY | O_CREAT, 0000);
-       err += test_open(0, O_RDWR | O_CREAT, 0400);
-       err += test_open(0, O_RDWR | O_CREAT, 0200);
-       err += test_open(0, O_RDWR | O_CREAT, 0000);
-       err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600);
-       err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
-       err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
-       err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
-       err += test_open_acc(O_RDONLY, 0600, 0);
-       err += test_open_acc(O_WRONLY, 0600, 0);
-       err += test_open_acc(O_RDWR,   0600, 0);
-       err += test_open_acc(O_RDONLY, 0400, 0);
-       err += test_open_acc(O_WRONLY, 0200, 0);
-       if(!is_root) {
-               err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
-               err += test_open_acc(O_WRONLY, 0400, EACCES);
-               err += test_open_acc(O_RDWR,   0400, EACCES);
-               err += test_open_acc(O_RDONLY, 0200, EACCES);
-               err += test_open_acc(O_RDWR,   0200, EACCES);
-               err += test_open_acc(O_RDONLY, 0000, EACCES);
-               err += test_open_acc(O_WRONLY, 0000, EACCES);
-               err += test_open_acc(O_RDWR,   0000, EACCES);
-       }
-       err += test_create_ro_dir(O_CREAT);
-       err += test_create_ro_dir(O_CREAT | O_EXCL);
-       err += test_create_ro_dir(O_CREAT | O_WRONLY);
-       err += test_create_ro_dir(O_CREAT | O_TRUNC);
-
-       unlink(testfile);
-       unlink(testfile2);
-       rmdir(testdir);
-       rmdir(testdir2);
-
-       if (err) {
-               fprintf(stderr, "%i tests failed\n", -err);
-               return 1;
-       }
-
-       return 0;
-}
index cfd6734c924d2636c52b87baf65adb72f1fd4848..92a67c04a5702b599f36e734406b20e25308920a 100755 (executable)
@@ -77,7 +77,7 @@ def test_passthrough(tmpdir, name, debug):
         wait_for_mount(mount_process, mnt_dir)
         work_dir = pjoin(mnt_dir, src_dir)
 
-        subprocess.check_call([ os.path.join(basename, 'test', 'test'),
+        subprocess.check_call([ os.path.join(basename, 'test', 'test_syscalls'),
                     work_dir, ':' + src_dir ])
 
         tst_write(work_dir)
diff --git a/test/test_syscalls.c b/test/test_syscalls.c
new file mode 100644 (file)
index 0000000..281f218
--- /dev/null
@@ -0,0 +1,1522 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <utime.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+static char testfile[1024];
+static char testfile2[1024];
+static char testdir[1024];
+static char testdir2[1024];
+static char subfile[1024];
+
+static char testfile_r[1024];
+static char testfile2_r[1024];
+static char testdir_r[1024];
+static char testdir2_r[1024];
+static char subfile_r[1024];
+
+static char testname[256];
+static char testdata[] = "abcdefghijklmnopqrstuvwxyz";
+static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./";
+static const char *testdir_files[] = { "f1", "f2", NULL};
+static char zerodata[4096];
+static int testdatalen = sizeof(testdata) - 1;
+static int testdata2len = sizeof(testdata2) - 1;
+static unsigned int testnum = 1;
+static unsigned int select_test = 0;
+static unsigned int skip_test = 0;
+
+#define MAX_ENTRIES 1024
+
+static void test_perror(const char *func, const char *msg)
+{
+       fprintf(stderr, "%s %s() - %s: %s\n", testname, func, msg,
+               strerror(errno));
+}
+
+static void test_error(const char *func, const char *msg, ...)
+       __attribute__ ((format (printf, 2, 3)));
+
+static void __start_test(const char *fmt, ...)
+       __attribute__ ((format (printf, 1, 2)));
+
+static void test_error(const char *func, const char *msg, ...)
+{
+       va_list ap;
+       fprintf(stderr, "%s %s() - ", testname, func);
+       va_start(ap, msg);
+       vfprintf(stderr, msg, ap);
+       va_end(ap);
+       fprintf(stderr, "\n");
+}
+
+static void success(void)
+{
+       fprintf(stderr, "%s OK\n", testname);
+}
+
+static void __start_test(const char *fmt, ...)
+{
+       unsigned int n;
+       va_list ap;
+       n = sprintf(testname, "%3i [", testnum++);
+       va_start(ap, fmt);
+       n += vsprintf(testname + n, fmt, ap);
+       va_end(ap);
+       sprintf(testname + n, "]");
+}
+
+#define start_test(msg, args...) { \
+       if ((select_test && testnum != select_test) || \
+           (testnum == skip_test)) { \
+               testnum++; \
+               return 0; \
+       } \
+       __start_test(msg, ##args);              \
+}
+
+#define PERROR(msg) test_perror(__FUNCTION__, msg)
+#define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args)
+
+static int check_size(const char *path, int len)
+{
+       struct stat stbuf;
+       int res = stat(path, &stbuf);
+       if (res == -1) {
+               PERROR("stat");
+               return -1;
+       }
+       if (stbuf.st_size != len) {
+               ERROR("length %u instead of %u", (int) stbuf.st_size,
+                     (int) len);
+               return -1;
+       }
+       return 0;
+}
+
+static int fcheck_size(int fd, int len)
+{
+       struct stat stbuf;
+       int res = fstat(fd, &stbuf);
+       if (res == -1) {
+               PERROR("fstat");
+               return -1;
+       }
+       if (stbuf.st_size != len) {
+               ERROR("length %u instead of %u", (int) stbuf.st_size,
+                     (int) len);
+               return -1;
+       }
+       return 0;
+}
+
+static int check_type(const char *path, mode_t type)
+{
+       struct stat stbuf;
+       int res = lstat(path, &stbuf);
+       if (res == -1) {
+               PERROR("lstat");
+               return -1;
+       }
+       if ((stbuf.st_mode & S_IFMT) != type) {
+               ERROR("type 0%o instead of 0%o", stbuf.st_mode & S_IFMT, type);
+               return -1;
+       }
+       return 0;
+}
+
+static int fcheck_type(int fd, mode_t type)
+{
+       struct stat stbuf;
+       int res = fstat(fd, &stbuf);
+       if (res == -1) {
+               PERROR("fstat");
+               return -1;
+       }
+       if ((stbuf.st_mode & S_IFMT) != type) {
+               ERROR("type 0%o instead of 0%o", stbuf.st_mode & S_IFMT, type);
+               return -1;
+       }
+       return 0;
+}
+
+static int check_mode(const char *path, mode_t mode)
+{
+       struct stat stbuf;
+       int res = lstat(path, &stbuf);
+       if (res == -1) {
+               PERROR("lstat");
+               return -1;
+       }
+       if ((stbuf.st_mode & 07777) != mode) {
+               ERROR("mode 0%o instead of 0%o", stbuf.st_mode & 07777, mode);
+               return -1;
+       }
+       return 0;
+}
+
+static int fcheck_mode(int fd, mode_t mode)
+{
+       struct stat stbuf;
+       int res = fstat(fd, &stbuf);
+       if (res == -1) {
+               PERROR("fstat");
+               return -1;
+       }
+       if ((stbuf.st_mode & 07777) != mode) {
+               ERROR("mode 0%o instead of 0%o", stbuf.st_mode & 07777, mode);
+               return -1;
+       }
+       return 0;
+}
+
+static int check_times(const char *path, time_t atime, time_t mtime)
+{
+       int err = 0;
+       struct stat stbuf;
+       int res = lstat(path, &stbuf);
+       if (res == -1) {
+               PERROR("lstat");
+               return -1;
+       }
+       if (stbuf.st_atime != atime) {
+               ERROR("atime %li instead of %li", stbuf.st_atime, atime);
+               err--;
+       }
+       if (stbuf.st_mtime != mtime) {
+               ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
+               err--;
+       }
+       if (err)
+               return -1;
+
+       return 0;
+}
+
+#if 0
+static int fcheck_times(int fd, time_t atime, time_t mtime)
+{
+       int err = 0;
+       struct stat stbuf;
+       int res = fstat(fd, &stbuf);
+       if (res == -1) {
+               PERROR("fstat");
+               return -1;
+       }
+       if (stbuf.st_atime != atime) {
+               ERROR("atime %li instead of %li", stbuf.st_atime, atime);
+               err--;
+       }
+       if (stbuf.st_mtime != mtime) {
+               ERROR("mtime %li instead of %li", stbuf.st_mtime, mtime);
+               err--;
+       }
+       if (err)
+               return -1;
+
+       return 0;
+}
+#endif
+
+static int check_nlink(const char *path, nlink_t nlink)
+{
+       struct stat stbuf;
+       int res = lstat(path, &stbuf);
+       if (res == -1) {
+               PERROR("lstat");
+               return -1;
+       }
+       if (stbuf.st_nlink != nlink) {
+               ERROR("nlink %li instead of %li", (long) stbuf.st_nlink,
+                     (long) nlink);
+               return -1;
+       }
+       return 0;
+}
+
+static int fcheck_nlink(int fd, nlink_t nlink)
+{
+       struct stat stbuf;
+       int res = fstat(fd, &stbuf);
+       if (res == -1) {
+               PERROR("fstat");
+               return -1;
+       }
+       if (stbuf.st_nlink != nlink) {
+               ERROR("nlink %li instead of %li", (long) stbuf.st_nlink,
+                     (long) nlink);
+               return -1;
+       }
+       return 0;
+}
+
+static int check_nonexist(const char *path)
+{
+       struct stat stbuf;
+       int res = lstat(path, &stbuf);
+       if (res == 0) {
+               ERROR("file should not exist");
+               return -1;
+       }
+       if (errno != ENOENT) {
+               ERROR("file should not exist: %s", strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
+static int check_buffer(const char *buf, const char *data, unsigned len)
+{
+       if (memcmp(buf, data, len) != 0) {
+               ERROR("data mismatch");
+               return -1;
+       }
+       return 0;
+}
+
+static int check_data(const char *path, const char *data, int offset,
+                     unsigned len)
+{
+       char buf[4096];
+       int res;
+       int fd = open(path, O_RDONLY);
+       if (fd == -1) {
+               PERROR("open");
+               return -1;
+       }
+       if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
+               PERROR("lseek");
+               close(fd);
+               return -1;
+       }
+       while (len) {
+               int rdlen = len < sizeof(buf) ? len : sizeof(buf);
+               res = read(fd, buf, rdlen);
+               if (res == -1) {
+                       PERROR("read");
+                       close(fd);
+                       return -1;
+               }
+               if (res != rdlen) {
+                       ERROR("short read: %u instead of %u", res, rdlen);
+                       close(fd);
+                       return -1;
+               }
+               if (check_buffer(buf, data, rdlen) != 0) {
+                       close(fd);
+                       return -1;
+               }
+               data += rdlen;
+               len -= rdlen;
+       }
+       res = close(fd);
+       if (res == -1) {
+               PERROR("close");
+               return -1;
+       }
+       return 0;
+}
+
+static int fcheck_data(int fd, const char *data, int offset,
+                      unsigned len)
+{
+       char buf[4096];
+       int res;
+       if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
+               PERROR("lseek");
+               return -1;
+       }
+       while (len) {
+               int rdlen = len < sizeof(buf) ? len : sizeof(buf);
+               res = read(fd, buf, rdlen);
+               if (res == -1) {
+                       PERROR("read");
+                       return -1;
+               }
+               if (res != rdlen) {
+                       ERROR("short read: %u instead of %u", res, rdlen);
+                       return -1;
+               }
+               if (check_buffer(buf, data, rdlen) != 0) {
+                       return -1;
+               }
+               data += rdlen;
+               len -= rdlen;
+       }
+       return 0;
+}
+
+static int check_dir_contents(const char *path, const char **contents)
+{
+       int i;
+       int res;
+       int err = 0;
+       int found[MAX_ENTRIES];
+       const char *cont[MAX_ENTRIES];
+       DIR *dp;
+
+       for (i = 0; contents[i]; i++) {
+               assert(i < MAX_ENTRIES - 3);
+               found[i] = 0;
+               cont[i] = contents[i];
+       }
+       found[i] = 0;
+       cont[i++] = ".";
+       found[i] = 0;
+       cont[i++] = "..";
+       cont[i] = NULL;
+
+       dp = opendir(path);
+       if (dp == NULL) {
+               PERROR("opendir");
+               return -1;
+       }
+       memset(found, 0, sizeof(found));
+       while(1) {
+               struct dirent *de;
+               errno = 0;
+               de = readdir(dp);
+               if (de == NULL) {
+                       if (errno) {
+                               PERROR("readdir");
+                               closedir(dp);
+                               return -1;
+                       }
+                       break;
+               }
+               for (i = 0; cont[i] != NULL; i++) {
+                       assert(i < MAX_ENTRIES);
+                       if (strcmp(cont[i], de->d_name) == 0) {
+                               if (found[i]) {
+                                       ERROR("duplicate entry <%s>",
+                                             de->d_name);
+                                       err--;
+                               } else
+                                       found[i] = 1;
+                               break;
+                       }
+               }
+               if (!cont[i]) {
+                       ERROR("unexpected entry <%s>", de->d_name);
+                       err --;
+               }
+       }
+       for (i = 0; cont[i] != NULL; i++) {
+               if (!found[i]) {
+                       ERROR("missing entry <%s>", cont[i]);
+                       err--;
+               }
+       }
+       res = closedir(dp);
+       if (res == -1) {
+               PERROR("closedir");
+               return -1;
+       }
+       if (err)
+               return -1;
+
+       return 0;
+}
+
+static int create_file(const char *path, const char *data, int len)
+{
+       int res;
+       int fd;
+
+       unlink(path);
+       fd = creat(path, 0644);
+       if (fd == -1) {
+               PERROR("creat");
+               return -1;
+       }
+       if (len) {
+               res = write(fd, data, len);
+               if (res == -1) {
+                       PERROR("write");
+                       close(fd);
+                       return -1;
+               }
+               if (res != len) {
+                       ERROR("write is short: %u instead of %u", res, len);
+                       close(fd);
+                       return -1;
+               }
+       }
+       res = close(fd);
+       if (res == -1) {
+               PERROR("close");
+               return -1;
+       }
+       res = check_type(path, S_IFREG);
+       if (res == -1)
+               return -1;
+       res = check_mode(path, 0644);
+       if (res == -1)
+               return -1;
+       res = check_nlink(path, 1);
+       if (res == -1)
+               return -1;
+       res = check_size(path, len);
+       if (res == -1)
+               return -1;
+
+       if (len) {
+               res = check_data(path, data, 0, len);
+               if (res == -1)
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int cleanup_dir(const char *path, const char **dir_files, int quiet)
+{
+       int i;
+       int err = 0;
+
+       for (i = 0; dir_files[i]; i++) {
+               int res;
+               char fpath[1024];
+               sprintf(fpath, "%s/%s", path, dir_files[i]);
+               res = unlink(fpath);
+               if (res == -1 && !quiet) {
+                       PERROR("unlink");
+                       err --;
+               }
+       }
+       if (err)
+               return -1;
+
+       return 0;
+}
+
+static int create_dir(const char *path, const char **dir_files)
+{
+       int res;
+       int i;
+
+       rmdir(path);
+       res = mkdir(path, 0755);
+       if (res == -1) {
+               PERROR("mkdir");
+               return -1;
+       }
+       res = check_type(path, S_IFDIR);
+       if (res == -1)
+               return -1;
+       res = check_mode(path, 0755);
+       if (res == -1)
+               return -1;
+
+       for (i = 0; dir_files[i]; i++) {
+               char fpath[1024];
+               sprintf(fpath, "%s/%s", path, dir_files[i]);
+               res = create_file(fpath, "", 0);
+               if (res == -1) {
+                       cleanup_dir(path, dir_files, 1);
+                       return -1;
+               }
+       }
+       res = check_dir_contents(path, dir_files);
+       if (res == -1) {
+               cleanup_dir(path, dir_files, 1);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int test_truncate(int len)
+{
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int res;
+
+       start_test("truncate(%u)", (int) len);
+       res = create_file(testfile, data, datalen);
+       if (res == -1)
+               return -1;
+
+       res = truncate(testfile, len);
+       if (res == -1) {
+               PERROR("truncate");
+               return -1;
+       }
+       res = check_size(testfile, len);
+       if (res == -1)
+               return -1;
+
+       if (len > 0) {
+               if (len <= datalen) {
+                       res = check_data(testfile, data, 0, len);
+                       if (res == -1)
+                               return -1;
+               } else {
+                       res = check_data(testfile, data, 0, datalen);
+                       if (res == -1)
+                               return -1;
+                       res = check_data(testfile, zerodata, datalen,
+                                        len - datalen);
+                       if (res == -1)
+                               return -1;
+               }
+       }
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_ftruncate(int len, int mode)
+{
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int res;
+       int fd;
+
+       start_test("ftruncate(%u) mode: 0%03o", len, mode);
+       res = create_file(testfile, data, datalen);
+       if (res == -1)
+               return -1;
+
+       fd = open(testfile, O_WRONLY);
+       if (fd == -1) {
+               PERROR("open");
+               return -1;
+       }
+
+       res = fchmod(fd, mode);
+       if (res == -1) {
+               PERROR("fchmod");
+               close(fd);
+               return -1;
+       }
+       res = check_mode(testfile, mode);
+       if (res == -1) {
+               close(fd);
+               return -1;
+       }
+       res = ftruncate(fd, len);
+       if (res == -1) {
+               PERROR("ftruncate");
+               close(fd);
+               return -1;
+       }
+       close(fd);
+       res = check_size(testfile, len);
+       if (res == -1)
+               return -1;
+
+       if (len > 0) {
+               if (len <= datalen) {
+                       res = check_data(testfile, data, 0, len);
+                       if (res == -1)
+                               return -1;
+               } else {
+                       res = check_data(testfile, data, 0, datalen);
+                       if (res == -1)
+                               return -1;
+                       res = check_data(testfile, zerodata, datalen,
+                                        len - datalen);
+                       if (res == -1)
+                               return -1;
+               }
+       }
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_utime(void)
+{
+       struct utimbuf utm;
+       time_t atime = 987631200;
+       time_t mtime = 123116400;
+       int res;
+
+       start_test("utime");
+       res = create_file(testfile, NULL, 0);
+       if (res == -1)
+               return -1;
+
+       utm.actime = atime;
+       utm.modtime = mtime;
+       res = utime(testfile, &utm);
+       if (res == -1) {
+               PERROR("utime");
+               return -1;
+       }
+       res = check_times(testfile, atime, mtime);
+       if (res == -1) {
+               return -1;
+       }
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_create(void)
+{
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int err = 0;
+       int res;
+       int fd;
+
+       start_test("create");
+       unlink(testfile);
+       fd = creat(testfile, 0644);
+       if (fd == -1) {
+               PERROR("creat");
+               return -1;
+       }
+       res = write(fd, data, datalen);
+       if (res == -1) {
+               PERROR("write");
+               close(fd);
+               return -1;
+       }
+       if (res != datalen) {
+               ERROR("write is short: %u instead of %u", res, datalen);
+               close(fd);
+               return -1;
+       }
+       res = close(fd);
+       if (res == -1) {
+               PERROR("close");
+               return -1;
+       }
+       res = check_type(testfile, S_IFREG);
+       if (res == -1)
+               return -1;
+       err += check_mode(testfile, 0644);
+       err += check_nlink(testfile, 1);
+       err += check_size(testfile, datalen);
+       err += check_data(testfile, data, 0, datalen);
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_create_unlink(void)
+{
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int err = 0;
+       int res;
+       int fd;
+
+       start_test("create+unlink");
+       unlink(testfile);
+       fd = open(testfile, O_CREAT | O_RDWR | O_TRUNC, 0644);
+       if (fd == -1) {
+               PERROR("creat");
+               return -1;
+       }
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               close(fd);
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+       res = write(fd, data, datalen);
+       if (res == -1) {
+               PERROR("write");
+               close(fd);
+               return -1;
+       }
+       if (res != datalen) {
+               ERROR("write is short: %u instead of %u", res, datalen);
+               close(fd);
+               return -1;
+       }
+       err += fcheck_type(fd, S_IFREG);
+       err += fcheck_mode(fd, 0644);
+       err += fcheck_nlink(fd, 0);
+       err += fcheck_size(fd, datalen);
+       err += fcheck_data(fd, data, 0, datalen);
+       res = close(fd);
+       if (res == -1) {
+               PERROR("close");
+               err--;
+       }
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_mknod(void)
+{
+       int err = 0;
+       int res;
+
+       start_test("mknod");
+       unlink(testfile);
+       res = mknod(testfile, 0644, 0);
+       if (res == -1) {
+               PERROR("mknod");
+               return -1;
+       }
+       res = check_type(testfile, S_IFREG);
+       if (res == -1)
+               return -1;
+       err += check_mode(testfile, 0644);
+       err += check_nlink(testfile, 1);
+       err += check_size(testfile, 0);
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+#define test_open(exist, flags, mode)  do_test_open(exist, flags, #flags, mode)
+
+static int do_test_open(int exist, int flags, const char *flags_str, int mode)
+{
+       char buf[4096];
+       const char *data = testdata;
+       int datalen = testdatalen;
+       unsigned currlen = 0;
+       int err = 0;
+       int res;
+       int fd;
+       off_t off;
+
+       start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode);
+       unlink(testfile);
+       if (exist) {
+               res = create_file(testfile_r, testdata2, testdata2len);
+               if (res == -1)
+                       return -1;
+
+               currlen = testdata2len;
+       }
+
+       fd = open(testfile, flags, mode);
+       if ((flags & O_CREAT) && (flags & O_EXCL) && exist) {
+               if (fd != -1) {
+                       ERROR("open should have failed");
+                       close(fd);
+                       return -1;
+               } else if (errno == EEXIST)
+                       goto succ;
+       }
+       if (!(flags & O_CREAT) && !exist) {
+               if (fd != -1) {
+                       ERROR("open should have failed");
+                       close(fd);
+                       return -1;
+               } else if (errno == ENOENT)
+                       goto succ;
+       }
+       if (fd == -1) {
+               PERROR("open");
+               return -1;
+       }
+
+       if (flags & O_TRUNC)
+               currlen = 0;
+
+       err += check_type(testfile, S_IFREG);
+       if (exist)
+               err += check_mode(testfile, 0644);
+       else
+               err += check_mode(testfile, mode);
+       err += check_nlink(testfile, 1);
+       err += check_size(testfile, currlen);
+       if (exist && !(flags & O_TRUNC) && (mode & 0400))
+               err += check_data(testfile, testdata2, 0, testdata2len);
+
+       res = write(fd, data, datalen);
+       if ((flags & O_ACCMODE) != O_RDONLY) {
+               if (res == -1) {
+                       PERROR("write");
+                       err --;
+               } else if (res != datalen) {
+                       ERROR("write is short: %u instead of %u", res, datalen);
+                       err --;
+               } else {
+                       if (datalen > (int) currlen)
+                               currlen = datalen;
+
+                       err += check_size(testfile, currlen);
+
+                       if (mode & 0400) {
+                               err += check_data(testfile, data, 0, datalen);
+                               if (exist && !(flags & O_TRUNC) &&
+                                   testdata2len > datalen)
+                                       err += check_data(testfile,
+                                                         testdata2 + datalen,
+                                                         datalen,
+                                                         testdata2len - datalen);
+                       }
+               }
+       } else {
+               if (res != -1) {
+                       ERROR("write should have failed");
+                       err --;
+               } else if (errno != EBADF) {
+                       PERROR("write");
+                       err --;
+               }
+       }
+       off = lseek(fd, SEEK_SET, 0);
+       if (off == (off_t) -1) {
+               PERROR("lseek");
+               err--;
+       } else if (off != 0) {
+               ERROR("offset should have returned 0");
+               err --;
+       }
+       res = read(fd, buf, sizeof(buf));
+       if ((flags & O_ACCMODE) != O_WRONLY) {
+               if (res == -1) {
+                       PERROR("read");
+                       err--;
+               } else {
+                       int readsize =
+                               currlen < sizeof(buf) ? currlen : sizeof(buf);
+                       if (res != readsize) {
+                               ERROR("read is short: %i instead of %u",
+                                     res, readsize);
+                               err--;
+                       } else {
+                               if ((flags & O_ACCMODE) != O_RDONLY) {
+                                       err += check_buffer(buf, data, datalen);
+                                       if (exist && !(flags & O_TRUNC) &&
+                                           testdata2len > datalen)
+                                               err += check_buffer(buf + datalen,
+                                                                   testdata2 + datalen,
+                                                                   testdata2len - datalen);
+                               } else if (exist)
+                                       err += check_buffer(buf, testdata2,
+                                                           testdata2len);
+                       }
+               }
+       } else {
+               if (res != -1) {
+                       ERROR("read should have failed");
+                       err --;
+               } else if (errno != EBADF) {
+                       PERROR("read");
+                       err --;
+               }
+       }
+
+       res = close(fd);
+       if (res == -1) {
+               PERROR("close");
+               return -1;
+       }
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+       res = check_nonexist(testfile_r);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+succ:
+       success();
+       return 0;
+}
+
+#define test_open_acc(flags, mode, err)         \
+       do_test_open_acc(flags, #flags, mode, err)
+
+static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
+{
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int res;
+       int fd;
+
+       start_test("open_acc(%s) mode: 0%03o message: '%s'", flags_str, mode,
+                  strerror(err));
+       unlink(testfile);
+       res = create_file(testfile, data, datalen);
+       if (res == -1)
+               return -1;
+
+       res = chmod(testfile, mode);
+       if (res == -1) {
+               PERROR("chmod");
+               return -1;
+       }
+
+       res = check_mode(testfile, mode);
+       if (res == -1)
+               return -1;
+
+       fd = open(testfile, flags);
+       if (fd == -1) {
+               if (err != errno) {
+                       PERROR("open");
+                       return -1;
+               }
+       } else {
+               if (err) {
+                       ERROR("open should have failed");
+                       close(fd);
+                       return -1;
+               }
+               close(fd);
+       }
+       success();
+       return 0;
+}
+
+static int test_symlink(void)
+{
+       char buf[1024];
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int linklen = strlen(testfile);
+       int err = 0;
+       int res;
+
+       start_test("symlink");
+       res = create_file(testfile, data, datalen);
+       if (res == -1)
+               return -1;
+
+       unlink(testfile2);
+       res = symlink(testfile, testfile2);
+       if (res == -1) {
+               PERROR("symlink");
+               return -1;
+       }
+       res = check_type(testfile2, S_IFLNK);
+       if (res == -1)
+               return -1;
+       err += check_mode(testfile2, 0777);
+       err += check_nlink(testfile2, 1);
+       res = readlink(testfile2, buf, sizeof(buf));
+       if (res == -1) {
+               PERROR("readlink");
+               err--;
+       }
+       if (res != linklen) {
+               ERROR("short readlink: %u instead of %u", res, linklen);
+               err--;
+       }
+       if (memcmp(buf, testfile, linklen) != 0) {
+               ERROR("link mismatch");
+               err--;
+       }
+       err += check_size(testfile2, datalen);
+       err += check_data(testfile2, data, 0, datalen);
+       res = unlink(testfile2);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile2);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_link(void)
+{
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int err = 0;
+       int res;
+
+       start_test("link");
+       res = create_file(testfile, data, datalen);
+       if (res == -1)
+               return -1;
+
+       unlink(testfile2);
+       res = link(testfile, testfile2);
+       if (res == -1) {
+               PERROR("link");
+               return -1;
+       }
+       res = check_type(testfile2, S_IFREG);
+       if (res == -1)
+               return -1;
+       err += check_mode(testfile2, 0644);
+       err += check_nlink(testfile2, 2);
+       err += check_size(testfile2, datalen);
+       err += check_data(testfile2, data, 0, datalen);
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+
+       err += check_nlink(testfile2, 1);
+       res = unlink(testfile2);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile2);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_link2(void)
+{
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int err = 0;
+       int res;
+
+       start_test("link-unlink-link");
+       res = create_file(testfile, data, datalen);
+       if (res == -1)
+               return -1;
+
+       unlink(testfile2);
+       res = link(testfile, testfile2);
+       if (res == -1) {
+               PERROR("link");
+               return -1;
+       }
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+       res = link(testfile2, testfile);
+       if (res == -1) {
+               PERROR("link");
+       }
+       res = check_type(testfile, S_IFREG);
+       if (res == -1)
+               return -1;
+       err += check_mode(testfile, 0644);
+       err += check_nlink(testfile, 2);
+       err += check_size(testfile, datalen);
+       err += check_data(testfile, data, 0, datalen);
+
+       res = unlink(testfile2);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       err += check_nlink(testfile, 1);
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_rename_file(void)
+{
+       const char *data = testdata;
+       int datalen = testdatalen;
+       int err = 0;
+       int res;
+
+       start_test("rename file");
+       res = create_file(testfile, data, datalen);
+       if (res == -1)
+               return -1;
+
+       unlink(testfile2);
+       res = rename(testfile, testfile2);
+       if (res == -1) {
+               PERROR("rename");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+       res = check_type(testfile2, S_IFREG);
+       if (res == -1)
+               return -1;
+       err += check_mode(testfile2, 0644);
+       err += check_nlink(testfile2, 1);
+       err += check_size(testfile2, datalen);
+       err += check_data(testfile2, data, 0, datalen);
+       res = unlink(testfile2);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile2);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_rename_dir(void)
+{
+       int err = 0;
+       int res;
+
+       start_test("rename dir");
+       res = create_dir(testdir, testdir_files);
+       if (res == -1)
+               return -1;
+
+       rmdir(testdir2);
+       res = rename(testdir, testdir2);
+       if (res == -1) {
+               PERROR("rename");
+               cleanup_dir(testdir, testdir_files, 1);
+               return -1;
+       }
+       res = check_nonexist(testdir);
+       if (res == -1) {
+               cleanup_dir(testdir, testdir_files, 1);
+               return -1;
+       }
+       res = check_type(testdir2, S_IFDIR);
+       if (res == -1) {
+               cleanup_dir(testdir2, testdir_files, 1);
+               return -1;
+       }
+       err += check_mode(testdir2, 0755);
+       err += check_dir_contents(testdir2, testdir_files);
+       err += cleanup_dir(testdir2, testdir_files, 0);
+       res = rmdir(testdir2);
+       if (res == -1) {
+               PERROR("rmdir");
+               return -1;
+       }
+       res = check_nonexist(testdir2);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_mkfifo(void)
+{
+       int res;
+       int err = 0;
+
+       start_test("mkfifo");
+       unlink(testfile);
+       res = mkfifo(testfile, 0644);
+       if (res == -1) {
+               PERROR("mkfifo");
+               return -1;
+       }
+       res = check_type(testfile, S_IFIFO);
+       if (res == -1)
+               return -1;
+       err += check_mode(testfile, 0644);
+       err += check_nlink(testfile, 1);
+       res = unlink(testfile);
+       if (res == -1) {
+               PERROR("unlink");
+               return -1;
+       }
+       res = check_nonexist(testfile);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+static int test_mkdir(void)
+{
+       int res;
+       int err = 0;
+       const char *dir_contents[] = {NULL};
+
+       start_test("mkdir");
+       rmdir(testdir);
+       res = mkdir(testdir, 0755);
+       if (res == -1) {
+               PERROR("mkdir");
+               return -1;
+       }
+       res = check_type(testdir, S_IFDIR);
+       if (res == -1)
+               return -1;
+       err += check_mode(testdir, 0755);
+       err += check_nlink(testdir, 2);
+       err += check_dir_contents(testdir, dir_contents);
+       res = rmdir(testdir);
+       if (res == -1) {
+               PERROR("rmdir");
+               return -1;
+       }
+       res = check_nonexist(testdir);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+#define test_create_ro_dir(flags)       \
+       do_test_create_ro_dir(flags, #flags)
+
+static int do_test_create_ro_dir(int flags, const char *flags_str)
+{
+       int res;
+       int err = 0;
+       int fd;
+
+       start_test("open(%s) in read-only directory", flags_str);
+       rmdir(testdir);
+       res = mkdir(testdir, 0555);
+       if (res == -1) {
+               PERROR("mkdir");
+               return -1;
+       }
+       fd = open(subfile, flags, 0644);
+       if (fd != -1) {
+               close(fd);
+               unlink(subfile);
+               ERROR("open should have failed");
+               err--;
+       } else {
+               res = check_nonexist(subfile);
+               if (res == -1)
+                       err--;
+       }
+       unlink(subfile);
+       res = rmdir(testdir);
+       if (res == -1) {
+               PERROR("rmdir");
+               return -1;
+       }
+       res = check_nonexist(testdir);
+       if (res == -1)
+               return -1;
+       if (err)
+               return -1;
+
+       success();
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       const char *basepath;
+       const char *realpath;
+       int err = 0;
+       int a;
+       int is_root;
+
+       umask(0);
+       if (argc < 2 || argc > 4) {
+               fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#]\n", argv[0]);
+               return 1;
+       }
+       basepath = argv[1];
+       realpath = basepath;
+       for (a = 2; a < argc; a++) {
+               char *endptr;
+               char *arg = argv[a];
+               if (arg[0] == ':') {
+                       realpath = arg + 1;
+               } else {
+                       if (arg[0] == '-') {
+                               arg++;
+                               skip_test = strtoul(arg, &endptr, 10);
+                       } else {
+                               select_test = strtoul(arg, &endptr, 10);
+                       }
+                       if (arg[0] == '\0' || *endptr != '\0') {
+                               fprintf(stderr, "invalid number: '%s'\n", arg);
+                               return 1;
+                       }
+               }
+       }
+       assert(strlen(basepath) < 512);
+       assert(strlen(realpath) < 512);
+       if (basepath[0] != '/') {
+               fprintf(stderr, "testdir must be an absolute path\n");
+               return 1;
+       }
+
+       sprintf(testfile, "%s/testfile", basepath);
+       sprintf(testfile2, "%s/testfile2", basepath);
+       sprintf(testdir, "%s/testdir", basepath);
+       sprintf(testdir2, "%s/testdir2", basepath);
+       sprintf(subfile, "%s/subfile", testdir2);
+
+       sprintf(testfile_r, "%s/testfile", realpath);
+       sprintf(testfile2_r, "%s/testfile2", realpath);
+       sprintf(testdir_r, "%s/testdir", realpath);
+       sprintf(testdir2_r, "%s/testdir2", realpath);
+       sprintf(subfile_r, "%s/subfile", testdir2_r);
+
+       is_root = (geteuid() == 0);
+
+       err += test_create();
+       err += test_create_unlink();
+       err += test_mknod();
+       err += test_symlink();
+       err += test_link();
+       err += test_link2();
+       err += test_mkfifo();
+       err += test_mkdir();
+       err += test_rename_file();
+       err += test_rename_dir();
+       err += test_utime();
+       err += test_truncate(0);
+       err += test_truncate(testdatalen / 2);
+       err += test_truncate(testdatalen);
+       err += test_truncate(testdatalen + 100);
+       err += test_ftruncate(0, 0600);
+       err += test_ftruncate(testdatalen / 2, 0600);
+       err += test_ftruncate(testdatalen, 0600);
+       err += test_ftruncate(testdatalen + 100, 0600);
+       err += test_ftruncate(0, 0400);
+       err += test_ftruncate(0, 0200);
+       err += test_ftruncate(0, 0000);
+       err += test_open(0, O_RDONLY, 0);
+       err += test_open(1, O_RDONLY, 0);
+       err += test_open(1, O_RDWR, 0);
+       err += test_open(1, O_WRONLY, 0);
+       err += test_open(0, O_RDWR | O_CREAT, 0600);
+       err += test_open(1, O_RDWR | O_CREAT, 0600);
+       err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600);
+       err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600);
+       err += test_open(0, O_RDONLY | O_CREAT, 0600);
+       err += test_open(0, O_RDONLY | O_CREAT, 0400);
+       err += test_open(0, O_RDONLY | O_CREAT, 0200);
+       err += test_open(0, O_RDONLY | O_CREAT, 0000);
+       err += test_open(0, O_WRONLY | O_CREAT, 0600);
+       err += test_open(0, O_WRONLY | O_CREAT, 0400);
+       err += test_open(0, O_WRONLY | O_CREAT, 0200);
+       err += test_open(0, O_WRONLY | O_CREAT, 0000);
+       err += test_open(0, O_RDWR | O_CREAT, 0400);
+       err += test_open(0, O_RDWR | O_CREAT, 0200);
+       err += test_open(0, O_RDWR | O_CREAT, 0000);
+       err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600);
+       err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
+       err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
+       err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
+       err += test_open_acc(O_RDONLY, 0600, 0);
+       err += test_open_acc(O_WRONLY, 0600, 0);
+       err += test_open_acc(O_RDWR,   0600, 0);
+       err += test_open_acc(O_RDONLY, 0400, 0);
+       err += test_open_acc(O_WRONLY, 0200, 0);
+       if(!is_root) {
+               err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
+               err += test_open_acc(O_WRONLY, 0400, EACCES);
+               err += test_open_acc(O_RDWR,   0400, EACCES);
+               err += test_open_acc(O_RDONLY, 0200, EACCES);
+               err += test_open_acc(O_RDWR,   0200, EACCES);
+               err += test_open_acc(O_RDONLY, 0000, EACCES);
+               err += test_open_acc(O_WRONLY, 0000, EACCES);
+               err += test_open_acc(O_RDWR,   0000, EACCES);
+       }
+       err += test_create_ro_dir(O_CREAT);
+       err += test_create_ro_dir(O_CREAT | O_EXCL);
+       err += test_create_ro_dir(O_CREAT | O_WRONLY);
+       err += test_create_ro_dir(O_CREAT | O_TRUNC);
+
+       unlink(testfile);
+       unlink(testfile2);
+       rmdir(testdir);
+       rmdir(testdir2);
+
+       if (err) {
+               fprintf(stderr, "%i tests failed\n", -err);
+               return 1;
+       }
+
+       return 0;
+}
index 6bba9e26e7ac53fbd9650fb870cf833e0712596d..48670bdc3819c483907b28b3917dcf32cb4a4052 100644 (file)
@@ -105,7 +105,7 @@ def fuse_test_marker():
 
     return pytest.mark.uses_fuse()
 
-# If valgrind and libtool are available, use them
+# If valgrind is available, use it
 def has_program(name):
     try:
         ret = subprocess.call([name, '--version'],
@@ -115,9 +115,8 @@ def has_program(name):
         return False
     return ret == 0
 
-if has_program('valgrind') and has_program('libtool'):
-    base_cmdline = [ 'libtool', '--mode=execute',
-                     'valgrind', '-q', '--' ]
+if has_program('valgrind'):
+    base_cmdline = [ 'valgrind', '-q', '--' ]
 else:
     base_cmdline = []
 
diff --git a/test/wrong_command.c b/test/wrong_command.c
new file mode 100644 (file)
index 0000000..ef835b3
--- /dev/null
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+int main(void) {
+       fprintf(stderr, "\x1B[31m\e[1m"
+               "This is not the command you are looking for.\n"
+               "You probably want to run 'ninja tests' instead "
+               "(note the 's' at the end).\n"
+               "\e[0m");
+       return 1;
+}
index 756afea8235549f0642d6144e04d1f6c2238e4c1..1eb2ec3a8061f7cd509b3b16a232a6498a942538 100644 (file)
@@ -23,7 +23,7 @@ install-exec-hook:
                mknod $(DESTDIR)/dev/fuse -m 0666 c 10 229 || true; \
        fi
 
-EXTRA_DIST = udev.rules init_script
+EXTRA_DIST = udev.rules init_script meson.build install_helper.sh
 
 MOUNT_FUSE_PATH = @MOUNT_FUSE_PATH@
 UDEV_RULES_PATH = @UDEV_RULES_PATH@
diff --git a/util/install_helper.sh b/util/install_helper.sh
new file mode 100755 (executable)
index 0000000..fe65c1c
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Don't call this script. It is used internally by the Meson
+# build system. Thank you for your cooperation.
+#
+
+set -e
+
+sysconfdir="$1"
+bindir="$2"
+prefix="${MESON_INSTALL_DESTDIR_PREFIX}"
+
+chown root:root "${prefix}/${bindir}/fusermount3"
+chmod u+s "${prefix}/${bindir}/fusermount3"
+
+if test ! -e "${DESTDIR}/dev/fuse"; then
+    mkdir -p "${DESTDIR}/dev"
+    mknod "${DESTDIR}/dev/fuse" -m 0666 c 10 229
+fi
+
+install -D -m 644 "${MESON_SOURCE_ROOT}/util/udev.rules" \
+        "${prefix}/lib/udev/rules.d/99-fuse3.rules"
+
+install -D -m 755 "${MESON_SOURCE_ROOT}/util/init_script" \
+        "${prefix}/${sysconfdir}/init.d/fuse3"
+
+if test -x /usr/sbin/update-rc.d; then
+    # May fail for a DESTDIR installation
+    /usr/sbin/update-rc.d fuse3 start 34 S . start 41 0 6 . || /bin/true
+fi
+
+
diff --git a/util/meson.build b/util/meson.build
new file mode 100644 (file)
index 0000000..43c4973
--- /dev/null
@@ -0,0 +1,27 @@
+# Attention, emacs, please use -*- mode: python -*-
+# (even though this isn't actually Python code)
+
+# we re-use mount_util.c from the library, but do want to keep ourself
+# as stand-alone as possible. in order to make an out-of-source build
+# possible, we "generate" the file from its original location by
+# copying it over.
+mount_util_c = custom_target('mount_util',
+  input : '../lib/mount_util.c',
+  output : 'mount_util.c',
+  command : ['cp', '-a', '@INPUT@', '@OUTPUT@'],
+)
+
+executable('fusermount3', ['fusermount.c', mount_util_c],
+           include_directories: include_dirs,
+           install: true,
+           install_dir: get_option('bindir'))
+
+executable('mount.fuse3', ['mount.fuse.c'], 
+           include_directories: include_dirs,
+           install: true,
+           install_dir: get_option('sbindir'))
+
+meson.add_install_script('install_helper.sh', get_option('sysconfdir'),
+                         get_option('bindir'), get_option('libdir'))
+
+