Merge tag 'fuse-3.10.5' into HEAD

Tagged release.

Test: MediaProviderTests
Bug: 216129364
Signed-off-by: Alessio Balsini <balsini@google.com>
Change-Id: I1fcb0684f88e6160943f0d0d3ab797be13695e61
diff --git a/.ackrc b/.ackrc
deleted file mode 100644
index c8f5d0a..0000000
--- a/.ackrc
+++ /dev/null
@@ -1,2 +0,0 @@
---ignore-dir=build
---ignore-dir=doc/html
diff --git a/.cirrus.yml b/.cirrus.yml
deleted file mode 100644
index e1fa3ee..0000000
--- a/.cirrus.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-# Build libfuse on FreeBSD, but don't run the tests.
-# More work is required to make the tests work.
-task:
-  name: FreeBSD
-  freebsd_instance:
-    image: freebsd-12-1-release-amd64
-  install_script: ASSUME_ALWAYS_YES=yes pkg bootstrap -f; pkg install -y meson ninja
-  script:
-    - mkdir build
-    - cd build
-    - meson ..
-    - ninja
diff --git a/.dir-locals.el b/.dir-locals.el
deleted file mode 100644
index 628f512..0000000
--- a/.dir-locals.el
+++ /dev/null
@@ -1,47 +0,0 @@
-((python-mode . ((indent-tabs-mode . nil)))
- (autoconf-mode . ((indent-tabs-mode . t)))
- (c++-mode . ((c-file-style . "k&r")
-              (indent-tabs-mode . nil)
-              (c-basic-offset . 4)
-              (c-file-offsets .
-                              ((block-close . 0)
-                               (brace-list-close . 0)
-                               (brace-list-entry . 0)
-                               (brace-list-intro . +)
-                               (case-label . 0)
-                               (class-close . 0)
-                               (defun-block-intro . +)
-                               (defun-close . 0)
-                               (defun-open . 0)
-                               (else-clause . 0)
-                               (inclass . +)
-                               (label . 0)
-                               (statement . 0)
-                               (statement-block-intro . +)
-                               (statement-case-intro . +)
-                               (statement-cont . +)
-                               (substatement . +)
-                               (topmost-intro . 0)))))
- (c-mode . ((c-file-style . "stroustrup")
-	    (indent-tabs-mode . t)
-	    (tab-width . 8)
-	    (c-basic-offset . 8)
-	    (c-file-offsets .
-			    ((block-close . 0)
-			     (brace-list-close . 0)
-			     (brace-list-entry . 0)
-			     (brace-list-intro . +)
-			     (case-label . 0)
-			     (class-close . 0)
-			     (defun-block-intro . +)
-			     (defun-close . 0)
-			     (defun-open . 0)
-			     (else-clause . 0)
-			     (inclass . +)
-			     (label . 0)
-			     (statement . 0)
-			     (statement-block-intro . +)
-			     (statement-case-intro . +)
-			     (statement-cont . +)
-			     (substatement . +)
-			     (topmost-intro . 0))))))
diff --git a/.github/ISSUE_TEMPLATE/issue-report.md b/.github/ISSUE_TEMPLATE/issue-report.md
deleted file mode 100644
index 277537e..0000000
--- a/.github/ISSUE_TEMPLATE/issue-report.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-name: Issue report
-about: Create a report to help us improve
-title: ''
-labels: ''
-assignees: ''
-
----
-
-PLEASE READ BEFORE REPORTING AN ISSUE
-
-libfuse does not have any active, regular contributors or developers.  The current maintainer continues to apply pull requests and tries to make regular releases, but unfortunately has no capacity to do any development beyond addressing high-impact issues. When reporting bugs, please understand that unless you are including a pull request or are reporting a critical issue, you will probably not get a response.
-
-To prevent the issue tracker from being flooded with issues that no-one is intending to work on, and to give more visibilty to critical issues that users should be aware of and that most urgently need attention, I will also close most bug reports once they've been inactive for a while.
-
-Please note that this isn't meant to imply that you haven't found a bug - you most likely have and I'm grateful that you took the time to report it. Unfortunately, libfuse is a purely volunteer driven project, and at the moment there simply aren't any volunteers.
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index bcee2f2..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-sudo: required
-dist: bionic
-
-before_install:
-    - curl https://spot.fedorapeople.org/spotfoss-ppa.key | sudo apt-key add -
-    - wget https://launchpad.net/~spotfoss/+archive/ubuntu/ppa/+files/valgrind_3.16.1-2_amd64.deb
-    - sudo apt install ./valgrind_3.16.1-2_amd64.deb
-language:
-    - c
-    - c++
-addons:
-  apt:
-    sources:
-    - sourceline: 'ppa:ubuntu-toolchain-r/test'
-    packages:
-    - doxygen
-    - clang
-    - libstdc++-7-dev 
-    - libstdc++-10-dev
-    - gcc
-    - gcc-7
-    - gcc-10
-    - python3-pip
-    - python3-setuptools
-    - ninja-build
-install: test/travis-install.sh
-script: test/travis-build.sh
-
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..c862300
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,95 @@
+package {
+    default_applicable_licenses: ["external_libfuse_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+    name: "external_libfuse_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-BSD",
+        "SPDX-license-identifier-GPL",
+        "SPDX-license-identifier-GPL-2.0",
+        "SPDX-license-identifier-LGPL",
+        "SPDX-license-identifier-LGPL-2.0",
+        "SPDX-license-identifier-LGPL-2.1",
+        "SPDX-license-identifier-LGPL-3.0",
+    ],
+    license_text: [
+        "LGPL2.txt",
+        "LICENSE",
+    ],
+}
+
+cc_defaults {
+    name: "libfuse_default_flags",
+    local_include_dirs: ["include/", "lib/"],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-DFUSERMOUNT_DIR=\"/system/bin\"",
+        "-DFUSE_USE_VERSION=35",
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wno-sign-compare",
+        "-Wno-incompatible-pointer-types",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+    ],
+
+    clang: true,
+    sdk_version: "current",
+    min_sdk_version: "30",
+
+    ldflags: [
+    ],
+}
+
+cc_library {
+    name: "libfuse",
+    defaults: [
+        "libfuse_default_flags",
+    ],
+
+    export_include_dirs: ["include", "lib"],
+
+    version_script: "lib/fuse_versionscript",
+
+    vendor_available: true,
+
+    srcs: [
+        "lib/buffer.c",
+        "lib/cuse_lowlevel.c",
+        "lib/fuse.c",
+        "lib/fuse_log.c",
+        "lib/fuse_loop.c",
+        "lib/fuse_loop_mt.c",
+        "lib/fuse_lowlevel.c",
+        "lib/fuse_opt.c",
+        "lib/fuse_signals.c",
+        "lib/helper.c",
+        "lib/modules/subdir.c",
+        "lib/modules/iconv.c",
+        "lib/mount.c",
+        "lib/mount_util.c",
+    ],
+}
diff --git a/Android.patch b/Android.patch
new file mode 100644
index 0000000..fb792cc
--- /dev/null
+++ b/Android.patch
@@ -0,0 +1,112 @@
+diff --git a/Android.patch b/Android.patch
+new file mode 100644
+index 0000000..e69de29
+diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
+index 2971d29..8a45f42 100644
+--- a/include/fuse_kernel.h
++++ b/include/fuse_kernel.h
+@@ -425,6 +425,9 @@ enum fuse_opcode {
+ 
+ 	/* CUSE specific operations */
+ 	CUSE_INIT		= 4096,
++
++        /* Android specific operations */
++        FUSE_CANONICAL_PATH     = 2016,
+ };
+ 
+ enum fuse_notify_code {
+diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
+index 18c6363..e81c282 100644
+--- a/include/fuse_lowlevel.h
++++ b/include/fuse_lowlevel.h
+@@ -317,6 +317,18 @@ struct fuse_lowlevel_ops {
+ 	 */
+ 	void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+ 
++        /**
++	 * Return canonical path for inotify
++	 *
++	 * Valid replies:
++	 *   fuse_reply_canonical_path
++	 *   fuse_reply_err
++	 *
++	 * @param req request handle
++	 * @param ino the inode number
++	 */
++	void (*canonical_path) (fuse_req_t req, fuse_ino_t ino);
++
+ 	/**
+ 	 * Create file node
+ 	 *
+@@ -1337,6 +1349,18 @@ int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
+  */
+ int fuse_reply_readlink(fuse_req_t req, const char *link);
+ 
++/**
++ * Reply with the canonical path for inotify
++ *
++ * Possible requests:
++ *   canonical_path
++ *
++ * @param req request handle
++ * @param path to canonicalize
++ * @return zero for success, -errno for failure to send reply
++ */
++int fuse_reply_canonical_path(fuse_req_t req, const char *path);
++
+ /**
+  * Reply with open parameters
+  *
+diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
+index f2d7038..334b497 100644
+--- a/lib/fuse_lowlevel.c
++++ b/lib/fuse_lowlevel.c
+@@ -450,6 +450,11 @@ int fuse_reply_readlink(fuse_req_t req, const char *linkname)
+ 	return send_reply_ok(req, linkname, strlen(linkname));
+ }
+ 
++int fuse_reply_canonical_path(fuse_req_t req, const char *path)
++{
++	return send_reply_ok(req, path, strlen(path));
++}
++
+ int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
+ {
+ 	struct fuse_open_out arg;
+@@ -1202,6 +1207,16 @@ static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+ 		fuse_reply_err(req, ENOSYS);
+ }
+ 
++static void do_canonical_path(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
++{
++	(void) inarg;
++
++	if (req->se->op.canonical_path)
++		req->se->op.canonical_path(req, nodeid);
++	else
++		fuse_reply_err(req, ENOSYS);
++}
++
+ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+ {
+ 	struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
+@@ -2456,6 +2471,7 @@ static struct {
+ 	[FUSE_GETATTR]	   = { do_getattr,     "GETATTR"     },
+ 	[FUSE_SETATTR]	   = { do_setattr,     "SETATTR"     },
+ 	[FUSE_READLINK]	   = { do_readlink,    "READLINK"    },
++        [FUSE_CANONICAL_PATH] = { do_canonical_path, "CANONICAL_PATH" },
+ 	[FUSE_SYMLINK]	   = { do_symlink,     "SYMLINK"     },
+ 	[FUSE_MKNOD]	   = { do_mknod,       "MKNOD"	     },
+ 	[FUSE_MKDIR]	   = { do_mkdir,       "MKDIR"	     },
+diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
+index d18ba29..4c075a3 100644
+--- a/lib/fuse_versionscript
++++ b/lib/fuse_versionscript
+@@ -163,6 +163,7 @@ FUSE_3.7 {
+ 	global:
+ 		fuse_set_log_func;
+ 		fuse_log;
++		fuse_reply_canonical_path;
+ } FUSE_3.3;
+ 
+ # Local Variables:
diff --git a/ChangeLog.rst b/ChangeLog.rst
deleted file mode 100644
index 521c163..0000000
--- a/ChangeLog.rst
+++ /dev/null
@@ -1,705 +0,0 @@
-libfuse 3.10.5 (2021-09-06)
-===========================
-
-* Various improvements to make unit tests more robust.
-  
-
-libfuse 3.10.4 (2021-06-09)
-===========================
-
-* Building of unit tests is now optional.
-* Fixed a test failure when running tests under XFS.
-* Fixed memory leaks in examples.
-* Minor documentation fixes.  
-
-libfuse 3.10.3 (2021-04-12)
-===========================
-
-* Fix returning d_ino and d_type from readdir(3) in non-plus mode
-  
-libfuse 3.10.2 (2021-02-05)
-===========================
-
-* Allow "nonempty" as a mount option, for backwards compatibility with fusermount 2. The
-  option has no effect since mounting over non-empty directories is allowed by default.
-* Fix returning inode numbers from readdir() in offset==0 mode.
-* FUSE filesystems can now be mounted underneath EXFAT mountpoints.
-* Various minor bugfixes.  
-
-libfuse 3.10.1 (2020-12-07)
-===========================
-
-* Various minor bugfixes.
-
-libfuse 3.10.0 (2020-10-09)
-===========================
-
-* Add FUSE_CAP_CACHE_SYMLINKS: allow caching symlinks in kernel page cache.
-* Various minor bugfixes and improvements.  
-
-libfuse 3.9.4 (2020-08-09)
-==========================
-
-This was an "accidental" release, it is equivalent to 3.9.3.
-
-libfuse 3.9.3 (2020-08-09)
-==========================
-
-* Fixed compilation under OS X and µClibc.
-* Minor bugfixes and doc updates.
-
-libfuse 3.9.2 (2020-06-12)
-==========================
-
-* Remove obsolete workarounds in examples.
-* Do not require C++ compiler for building.
-* Minor bugfixes.
-
-libfuse 3.9.1 (2020-03-19)
-===========================
-
-* Fixed memory leak in fuse_session_new().
-* Fixed an issue with the linker version script.
-* Make ioctl prototype conditional on FUSE_USE_VERSION.  Define FUSE_USE_VERSION < 35 to
-  get old ioctl prototype with int commands; define FUSE_USE_VERSION >= 35 to get new
-  ioctl prototype with unsigned int commands.
-* Various small bugfixes.
-
-libfuse 3.9.0 (2019-12-14)
-==========================
-
-* Added support for FUSE_EXPLICIT_INVAL_DATA to enable
-  only invalidate cached pages on explicit request.
-
-libfuse 3.8.0 (2019-11-03)
-==========================
-
-* Added support for FUSE_LSEEK operation which can be used to report holes
-  in sparse files.
-
-libfuse 3.7.0 (2019-09-27)
-==========================
-
-* Added UFSD to whitelist (so users can now mount FUSE filesystems
-  on mountpoints within UFSD filesystems).
-* Added custom log message handler function support so that libfuse
-  applications can direct messages to syslog(3) or other logging systems.
-  stderr remains the default.  See `fuse_log.h` for the new API.
-
-libfuse 3.6.2 (2019-07-09)
-==========================
-
-* The init script is now installed to /etc/ rather than /usr/local/etc
-  by default.
-
-libfuse 3.6.1 (2019-06-13)
-==========================
-
-* Fixed version number (release 3.6.0 was shipped with a declared
-  version of 3.0.0).
-
-libfuse 3.6.0 (2019-06-13)
-==========================
-
-* Added a new example (passthrough_hp). The functionality is similar
-  to passthrough_ll, but the implementation focuses on performance and
-  correctness rather than simplicity.
-* Added support for fuse kernel feature `max_pages` which allows to increase
-  the maximum number of pages that can be used per request. This feature was
-  introduced in kernel 4.20. `max_pages` is set based on the value in
-  `max_write`. By default `max_write` will be 1MiB now for kernels that support
-  `max_pages`. If you want smaller buffers or writes you have to set
-  `max_write` manually.
-
-libfuse 3.5.0 (2019-04-16)
-==========================
-
-* Changed ioctl commands to "unsigned int" in order to support commands
-  which do not fit into a signed int. Commands issued by applications
-  are still truncated to 32 bits.
-* Added SMB2 to whitelist (so users can now mount FUSE filesystems
-  on mountpoints within SMB 2.0 filesystems).
-* Added a new `cache_readdir` flag to `fuse_file_info` to enable
-  caching of readdir results. Supported by kernels 4.20 and newer.
-* Add support and documentation for FUSE_CAP_NO_OPENDIR_SUPPORT.
-
-libfuse 3.4.2 (2019-03-09)
-==========================
-
-* Fixed a memory leak in `examples/passthrough_ll.c`.
-* Added OpenAFS to whitelist (so users can now mount FUSE filesystems
-  on mountpoints within OpenAFS filesystems).
-* Added HFS+ to whitelist (so users can now mount FUSE filesystems
-  on mountpoints within HFS+ filesystems).
-* Documentation improvements.
-
-libfuse 3.4.1 (2018-12-22)
-==========================
-
-* The `examples/passthrough_ll.c` example filesystem has been
-  significantly extended.
-* Support for `copy_file_range` has been added.
-* Build system updates for non-Linux systems.
-
-libfuse 3.4.0
-=============
-
-* Add `copy_file_range()` to support efficient copying of data from one file to
-  an other.
-
-libfuse 3.3.0 (2018-11-06)
-==========================
-
-* The `auto_unmount` mode now works correctly in combination with
-  autofs.
-
-* The FUSE_CAP_READDIRPLUS_AUTO capability is no longer enabled by
-  default unless the file system defines both a readdir() and a
-  readdirplus() handler.
-
-* The description of the FUSE_CAP_READDIRPLUS_AUTO flag has been
-  improved.
-
-* Allow open `/dev/fuse` file descriptors to be passed via mountpoints of the
-  special format `/dev/fd/%u`. This allows mounting to be handled by the parent
-  so the FUSE filesystem process can run fully unprivileged.
-
-* Add a `drop_privileges` option to mount.fuse3 which causes it to open
-  `/dev/fuse` and mount the file system itself, then run the FUSE file
-  filesystem fully unprivileged and unable to re-acquire privilege via setuid,
-  fscaps, etc.
-
-* Documented under which conditions the `fuse_lowlevel_notify_*`
-  functions may block.
-
-libfuse 3.2.6 (2018-08-31)
-==========================
-
-* The fuse_main() function now returns more fine-grained error codes.
-* FUSE filesystems may now be mounted on mountpoint within
-  bcachefs, aufs and FAT filesystems.
-* libfuse may now be used as a Meson subproject.
-* Fix a few low-impact memory leaks.
-* The `fuse.conf` file is no longer looked for in `/etc`, but in the
-  *sysconfdir* directory (which can be set with `meson configure`). By
-  default, the location is thus `/usr/local/etc/fuse.conf`.
-
-libfuse 3.2.5 (2018-07-24)
-==========================
-
-* SECURITY UPDATE: In previous versions of libfuse it was possible to
-  for unprivileged users to specify the `allow_other` option even when
-  this was forbidden in `/etc/fuse.conf`.  The vulnerability is
-  present only on systems where SELinux is active (including in
-  permissive mode).
-* The fusermount binary has been hardened in several ways to reduce
-  potential attack surface. Most importantly, mountpoints and mount
-  options must now match a hard-coded whitelist. It is expected that
-  this whitelist covers all regular use-cases.
-* Added a test of `seekdir` to test_syscalls.
-* Fixed `readdir` bug when non-zero offsets are given to filler and the
-  filesystem client, after reading a whole directory, re-reads it from a
-  non-zero offset e. g. by calling `seekdir` followed by `readdir`.
-
-libfuse 3.2.4 (2018-07-11)
-==========================
-
-* Fixed `rename` deadlock on FreeBSD.
-
-libfuse 3.2.3 (2018-05-11)
-==========================
-
-* Fixed a number of compiler warnings.  
-
-libfuse 3.2.2 (2018-03-31)
-==========================
-
-* Added example fuse.conf file.
-* Added "support" for -o nofail mount option (the option is accepted
-  and ignored).
-* Various small bugfixes.  
-
-libfuse 3.2.1 (2017-11-14)
-==========================
-
-* Various small bugfixes.
-
-libfuse 3.2.0 (2017-09-12)
-==========================
-
-* Support for building with autotools has been dropped.
-
-* Added new `fuse_invalidate_path()` routine for cache invalidation
-  from the high-level FUSE API, along with an example and tests.
-
-* There's a new `printcap` example that can be used to determine the
-  capabilities of the running kernel.
-
-* `fuse_loop_mt()` now returns the minus the actual errno if there was
-  an error (instead of just -1).
-
-* `fuse_loop()` no longer returns a positive value if the filesystem
-  loop was terminated without errors or signals.
-
-* Improved documentation of `fuse_lowlevel_notify_*` functions.
-
-* `fuse_lowlevel_notify_inval_inode()` and
-  `fuse_lowlevel_notify_inval_entry()` now return -ENOSYS instead of
-  an undefined error if the function is not supported by the kernel.
-
-* Documented the special meaning of the *zero* offset for the
-  fuse_fill_dir_t function.
-
-* The `passthrough_fh` example now works under FreeBSD.
-
-* libfuse can now be build without libiconv.
-
-* Fixed support for `FUSE_CAP_POSIX_ACL`: setting this capability
-  flag had no effect in the previous versions of libfuse 3.x;
-  now ACLs should actually work.
-
-* Fixed a number of compilation problems under FreeBSD.
-
-* Fixed installation directory for udev rules.
-
-* Fixed compilation with LTO.
-
-libfuse 3.1.1 (2017-08-06)
-==========================
-
-* Documentation: clarified how filesystems are supposed to process
-  open() and create() flags (see include/fuse_lowlevel.h).
-
-* Fixed a compilation problem of the passthrough_ll example on
-  32 bit systems (wrong check and wrong error message).
-
-* pkg-config is now used to determine the proper directory for
-  udev rules.
-
-* Fixed a symbol versioning problem that resulted in very strange
-  failures (segfaults, unexpected behavior) in different situations.
-
-* Fixed a test failure when /tmp is on btrfs.
-
-* The maximum number of idle worker threads used by `fuse_loop_mt()`
-  is now configurable.
-
-* `fuse_loop_mt()` and `fuse_session_loop_mt()` now take a
-  `struct fuse_loop_config` parameter that supersedes the *clone_fd*
-  parameter.
-
-* Incorporated several patches from the FreeBSD port. libfuse should
-  now compile under FreeBSD without the need for patches.
-
-* The passthrough_ll example now supports writeback caching.
-
-libfuse 3.1.0 (2017-07-08)
-==========================
-
-* Added new `fuse_lib_help()` function. File-systems that previously
-  passed a ``--help`` option to `fuse_new()` must now process the
-  ``--help`` option internally and call `fuse_lib_help()` to print the
-  help for generic FUSE options.
-* Fixed description of the `fuse_conn_info->time_gran`. The default
-  value of zero actually corresponds to full nanosecond resolution,
-  not one second resolution.
-* The init script is now installed into the right location
-  (``$DESTDIR/etc/init.d`` rather than ``$prefix/$sysconfdir/init.d``)
-* The `example/passthrough_ll` filesystem now supports creating
-  and writing to files.
-* `fuse_main()` / `fuse_remove_signal_handlers()`: do not reset
-  `SIGPIPE` handler to `SIG_DFL` if it was not set by us.
-* Documented the `RENAME_EXCHANGE` and `RENAME_NOREPLACE` flags that
-  may be passed to the `rename` handler of both the high- and
-  low-level API. Filesystem authors are strongly encouraged to check
-  that these flags are handled correctly.
-
-libfuse 3.0.2 (2017-05-24)
-==========================
-
-* Option parsing for the high-level API now works correctly
-  (previously, default values would override specified values).
-* Tests should now build (and run) under FreeBSD.
-* Improved documentation of `struct fuse_context`
-* Internal: calculate request buffer size from page size and kernel
-  page limit instead of using hardcoded 128 kB limit.
-
-
-libfuse 3.0.1 (2017-04-10)
-==========================
-
-* Re-introduced *examples/null.c*.
-* Added experimental support for building with Meson.
-* Document that `-o auto_unmount` implies `-o nodev,nosuid`.
-* Document that the *use_ino* option of the high-level interface does
-  not affect the inode that libfuse and the kernel use internally.
-* Fixed test cases for passthrough* examples (they weren't actually
-  testing the examples).
-* Fixed several bugs in the passthrough* examples.
-
-libfuse 3.0.0 (2016-12-08)
-==========================
-
-* NOTE TO PACKAGERS:
-
-  libfuse 3 is designed to be co-installable with libfuse 2. However,
-  some files will be installed by both libfuse 2 and libfuse 3
-  (e.g. /etc/fuse.conf, the udev and init scripts, and the
-  mount.fuse(8) manpage). These files should be taken from
-  libfuse 3. The format/content is guaranteed to remain backwards
-  compatible with libfuse 2.
-
-  We recommend to ship libfuse2 and libfuse3 in three separate
-  packages: a libfuse-common package that contains files shared by
-  libfuse 2+3 (taken from the libfuse3 tarball), and libfuse2 and
-  libfuse3 packages that contain the shared library and helper
-  programs for the respective version.
-
-* Fixed test errors when running tests as root.
-
-* Made check for util-linux version more robust.
-
-* Added documentation for all fuse capability flags (`FUSE_CAP_*`) and
-  `struct fuse_conn_info` fields.
-
-* fuse_loop(), fuse_loop_mt(), fuse_session_loop() and
-  fuse_session_loop_mt() now return more detailed error codes instead
-  of just -1. See the documentation of fuse_session_loop() for details.
-
-* The FUSE main loop is now aborted if the file-system requests
-  capabilities that are not supported by the kernel. In this case, the
-  session loop is exited with a return code of ``-EPROTO``.
-
-* Most file-system capabilities that were opt-in in libfuse2 are now
-  enabled by default. Filesystem developers are encouraged to review
-  the documentation of the FUSE_CAP_* features to ensure that their
-  filesystem is compatible with the new semantics. As before, a
-  particular capability can still be disabled by unsetting the
-  corresponding bit of `fuse_conn_info.wants` in the init() handler.
-
-* Added FUSE_CAP_PARALLEL_DIROPS and FUSE_CAP_POSIX_ACL,
-  FUSE_HANDLE_KILLPRIV feature flags.
-
-* FUSE filesystems are now responsible for unsetting the setuid/setgid
-  flags when a file is written, truncated, or its owner
-  changed. Previously, this was handled by the kernel but subject to
-  race conditions.
-
-* The fusermount and mount.fuse binaries have been renamed to
-  fusermount3 and mount.fuse3 to allow co-installation of libfuse 2.x
-  and 3.x
-
-* Added a `max_read` field to `struct fuse_conn_info`. For the time
-  being, the maximum size of read requests has to be specified both
-  there *and* passed to fuse_session_new() using the ``-o
-  max_read=<n>`` mount option. At some point in the future, specifying
-  the mount option will no longer be necessary.
-
-* Documentation: clarified that the fuse_argv structure that is passed
-  to `fuse_new()` and `fuse_lowlevel_new()` must always contain at
-  least one element.
-
-* The high-level init() handler now receives an additional struct
-  fuse_config pointer that can be used to adjust high-level API
-  specific configuration options.
-
-* The `nopath_flag` field of struct fuse_operations has been
-  removed. Instead, a new `nullpath_ok` flag can now be set
-  in struct fuse_config.
-
-* File systems that use the low-level API and support lookup requests
-  for '.' and '..' should continue make sure to set the
-  FUSE_CAP_EXPORT_SUPPORT bit in fuse_conn_info->want.
-
-  (This has actually always been the case, but was not very obvious
-  from the documentation).
-
-* The help text generated by fuse_lowlevel_help(), fuse_new() (and
-  indirectly fuse_main()) no longer includes options that are unlikely
-  to be of interest to end-users. The full list of accepted options is
-  now included in the respective function's documentation (located in
-  the fuse.h/fuse_lowlevel.h and doc/html).
-
-* The ``-o nopath`` option has been dropped - it never actually did
-  anything (since it is unconditionally overwritten with the value of
-  the `nopath` flag in `struct fuse_operations).
-
-* The ``-o large_read`` mount option has been dropped. Hopefully no
-  one uses a Linux 2.4 kernel anymore.
-
-* The `-o nonempty` mount point has been removed, mounting over
-  non-empty directories is now always allowed. This brings the
-  behavior of FUSE file systems in-line with the behavior of the
-  regular `mount` command.
-
-  File systems that do not want to allow mounting to non-empty
-  directories should perform this check themselves before handing
-  control to libfuse.
-
-* The chmod, chown, truncate, utimens and getattr handlers of the
-  high-level API now all receive an additional struct fuse_file_info
-  pointer (which, however, may be NULL even if the file is currently
-  open).
-
-  The fgetattr and ftruncate handlers have become obsolete and have
-  been removed.
-
-* The `fuse_session_new` function no longer accepts the ``-o
-  clone_fd`` option. Instead, this has become a parameter of the
-  `fuse_session_loop_mt` and ``fuse_loop_mt` functions.
-
-* For low-level file systems that implement the `write_buf` handler,
-  the `splice_read` option is now enabled by default. As usual, this
-  can be changed in the file system's `init` handler.
-
-* The treatment of low-level options has been made more consistent:
-
-  Options that can be set in the init() handler (via the
-  fuse_conn_info parameter) can now be set only here,
-  i.e. fuse_session_new() no longer accepts arguments that change the
-  fuse_conn_info object before or after the call do init(). As a side
-  effect, this removes the ambiguity where some options can be
-  overwritten by init(), while others overwrite the choices made by
-  init().
-
-  For file systems that wish to offer command line options for these
-  settings, the new fuse_parse_conn_info_opts() and
-  fuse_apply_conn_info_opts() functions are available.
-
-  Consequently, the fuse_lowlevel_help() method has been dropped.
-
-* The `async_read` field in `struct fuse_conn_info` has been
-  removed. To determine if the kernel supports asynchronous reads,
-  file systems should check the `FUSE_CAP_ASYNC_READ` bit of the
-  `capable` field. To enable/disable asynchronous reads, file systems
-  should set the flag in the `wanted` field.
-
-* The `fuse_parse_cmdline` function no longer prints out help when the
-  ``--verbose`` or ``--help`` flags are given. This needs to be done
-  by the file system (e.g. using the `fuse_cmdline_help()` and
-  `fuse_lowlevel_help()` functions).
-
-* Added ``example/cuse_client.c`` to test ``example/cuse.c``.
-
-* Removed ``example/null.c``. This has not been working for a while
-  for unknown reasons -- maybe because it tries to treat the
-  mountpoint as a file rather than a directory?
-
-* There are several new examples that demonstrate the use of
-  the ``fuse_lowlevel_notify_*`` functions:
-
-  - ``example/notify_store_retrieve.c``
-  - ``example/notify_inval_inode.c``
-  - ``example/notify_inval_entry.c``
-
-* The ``-o big_writes`` mount option has been removed. It is now
-  always active. File systems that want to limit the size of write
-  requests should use the ``-o max_write=<N>`` option instead.
-
-* The `fuse_lowlevel_new` function has been renamed to
-  `fuse_session_new` and no longer interprets the --version or --help
-  options. To print help or version information, use the new
-  `fuse_lowlevel_help` and `fuse_lowlevel_version` functions.
-
-* The ``allow_other`` and ``allow_root`` mount options (accepted by
-  `fuse_session_new()`) may now be specified together. In this case,
-  ``allow_root`` takes precedence.
-
-* There are new `fuse_session_unmount` and `fuse_session_mount`
-  functions that should be used in the low-level API. The `fuse_mount`
-  and `fuse_unmount` functions should be used with the high-level API
-  only.
-
-* Neither `fuse_mount` nor `fuse_session_mount` take struct fuse_opts
-  parameters anymore. Mount options are parsed by `fuse_new` (for the
-  high-level API) and `fuse_session_new` (for the low-level API)
-  instead. To print help or version information, use the new
-  `fuse_mount_help` and `fuse_mount_version` functions.
-
-* The ``fuse_lowlevel_notify_*`` functions now all take a `struct
-  fuse_session` parameter instead of a `struct fuse_chan`.
-
-* The channel interface (``fuse_chan_*`` functions) has been made
-  private. As a result, the typical initialization sequence of a
-  low-level file system has changed from ::
-
-        ch = fuse_mount(mountpoint, &args);
-        se = fuse_lowlevel_new(&args, &lo_oper, sizeof(lo_oper), &lo);
-        fuse_set_signal_handlers(se);
-        fuse_session_add_chan(se, ch);
-        fuse_daemonize(fg);
-        if (mt)
-            fuse_session_loop_mt(se);
-        else
-            fuse_session_loop(se);
-        fuse_remove_signal_handlers(se);
-        fuse_session_remove_chan(ch);
-        fuse_session_destroy(se);
-        fuse_unmount(mountpoint, ch);
-
-  to ::
-
-        se = fuse_session_new(&args, &ll_ops, sizeof(ll_ops), NULL);
-        fuse_set_signal_handlers(se);
-        fuse_session_mount(se, mountpoint);
-        fuse_daemonize(fg);
-        if (mt)
-            fuse_session_loop_mt(se);
-        else
-            fuse_session_loop(se);
-        fuse_remove_signal_handlers(se);
-        fuse_session_unmount(se);
-        fuse_lowlevel_destroy(se);
-
-  The typical high-level setup has changed from ::
-
-        ch = fuse_mount(*mountpoint, &args);
-        fuse = fuse_new(ch, &args, op, op_size, user_data);
-        se = fuse_get_session(fuse);
-        fuse_set_signal_handlers(se);
-        fuse_daemonize(fg);
-        if (mt)
-            fuse_loop_mt(fuse);
-        else
-            fuse_loop(fuse);
-        fuse_remove_signal_handlers(se);
-        fuse_unmount(mountpoint, ch);
-        fuse_destroy(fuse);
-
-  to ::
-
-        fuse = fuse_new(&args, op, op_size, user_data);
-        se = fuse_get_session(fuse);
-        fuse_set_signal_handlers(se);
-        fuse_mount(fuse, mountpoint);
-        fuse_daemonize(fg);
-         if (mt)
-            fuse_loop_mt(fuse);
-        else
-            fuse_loop(fuse);
-        fuse_remove_signal_handlers(se);
-        fuse_unmount(fuse);
-        fuse_destroy(fuse);
-
-  File systems that use `fuse_main` are not affected by this change.
-
-  For integration with custom event loops, the new `fuse_session_fd`
-  function provides the file descriptor that's used for communication
-  with the kernel.
-
-* Added *clone_fd* option.  This creates a separate device file
-  descriptor for each processing thread, which might improve
-  performance.
-
-* Added *writeback_cache* option. With kernel 3.14 and newer this
-  enables write-back caching which can significantly improve
-  performance.
-
-* Added *async_dio* option. With kernel 3.13 and newer, this allows
-  direct I/O to be done asynchronously.
-
-* The (high- and low-level) `rename` handlers now takes a *flags*
-  parameter (with values corresponding to the *renameat2* system call
-  introduced in Linux 3.15).
-
-* The "ulockmgr_server" has been dropped.
-
-* There is a new (low-level) `readdirplus` handler, with a
-  corresponding example in ``examples/fuse_lo-plus.c`` and a new
-  `fuse_add_direntry_plus` API function.
-
-* The (high-level) `readdir` handler now takes a *flags* argument.
-
-* The (high-level) `filler` function passed to `readdir` now takes an
-  additional *flags* argument.
-
-* The (high-level) `getdir` handler has been dropped.
-
-* The *flag_nullpath_ok* and *flag_utime_omit_ok* flags have been
-  dropped.
-
-* The (high-level) *utime* handler has been dropped.
-
-* The `fuse_invalidate` function has been removed.
-
-* The `fuse_is_lib_option` function has been removed.
-
-* The *fh_old* member of `struct fuse_file_info` has been dropped.
-
-* The type of the *writepage* member of `struct fuse_file_info` was
-  changed from *int* to *unsigned int*.
-
-* The `struct fuse_file_info` gained a new *poll_events* member.
-
-* There is a new `fuse_pkgversion` function.
-
-* The *fuse_off_t* and *fuse_ino_t* changed from *unsigned long* to
-  *uint64_t*, i.e. they are now 64 bits also on 32-bit systems.
-
-* The type of the *generation* member of `struct fuse_entry_param*
-  changed from *unsigned* to *uint64_t*.
-
-* The (low-level) `setattr` handler gained a *FUSE_SET_ATTR_CTIME* bit
-  *for its *to_set* parameter.
-
-* The `struct fuse_session_ops` data structure has been dropped.
-
-* The documentation has been clarified and improved in many places.
-
-
-FUSE 2.9.7 (2016-06-20)
-=======================
-
-* Added SELinux support.
-* Fixed race-condition when session is terminated right after starting
-  a FUSE file system.
-
-FUSE 2.9.6 (2016-04-23)
-=======================
-
-* Tarball now includes documentation.
-* Shared-object version has now been bumped correctly.
-
-FUSE 2.9.5 (2016-01-14)
-=======================
-
-* New maintainer: Nikolaus Rath <Nikolaus@rath.org>. Many thanks to
-  Miklos Szeredi <miklos@szeredi.hu> for bringing FUSE to where it is
-  now!
-
-* fix warning in mount.c:receive_fd().  Reported by Albert Berger
-
-* fix possible memory leak.  Reported by Jose R. Guzman
-
-FUSE 2.9.4 (2015-05-22)
-=======================
-
-* fix exec environment for mount and umount.  Found by Tavis Ormandy
-  (CVE-2015-3202).
-
-* fix fuse_remove_signal_handlers() to properly restore the default
-  signal handler.  Reported by: Chris Johnson
-
-* highlevel API: fix directory file handle passed to ioctl() method.
-  Reported by Eric Biggers
-
-* libfuse: document deadlock avoidance for fuse_notify_inval_entry()
-  and fuse_notify_delete()
-
-* fusermount, libfuse: send value as unsigned in "user_id=" and
-  "group_id=" options.  Uids/gids larger than 2147483647 would result
-  in EINVAL when mounting the filesystem.  This also needs a fix in
-  the kernel.
-
-* Initialize stat buffer passed to ->getattr() and ->fgetattr() to
-  zero in all cases.  Reported by Daniel Iwan
-
-* libfuse: Add missing includes.  This allows compiling fuse with
-  musl.  Patch by Daniel Thau
-
-
-Older Versions (before 2013-01-01)
-==================================
-
-Please see Git history, e.g. at
-https://github.com/libfuse/libfuse/blob/fuse_2_9_3/ChangeLog.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..78a08d4
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,18 @@
+name: "libfuse"
+description:
+    "The reference implementation of the Linux FUSE (Filesystem in Userspace) "
+    "interface"
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://github.com/libfuse"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/libfuse/libfuse.git"
+  }
+  version: "fuse-3.8.0"
+  last_upgrade_date { year: 2019 month: 11 day: 6 }
+  license_type: RESTRICTED_IF_STATICALLY_LINKED
+}
diff --git a/MODULE_LICENSE_LGPL2 b/MODULE_LICENSE_LGPL2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_LGPL2
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..43e933d
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+LGPL2.txt
\ No newline at end of file
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..5ee6098
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+balsini@google.com
diff --git a/doc/.gitignore b/doc/.gitignore
deleted file mode 100644
index 1b0a5aa..0000000
--- a/doc/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-doxygen_sqlite3.db
-html/
diff --git a/doc/Doxyfile b/doc/Doxyfile
deleted file mode 100644
index 46ea6cd..0000000
--- a/doc/Doxyfile
+++ /dev/null
@@ -1,1235 +0,0 @@
-# Doxyfile 1.5.6
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
-#
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING      = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
-
-PROJECT_NAME           = libfuse
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER         =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY       = doc
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS         = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
-# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
-# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
-# and Ukrainian.
-
-OUTPUT_LANGUAGE        = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC      = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF           = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF       = "The $name class" \
-                         "The $name widget" \
-                         "The $name file" \
-                         is \
-                         provides \
-                         specifies \
-                         contains \
-                         represents \
-                         a \
-                         an \
-                         the
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC    = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB  = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES        = YES
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH        =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH    =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES            = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF      = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF           = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS           = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES  = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE               = 8
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES                =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C  = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA   = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN   = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL   = NO
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT    = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT        = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT            = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT   = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC   = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING            = YES
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT   = NO
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL            = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE        = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC         = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES  = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS  = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
-
-EXTRACT_ANON_NSPACES   = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS     = YES
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES     = YES
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS  = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS      = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS          = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES       = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES       = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES     = YES
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO            = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS       = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS        = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES       = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME     = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST      = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST      = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST       = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS       =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES  = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES        = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES             = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.  This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES        = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER    =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET                  = YES
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS               = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED   = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR      = YES
-
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC       = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT            = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE           =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT                  = .
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING         = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
-
-FILE_PATTERNS          = *.h *.c *.h *.dox
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE              = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-
-EXCLUDE                =
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS       = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS       =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS        =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH           = example
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS       = *.c *.h
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE      = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH             =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER           =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.  Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.  The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
-
-FILTER_PATTERNS        =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES    = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER         = YES
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES         = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS    = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION    = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.  Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS              = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS       = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX     = NO
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX    = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX          =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML          = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT            = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION    = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
-
-HTML_HEADER            =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER            =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET        =
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP      = NO
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-
-GENERATE_DOCSET        = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME        = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID       = org.doxygen.Project
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS  = YES
-
-HTML_EXTRA_FILES = doc/fast17-vangoor.pdf
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE               =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION           =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI           = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING     =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC             = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND             = NO
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX          = NO
-
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE   = 4
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to FRAME, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
-# probably better off using the HTML help feature. Other possible values
-# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list;
-# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
-# disables this behavior completely. For backwards compatibility with previous
-# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
-# respectively.
-
-GENERATE_TREEVIEW      = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH         = 250
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE       = 10
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX         = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF           = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN           = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML           = NO
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF   = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD       = NO
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING   = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION        = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF     = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES        = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH           =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS  =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED             = FUSE_USE_VERSION=35
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
-
-EXPAND_AS_DEFINED      =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
-
-SKIP_FUNCTION_MACROS   = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#   TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#   TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES               =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE       =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS           = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS        = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH              = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
-
-CLASS_DIAGRAMS         = NO
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH            =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS   = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT               = NO
-
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
-
-DOT_FONTPATH           =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH            = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH    = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS           = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK               = NO
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS     = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH          = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH      = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH             = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH           = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY    = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH        = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
-
-DOT_IMAGE_FORMAT       = png
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH               =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS           =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES    = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH    = 1000
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is enabled by default, which results in a transparent
-# background. Warning: Depending on the platform used, enabling this option
-# may lead to badly anti-aliased labels on the edges of a graph (i.e. they
-# become hard to read).
-
-DOT_TRANSPARENT        = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS      = NO
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND        = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP            = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE           = NO
diff --git a/doc/README.NFS b/doc/README.NFS
deleted file mode 100644
index 239dcb2..0000000
--- a/doc/README.NFS
+++ /dev/null
@@ -1,33 +0,0 @@
-NFS exporting is supported in Linux kernels 2.6.27 or later.
-
-You need to add an fsid=NNN option to /etc/exports to make exporting a
-FUSE directory work.
-
-Filesystem support
-------------------
-
-NFS exporting works to some extent on all fuse filesystems, but not
-perfectly.  This is due to the stateless nature of the protocol, the
-server has no way of knowing whether the client is keeping a reference
-to a file or not, and hence that file may be removed from the server's
-cache.  In that case there has to be a way to look up that object
-using the inode number, otherwise an ESTALE error will be returned.
-
-1) low-level interface
-
-Filesystems need to set FUSE_CAP_EXPORT_SUPPORT in conn->wants and
-implement special lookups for the names "." and "..". The former may
-be requested on any inode, including non-directories, while the latter
-is only requested for directories.  Otherwise these special lookups
-should behave identically to ordinary lookups.
-
-2) high-level interface
-
-Because the high-level interface is path based, it is not possible to
-delegate looking up by inode to the filesystem.
-
-To work around this, currently a "noforget" option is provided, which
-makes the library remember nodes forever.  This will make the NFS
-server happy, but also results in an ever growing memory footprint for
-the filesystem.  For this reason if the filesystem is large (or the
-memory is small), then this option is not recommended.
diff --git a/doc/fast17-vangoor.pdf b/doc/fast17-vangoor.pdf
deleted file mode 100644
index cef7237..0000000
--- a/doc/fast17-vangoor.pdf
+++ /dev/null
Binary files differ
diff --git a/doc/fusermount3.1 b/doc/fusermount3.1
deleted file mode 100644
index 1455742..0000000
--- a/doc/fusermount3.1
+++ /dev/null
@@ -1,44 +0,0 @@
-.TH FUSERMOUNT3 1 2011\-10\-23 2.8.6 "Filesystem in Userspace (FUSE)"
-
-.SH NAME
-\fBfusermount3\fR \- mount and unmount FUSE filesystems
-
-.SH SYNOPSIS
-\fBfusermount3\fR [\fIOPTIONS\fR] \fIMOUNTPOINT\fR
-
-.SH DESCRIPTION
-Filesystem in Userspace (FUSE) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. It also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations.
-.PP
-\fBfusermount3\fR is a program to mount and unmount FUSE
-filesystems. It should be called directly only for unmounting FUSE
-file systems. To allow mounting and unmounting by unprivileged users,
-\fBfusermount3\fR needs to be installed set-uid root.
-.SH OPTIONS
-.IP "\-h" 4
-print help.
-.IP "\-V" 4
-print version.
-.IP "-o \fIOPTION\fR[,\fIOPTION\fR...]" 4
-mount options.
-.IP "-u" 4
-unmount.
-.IP "-q" 4
-quiet.
-.IP "-z" 4
-lazy unmount.
-
-.SH SEE ALSO
-\fImount\fR(8),
-\fImount.fuse3\fR(8),
-\fIfuse\fR(4),
-
-.SH HOMEPAGE
-More information about fusermount3 and the FUSE project can be found at <\fIhttp://fuse.sourceforge.net/\fR>.
-
-.SH AUTHORS
-.LP
-FUSE is currently maintained by Nikolaus Rath <Nikolaus@rath.org>
-.LP
-The original author of FUSE is Miklos Szeredi <\fImiklos@szeredi.hu\fR>.
-.LP
-This manual page was originally written by Daniel Baumann <\fIdaniel.baumann@progress\-technologies.net\fR>.
diff --git a/doc/kernel.txt b/doc/kernel.txt
deleted file mode 100644
index 6e7c7a1..0000000
--- a/doc/kernel.txt
+++ /dev/null
@@ -1,380 +0,0 @@
-Definitions
-~~~~~~~~~~~
-
-Userspace filesystem:
-
-  A filesystem in which data and metadata are provided by an ordinary
-  userspace process.  The filesystem can be accessed normally through
-  the kernel interface.
-
-Filesystem daemon:
-
-  The process(es) providing the data and metadata of the filesystem.
-
-Non-privileged mount (or user mount):
-
-  A userspace filesystem mounted by a non-privileged (non-root) user.
-  The filesystem daemon is running with the privileges of the mounting
-  user.  NOTE: this is not the same as mounts allowed with the "user"
-  option in /etc/fstab, which is not discussed here.
-
-Filesystem connection:
-
-  A connection between the filesystem daemon and the kernel.  The
-  connection exists until either the daemon dies, or the filesystem is
-  umounted.  Note that detaching (or lazy umounting) the filesystem
-  does _not_ break the connection, in this case it will exist until
-  the last reference to the filesystem is released.
-
-Mount owner:
-
-  The user who does the mounting.
-
-User:
-
-  The user who is performing filesystem operations.
-
-What is FUSE?
-~~~~~~~~~~~~~
-
-FUSE is a userspace filesystem framework.  It consists of a kernel
-module (fuse.ko), a userspace library (libfuse.*) and a mount utility
-(fusermount3).
-
-One of the most important features of FUSE is allowing secure,
-non-privileged mounts.  This opens up new possibilities for the use of
-filesystems.  A good example is sshfs: a secure network filesystem
-using the sftp protocol.
-
-The userspace library and utilities are available from the FUSE
-homepage:
-
-  https://github.com/libfuse/libfuse/
-
-Filesystem type
-~~~~~~~~~~~~~~~
-
-The filesystem type given to mount(2) can be one of the following:
-
-'fuse'
-
-  This is the usual way to mount a FUSE filesystem.  The first
-  argument of the mount system call may contain an arbitrary string,
-  which is not interpreted by the kernel.
-
-'fuseblk'
-
-  The filesystem is block device based.  The first argument of the
-  mount system call is interpreted as the name of the device.
-
-Mount options
-~~~~~~~~~~~~~
-
-See mount.fuse3(8).
-
-Control filesystem
-~~~~~~~~~~~~~~~~~~
-
-There's a control filesystem for FUSE, which can be mounted by:
-
-  mount -t fusectl none /sys/fs/fuse/connections
-
-Mounting it under the '/sys/fs/fuse/connections' directory makes it
-backwards compatible with versions before 2.6.0.
-
-Under the fuse control filesystem each connection has a directory
-named by a unique number.
-
-For each connection the following files exist within this directory:
-
- 'waiting'
-
-  The number of requests which are waiting to be transferred to
-  userspace or being processed by the filesystem daemon.  If there is
-  no filesystem activity and 'waiting' is non-zero, then the
-  filesystem is hung or deadlocked.
-
- 'abort'
-
-  Writing anything into this file will abort the filesystem
-  connection.  This means that all waiting requests will be aborted an
-  error returned for all aborted and new requests.
-
-Only the owner of the mount may read or write these files.
-
-Interrupting filesystem operations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If a process issuing a FUSE filesystem request is interrupted, the
-following will happen:
-
-  1) If the request is not yet sent to userspace AND the signal is
-     fatal (SIGKILL or unhandled fatal signal), then the request is
-     dequeued and returns immediately.
-
-  2) If the request is not yet sent to userspace AND the signal is not
-     fatal, then an 'interrupted' flag is set for the request.  When
-     the request has been successfully transferred to userspace and
-     this flag is set, an INTERRUPT request is queued.
-
-  3) If the request is already sent to userspace, then an INTERRUPT
-     request is queued.
-
-INTERRUPT requests take precedence over other requests, so the
-userspace filesystem will receive queued INTERRUPTs before any others.
-
-The userspace filesystem may ignore the INTERRUPT requests entirely,
-or may honor them by sending a reply to the _original_ request, with
-the error set to EINTR.
-
-It is also possible that there's a race between processing the
-original request and it's INTERRUPT request.  There are two possibilities:
-
-  1) The INTERRUPT request is processed before the original request is
-     processed
-
-  2) The INTERRUPT request is processed after the original request has
-     been answered
-
-If the filesystem cannot find the original request, it should wait for
-some timeout and/or a number of new requests to arrive, after which it
-should reply to the INTERRUPT request with an EAGAIN error.  In case
-1) the INTERRUPT request will be requeued.  In case 2) the INTERRUPT
-reply will be ignored.
-
-Aborting a filesystem connection
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-It is possible to get into certain situations where the filesystem is
-not responding.  Reasons for this may be:
-
-  a) Broken userspace filesystem implementation
-
-  b) Network connection down
-
-  c) Accidental deadlock
-
-  d) Malicious deadlock
-
-(For more on c) and d) see later sections)
-
-In either of these cases it may be useful to abort the connection to
-the filesystem.  There are several ways to do this:
-
-  - Kill the filesystem daemon.  Works in case of a) and b)
-
-  - Kill the filesystem daemon and all users of the filesystem.  Works
-    in all cases except some malicious deadlocks
-
-  - Use forced umount (umount -f).  Works in all cases but only if
-    filesystem is still attached (it hasn't been lazy unmounted)
-
-  - Abort filesystem through the FUSE control filesystem.  Most
-    powerful method, always works.
-
-How do non-privileged mounts work?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Since the mount() system call is a privileged operation, a helper
-program (fusermount3) is needed, which is installed setuid root.
-
-The implication of providing non-privileged mounts is that the mount
-owner must not be able to use this capability to compromise the
-system.  Obvious requirements arising from this are:
-
- A) mount owner should not be able to get elevated privileges with the
-    help of the mounted filesystem
-
- B) mount owner should not get illegitimate access to information from
-    other users' and the super user's processes
-
- C) mount owner should not be able to induce undesired behavior in
-    other users' or the super user's processes
-
-How are requirements fulfilled?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- A) The mount owner could gain elevated privileges by either:
-
-     1) creating a filesystem containing a device file, then opening
-	this device
-
-     2) creating a filesystem containing a suid or sgid application,
-	then executing this application
-
-    The solution is not to allow opening device files and ignore
-    setuid and setgid bits when executing programs.  To ensure this
-    fusermount3 always adds "nosuid" and "nodev" to the mount options
-    for non-privileged mounts.
-
- B) If another user is accessing files or directories in the
-    filesystem, the filesystem daemon serving requests can record the
-    exact sequence and timing of operations performed.  This
-    information is otherwise inaccessible to the mount owner, so this
-    counts as an information leak.
-
-    The solution to this problem will be presented in point 2) of C).
-
- C) There are several ways in which the mount owner can induce
-    undesired behavior in other users' processes, such as:
-
-     1) mounting a filesystem over a file or directory which the mount
-        owner could otherwise not be able to modify (or could only
-        make limited modifications).
-
-        This is solved in fusermount3, by checking the access
-        permissions on the mountpoint and only allowing the mount if
-        the mount owner can do unlimited modification (has write
-        access to the mountpoint, and mountpoint is not a "sticky"
-        directory)
-
-     2) Even if 1) is solved the mount owner can change the behavior
-        of other users' processes.
-
-         i) It can slow down or indefinitely delay the execution of a
-           filesystem operation creating a DoS against the user or the
-           whole system.  For example a suid application locking a
-           system file, and then accessing a file on the mount owner's
-           filesystem could be stopped, and thus causing the system
-           file to be locked forever.
-
-         ii) It can present files or directories of unlimited length, or
-           directory structures of unlimited depth, possibly causing a
-           system process to eat up diskspace, memory or other
-           resources, again causing DoS.
-
-	The solution to this as well as B) is not to allow processes
-	to access the filesystem, which could otherwise not be
-	monitored or manipulated by the mount owner.  Since if the
-	mount owner can ptrace a process, it can do all of the above
-	without using a FUSE mount, the same criteria as used in
-	ptrace can be used to check if a process is allowed to access
-	the filesystem or not.
-
-	Note that the ptrace check is not strictly necessary to
-	prevent B/2/i, it is enough to check if mount owner has enough
-	privilege to send signal to the process accessing the
-	filesystem, since SIGSTOP can be used to get a similar effect.
-
-I think these limitations are unacceptable?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If a sysadmin trusts the users enough, or can ensure through other
-measures, that system processes will never enter non-privileged
-mounts, it can relax the last limitation with a "user_allow_other"
-config option.  If this config option is set, the mounting user can
-add the "allow_other" mount option which disables the check for other
-users' processes.
-
-Kernel - userspace interface
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The following diagram shows how a filesystem operation (in this
-example unlink) is performed in FUSE.
-
-NOTE: everything in this description is greatly simplified
-
- |  "rm /mnt/fuse/file"               |  FUSE filesystem daemon
- |                                    |
- |                                    |  >sys_read()
- |                                    |    >fuse_dev_read()
- |                                    |      >request_wait()
- |                                    |        [sleep on fc->waitq]
- |                                    |
- |  >sys_unlink()                     |
- |    >fuse_unlink()                  |
- |      [get request from             |
- |       fc->unused_list]             |
- |      >request_send()               |
- |        [queue req on fc->pending]  |
- |        [wake up fc->waitq]         |        [woken up]
- |        >request_wait_answer()      |
- |          [sleep on req->waitq]     |
- |                                    |      <request_wait()
- |                                    |      [remove req from fc->pending]
- |                                    |      [copy req to read buffer]
- |                                    |      [add req to fc->processing]
- |                                    |    <fuse_dev_read()
- |                                    |  <sys_read()
- |                                    |
- |                                    |  [perform unlink]
- |                                    |
- |                                    |  >sys_write()
- |                                    |    >fuse_dev_write()
- |                                    |      [look up req in fc->processing]
- |                                    |      [remove from fc->processing]
- |                                    |      [copy write buffer to req]
- |          [woken up]                |      [wake up req->waitq]
- |                                    |    <fuse_dev_write()
- |                                    |  <sys_write()
- |        <request_wait_answer()      |
- |      <request_send()               |
- |      [add request to               |
- |       fc->unused_list]             |
- |    <fuse_unlink()                  |
- |  <sys_unlink()                     |
-
-There are a couple of ways in which to deadlock a FUSE filesystem.
-Since we are talking about unprivileged userspace programs,
-something must be done about these.
-
-Scenario 1 -  Simple deadlock
------------------------------
-
- |  "rm /mnt/fuse/file"               |  FUSE filesystem daemon
- |                                    |
- |  >sys_unlink("/mnt/fuse/file")     |
- |    [acquire inode semaphore        |
- |     for "file"]                    |
- |    >fuse_unlink()                  |
- |      [sleep on req->waitq]         |
- |                                    |  <sys_read()
- |                                    |  >sys_unlink("/mnt/fuse/file")
- |                                    |    [acquire inode semaphore
- |                                    |     for "file"]
- |                                    |    *DEADLOCK*
-
-The solution for this is to allow the filesystem to be aborted.
-
-Scenario 2 - Tricky deadlock
-----------------------------
-
-This one needs a carefully crafted filesystem.  It's a variation on
-the above, only the call back to the filesystem is not explicit,
-but is caused by a pagefault.
-
- |  Kamikaze filesystem thread 1      |  Kamikaze filesystem thread 2
- |                                    |
- |  [fd = open("/mnt/fuse/file")]     |  [request served normally]
- |  [mmap fd to 'addr']               |
- |  [close fd]                        |  [FLUSH triggers 'magic' flag]
- |  [read a byte from addr]           |
- |    >do_page_fault()                |
- |      [find or create page]         |
- |      [lock page]                   |
- |      >fuse_readpage()              |
- |         [queue READ request]       |
- |         [sleep on req->waitq]      |
- |                                    |  [read request to buffer]
- |                                    |  [create reply header before addr]
- |                                    |  >sys_write(addr - headerlength)
- |                                    |    >fuse_dev_write()
- |                                    |      [look up req in fc->processing]
- |                                    |      [remove from fc->processing]
- |                                    |      [copy write buffer to req]
- |                                    |        >do_page_fault()
- |                                    |           [find or create page]
- |                                    |           [lock page]
- |                                    |           * DEADLOCK *
-
-Solution is basically the same as above.
-
-An additional problem is that while the write buffer is being copied
-to the request, the request must not be interrupted/aborted.  This is
-because the destination address of the copy may not be valid after the
-request has returned.
-
-This is solved with doing the copy atomically, and allowing abort
-while the page(s) belonging to the write buffer are faulted with
-get_user_pages().  The 'req->locked' flag indicates when the copy is
-taking place, and abort is delayed until this flag is unset.
diff --git a/doc/mainpage.dox b/doc/mainpage.dox
deleted file mode 100644
index 36ba3bc..0000000
--- a/doc/mainpage.dox
+++ /dev/null
@@ -1,54 +0,0 @@
-/*!
-\mainpage libfuse API documentation
-
-FUSE (Filesystem in Userspace) is an interface for userspace programs
-to export a filesystem to the Linux kernel. The FUSE project consists
-of two components: the *fuse* kernel module (maintained in the regular
-kernel repositories) and the *libfuse* userspace library. libfuse
-provides the reference implementation for communicating with the FUSE
-kernel module.
-
-A FUSE file system is typically implemented as a standalone
-application that links with libfuse. libfuse provides functions to
-mount the file system, unmount it, read requests from the kernel, and
-send responses back.
-
-
-## Getting started ##
-
-libfuse offers two APIs: a "high-level", synchronous API, and a
-"low-level" asynchronous API. In both cases, incoming requests from
-the kernel are passed to the main program using callbacks. When using
-the high-level API, the callbacks may work with file names and paths
-instead of inodes, and processing of a request finishes when the
-callback function returns. When using the low-level API, the callbacks
-must work with inodes and responses must be sent explicitly using a
-separate set of API functions.
-
-The high-level API that is primarily specified in fuse.h. The
-low-level API that is primarily documented in fuse_lowlevel.h.
-
-## Examples ##
-
-FUSE comes with several examples in the <a
-href="files.html">examples</a> directory. A good starting point are
-hello.c (for the high-level API) and hello_ll.c (for the low-level
-API).
-
-## FUSE internals ##
-
-The authoritative source of information about libfuse internals
-(including the protocol used for communication with the FUSE kernel
-module) is the source code.
-
-However, some people have kindly documented different aspects of FUSE
-in a more beginner friendly way. While this information is
-increasingly out of date, it still provides a good overview:
-
-- Bharat Vangoor et al have included an overview of the FUSE internals
-  in a <a href="fast17-vangoor.pdf">paper evaluating FUSE performance</a>.
-
-- Some documentation of the kernel-userspace protocol is available on
-  the <a href="https://github.com/libfuse/libfuse/wiki/">libfuse wiki</a>.
-
-*/
diff --git a/doc/meson.build b/doc/meson.build
deleted file mode 100644
index db3e0b2..0000000
--- a/doc/meson.build
+++ /dev/null
@@ -1,4 +0,0 @@
-if not platform.endswith('bsd') and platform != 'dragonfly'
-  install_man('fusermount3.1', 'mount.fuse3.8')
-endif
-
diff --git a/doc/mount.fuse3.8 b/doc/mount.fuse3.8
deleted file mode 100644
index 0cb0b5c..0000000
--- a/doc/mount.fuse3.8
+++ /dev/null
@@ -1,258 +0,0 @@
-.TH fuse "8"
-.SH NAME
-fuse \- configuration and mount options for FUSE file systems
-.SH DESCRIPTION
-FUSE (Filesystem in Userspace) is a simple interface for userspace programs to export a virtual filesystem to the Linux kernel. FUSE also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations.
-.SH DEFINITIONS
-.TP
-\fBFUSE\fP
-The in-kernel filesystem that forwards requests to a user-space
-process.
-.TP
-\fBfilesystem\fP
-The user-space process that responds to requests received from the
-kernel.
-.TP
-\fBlibfuse\fP
-The shared library that most (user-space) filesystems use to
-communicate with FUSE (the kernel filesystem). libfuse also provides
-the \fBfusermount3\fP (or \fBfusermount\fP if you have older version of
-libfuse) helper to allow non-privileged users to mount filesystems.
-.TP
-\fBfilesystem owner\fP
-The user that starts the filesystem and instructs the kernel to
-associate it with a particular mountpoint. The latter is typically done
-by the filesystem itself on start-up. When using libfuse, this is done
-by calling the \fBfusermount3\fP utility.
-.TP
-\fBclient\fP
-Any process that interacts with the mountpoint.
-.SH CONFIGURATION
-Some options regarding mount policy can be set in the file \fI/etc/fuse.conf\fP. Currently these options are:
-.TP
-\fBmount_max = NNN\fP
-Set the maximum number of FUSE mounts allowed to non-root users. The default is 1000.
-.TP
-\fBuser_allow_other\fP
-Allow non-root users to specify the \fBallow_other\fP or
-\fBallow_root\fP mount options (see below).
-.TP
-These limits are enforced by the \fBfusermount3\fP helper, so they can be avoided by filesystems that run as root.
-.SH OPTIONS
-Most of the generic mount options described in \fBmount\fP are
-supported (\fBro\fP, \fBrw\fP, \fBsuid\fP, \fBnosuid\fP, \fBdev\fP,
-\fBnodev\fP, \fBexec\fP, \fBnoexec\fP, \fBatime\fP, \fBnoatime\fP,
-\fBsync\fP, \fBasync\fP, \fBdirsync\fP). Filesystems are mounted with
-\fBnodev,nosuid\fP by default, which can only be overridden by a
-privileged user.
-.SS "General mount options:"
-These are FUSE specific mount options that can be specified for all filesystems:
-.TP
-\fBdefault_permissions\fP
-This option instructs the kernel to perform its own permission check
-instead of deferring all permission checking to the
-filesystem. The check by the kernel is done in addition to any
-permission checks by the filesystem, and both have to succeed for an
-operation to be allowed. The kernel performs a standard UNIX permission
-check (based on mode bits and ownership of the directory entry, and
-uid/gid of the client).
-
-This mount option is activated implicitly if the filesystem enables
-ACL support during the initial feature negotiation when opening the
-device fd. In this case, the kernel performs both ACL and standard
-unix permission checking.
-
-Filesystems that do not implement any permission checking should
-generally add this option internally.
-.TP
-\fBallow_other\fP
-This option overrides the security measure
-restricting file access to the filesystem owner, so that all users
-(including root) can access the files.
-.TP
-\fBrootmode=M\fP
-Specifies the file mode of the filesystem's root (in octal
-representation).
-.TP
-\fBblkdev\fP
-Mount a filesystem backed by a block device.  This is a privileged
-option. The device must be specified with the \fBfsname=NAME\fP
-option.
-.TP
-\fBblksize=N\fP
-Set the block size for the filesystem. This option is only valid
-for 'fuseblk' type mounts. The default is 512.
-
-In most cases, this option should not be specified by
-the filesystem owner but set internally by the filesystem.
-.TP
-\fBmax_read=N\fP
-With this option the maximum size of read operations can be set. The
-default is infinite, but typically the kernel enforces its own limit
-in addition to this one. A value of zero corresponds to no limit.
-
-This option should not be specified by the filesystem owner. The
-correct (or optimum) value depends on the filesystem implementation
-and should thus be set by the filesystem internally.
-
-This mount option is deprecated in favor of direct negotiation over
-the device fd (as done for e.g. the maximum size of write
-operations). For the time being, libfuse-using filesystems that want
-to limit the read size must therefore use this mount option \fIand\fP
-set the same value again in the init() handler.
-.TP
-\fBfd=N\fP
-The file descriptor to use for communication between the userspace
-filesystem and the kernel.  The file descriptor must have been
-obtained by opening the FUSE device (/dev/fuse).
-
-This option should not be specified by the filesystem owner. It is set
-by libfuse (or, if libfuse is not used, must be set by the filesystem
-itself).
-.TP
-\fBuser_id=N\fP
-\fBgroup_id=N\fP
-Specifies the numeric uid/gid of the mount owner.
-
-This option should not be specified by the filesystem owner. It is set
-by libfuse (or, if libfuse is not used, must be set by the filesystem
-itself).
-.TP
-\fBfsname=NAME\fP
-Sets the filesystem source (first field in \fI/etc/mtab\fP). The
-default is the name of the filesystem process.
-.TP
-\fBsubtype=TYPE\fP
-Sets the filesystem type (third field in \fI/etc/mtab\fP). The default
-is the name of the filesystem process. If the kernel supports it, \fI/etc/mtab\fP and \fI/proc/mounts\fP will show the filesystem type as \fBfuse.TYPE\fP
-
-If the kernel doesn't support subtypes, the source field will be
-\fBTYPE#NAME\fP, or if \fBfsname\fP option is not specified, just
-\fBTYPE\fP.
-
-.SS "libfuse-specific mount options:"
-These following options are not actually passed to the kernel but
-interpreted by libfuse. They can be specified for all filesystems
-that use libfuse:
-.TP
-\fBallow_root\fP
-This option is similar to \fBallow_other\fP but file access is limited
-to the filesystem owner and root.  This option and \fBallow_other\fP are mutually exclusive.
-.TP
-\fBauto_unmount\fP
-This option enables automatic release of the mountpoint if filesystem
-terminates for any reason. Normally the filesystem is
-responsible for releasing the mountpoint, which means that the
-mountpoint becomes inaccessible if the filesystem terminates
-without first unmounting.
-
-At the moment, this option implies that the filesystem will also be
-mounted with \fBnodev\fP and \fBnosuid\fP (even when mounted by
-root). This restriction may be lifted in the future.
-
-.SS "High-level mount options:"
-These following options are not actually passed to the kernel but
-interpreted by libfuse. They can only be specified for filesystems
-that use the high-level libfuse API:
-.TP
-\fBkernel_cache\fP
-This option disables flushing the cache of the file contents on every \fBopen\fP(2).  This should only be enabled on filesystems, where the file data is never changed externally (not through the mounted FUSE filesystem).  Thus it is not suitable for network filesystems and other "intermediate" filesystems.
-
-\fBNOTE\fP: if this option is not specified (and neither \fBdirect_io\fP) data is still cached after the \fBopen\fP(2), so a \fBread\fP(2) system call will not always initiate a read operation.
-.TP
-\fBauto_cache\fP
-This option is an alternative to
-\fBkernel_cache\fP. Instead of unconditionally keeping cached data, the
-cached data is invalidated on \fBopen\fP(2) if the modification
-time or the size of the file has changed since it was last opened.
-.TP
-\fBumask=M\fP
-Override the permission bits in \fIst_mode\fP set by the filesystem. The resulting permission bits are the ones missing from the given umask value.  The value is given in octal representation.
-.TP
-\fBuid=N\fP
-Override the \fIst_uid\fP field set by the filesystem (N is numeric).
-.TP
-\fBgid=N\fP
-Override the \fIst_gid\fP field set by the filesystem (N is numeric).
-.TP
-\fBentry_timeout=T\fP
-The timeout in seconds for which name lookups will be cached. The default is 1.0 second. For all the timeout options, it is possible to give fractions of a second as well (e.g. \fBentry_timeout=2.8\fP)
-.TP
-\fBnegative_timeout=T\fP
-The timeout in seconds for which a negative lookup will be cached. This means, that if file did not exist (lookup returned \fBENOENT\fP), the lookup will only be redone after the timeout, and the file/directory will be assumed to not exist until then.  The default is 0.0 second, meaning that caching negative lookups are disabled.
-.TP
-\fBattr_timeout=T\fP
-The timeout in seconds for which file/directory attributes are cached.  The default is 1.0 second.
-.TP
-\fBac_attr_timeout=T\fP
-The timeout in seconds for which file attributes are cached for the purpose of checking if \fBauto_cache\fP should flush the file data on  open. The default is the value of \fBattr_timeout\fP
-.TP
-\fBnoforget\fP
-.TP
-\fBremember=T\fP
-Normally, libfuse assigns inodes to paths only for as long as the kernel
-is aware of them. With this option inodes are instead assigned
-for at least \fBT\fP seconds (or, in the case of \fBnoforget\fP,
-the life-time of the filesystem). This will require more
-memory, but may be necessary when using applications that make use of
-inode numbers.
-.TP
-\fBmodules=M1[:M2...]\fP
-Add modules to the filesystem stack.  Modules are pushed in the order they are specified, with the original filesystem being on the bottom of the stack.
-
-.SS "\fBmount.fuse3\fP options:"
-These options are interpreted by \fBmount.fuse3\fP and are thus only available when mounting a file system via \fBmount.fuse3\fP (such as when mounting via the generic \fBmount\fP(1) command or \fI/etc/fstab\fP). Supported options are:
-.TP
-\fBsetuid=USER\fP
-Switch to \fBUSER\fP and its primary group before launching the FUSE file system process. mount.fuse3 must be run as root or with \fBCAP_SETUID\fP and \fBCAP_SETGID\fP for this to work.
-.TP
-\fBdrop_privileges\fP
-Perform setup of the FUSE file descriptor and mounting the file system before launching the FUSE file system process. \fBmount.fuse3\fP requires privilege to do so, i.e. must be run as root or at least with \fBCAP_SYS_ADMIN\fP and \fBCAP_SETPCAP\fP. It will launch the file system process fully unprivileged, i.e. without \fBcapabilities\fP(7) and \fBprctl\fP(2) flags set up such that privileges can't be reacquired (e.g. via setuid or fscaps binaries). This reduces risk in the event of the FUSE file system process getting compromised by malicious file system data.
-
-.SH FUSE MODULES (STACKING)
-Modules are filesystem stacking support to high level API. Filesystem modules can be built into libfuse or loaded from shared object
-.SS "iconv"
-Perform file name character set conversion.  Options are:
-.TP
-\fBfrom_code=CHARSET\fP
-Character set to convert from (see \fBiconv -l\fP for a list of possible values). Default is \fBUTF-8\fP.
-.TP
-\fBto_code=CHARSET\fP
-Character set to convert to.  Default is determined by the current locale.
-.SS "subdir"
-Prepend a given directory to each path. Options are:
-.TP
-\fBsubdir=DIR\fP
-Directory to prepend to all paths.  This option is \fImandatory\fP.
-.TP
-\fBrellinks\fP
-Transform absolute symlinks into relative
-.TP
-\fBnorellinks\fP
-Do not transform absolute symlinks into relative.  This is the default.
-.SH SECURITY
-The fusermount3 program is installed set-user-gid to fuse. This is done to allow users from fuse group to mount
-their own filesystem implementations.
-There must however be some limitations, in order to prevent Bad User from
-doing nasty things.  Currently those limitations are:
-.IP 1.
-The user can only mount on a mountpoint, for which it has write permission
-.IP 2.
-The mountpoint is not a sticky directory which isn't owned by the user (like \fI/tmp\fP usually is)
-.IP 3.
-No other user (including root) can access the contents of the mounted filesystem.
-.SH NOTE
-FUSE filesystems are unmounted using the \fBfusermount3\fP(1) command (\fBfusermount3 -u mountpoint\fP).
-.SH "AUTHORS"
-.LP
-FUSE is currently maintained by Nikolaus Rath <Nikolaus@rath.org>
-.LP
-The original author of FUSE is Miklos Szeredi <mszeredi@inf.bme.hu>.
-.LP
-This man page was originally written by Bastien Roucaries <roucaries.bastien+debian@gmail.com> for the
-Debian GNU/Linux distribution.
-.SH SEE ALSO
-.BR fusermount3 (1)
-.BR fusermount (1)
-.BR mount (8)
-.BR fuse (4)
diff --git a/example/.gitignore b/example/.gitignore
deleted file mode 100644
index d59f16e..0000000
--- a/example/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-/passthrough
-/passthrough_fh
-/null
-/hello
-/hello_ll
-/ioctl
-/ioctl_client
-/poll
-/poll_client
-/cuse
-/cuse_client
-/passthrough_ll
-/notify_inval_inode
-/notify_inval_inode_fh
-/notify_store_retrieve
-/notify_inval_entry
diff --git a/example/cuse.c b/example/cuse.c
deleted file mode 100644
index 0c0e7bc..0000000
--- a/example/cuse.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
-  CUSE example: Character device in Userspace
-  Copyright (C) 2008-2009  SUSE Linux Products GmbH
-  Copyright (C) 2008-2009  Tejun Heo <tj@kernel.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-
-*/
-
-/** @file
- *
- * This example demonstrates how to implement a character device in
- * userspace ("CUSE"). This is only allowed for root. The character
- * device should appear in /dev under the specified name. It can be
- * tested with the cuse_client.c program.
- *
- * Mount the file system with:
- *
- *     cuse -f --name=mydevice
- *
- * You should now have a new /dev/mydevice character device. To "unmount" it,
- * kill the "cuse" process.
- *
- * To compile this example, run
- *
- *     gcc -Wall cuse.c `pkg-config fuse3 --cflags --libs` -o cuse
- *
- * ## Source code ##
- * \include cuse.c
- */
-
-
-#define FUSE_USE_VERSION 31
-
-#include <cuse_lowlevel.h>
-#include <fuse_opt.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include "ioctl.h"
-
-static void *cusexmp_buf;
-static size_t cusexmp_size;
-
-static const char *usage =
-"usage: cusexmp [options]\n"
-"\n"
-"options:\n"
-"    --help|-h             print this help message\n"
-"    --maj=MAJ|-M MAJ      device major number\n"
-"    --min=MIN|-m MIN      device minor number\n"
-"    --name=NAME|-n NAME   device name (mandatory)\n"
-"    -d   -o debug         enable debug output (implies -f)\n"
-"    -f                    foreground operation\n"
-"    -s                    disable multi-threaded operation\n"
-"\n";
-
-static int cusexmp_resize(size_t new_size)
-{
-	void *new_buf;
-
-	if (new_size == cusexmp_size)
-		return 0;
-
-	new_buf = realloc(cusexmp_buf, new_size);
-	if (!new_buf && new_size)
-		return -ENOMEM;
-
-	if (new_size > cusexmp_size)
-		memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
-
-	cusexmp_buf = new_buf;
-	cusexmp_size = new_size;
-
-	return 0;
-}
-
-static int cusexmp_expand(size_t new_size)
-{
-	if (new_size > cusexmp_size)
-		return cusexmp_resize(new_size);
-	return 0;
-}
-
-static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
-{
-	fuse_reply_open(req, fi);
-}
-
-static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
-			 struct fuse_file_info *fi)
-{
-	(void)fi;
-
-	if (off >= cusexmp_size)
-		off = cusexmp_size;
-	if (size > cusexmp_size - off)
-		size = cusexmp_size - off;
-
-	fuse_reply_buf(req, cusexmp_buf + off, size);
-}
-
-static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
-			  off_t off, struct fuse_file_info *fi)
-{
-	(void)fi;
-
-	if (cusexmp_expand(off + size)) {
-		fuse_reply_err(req, ENOMEM);
-		return;
-	}
-
-	memcpy(cusexmp_buf + off, buf, size);
-	fuse_reply_write(req, size);
-}
-
-static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
-		       size_t in_bufsz, size_t out_bufsz, int is_read)
-{
-	const struct fioc_rw_arg *arg;
-	struct iovec in_iov[2], out_iov[3], iov[3];
-	size_t cur_size;
-
-	/* read in arg */
-	in_iov[0].iov_base = addr;
-	in_iov[0].iov_len = sizeof(*arg);
-	if (!in_bufsz) {
-		fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
-		return;
-	}
-	arg = in_buf;
-	in_buf += sizeof(*arg);
-	in_bufsz -= sizeof(*arg);
-
-	/* prepare size outputs */
-	out_iov[0].iov_base =
-		addr + offsetof(struct fioc_rw_arg, prev_size);
-	out_iov[0].iov_len = sizeof(arg->prev_size);
-
-	out_iov[1].iov_base =
-		addr + offsetof(struct fioc_rw_arg, new_size);
-	out_iov[1].iov_len = sizeof(arg->new_size);
-
-	/* prepare client buf */
-	if (is_read) {
-		out_iov[2].iov_base = arg->buf;
-		out_iov[2].iov_len = arg->size;
-		if (!out_bufsz) {
-			fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
-			return;
-		}
-	} else {
-		in_iov[1].iov_base = arg->buf;
-		in_iov[1].iov_len = arg->size;
-		if (arg->size && !in_bufsz) {
-			fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
-			return;
-		}
-	}
-
-	/* we're all set */
-	cur_size = cusexmp_size;
-	iov[0].iov_base = &cur_size;
-	iov[0].iov_len = sizeof(cur_size);
-
-	iov[1].iov_base = &cusexmp_size;
-	iov[1].iov_len = sizeof(cusexmp_size);
-
-	if (is_read) {
-		size_t off = arg->offset;
-		size_t size = arg->size;
-
-		if (off >= cusexmp_size)
-			off = cusexmp_size;
-		if (size > cusexmp_size - off)
-			size = cusexmp_size - off;
-
-		iov[2].iov_base = cusexmp_buf + off;
-		iov[2].iov_len = size;
-		fuse_reply_ioctl_iov(req, size, iov, 3);
-	} else {
-		if (cusexmp_expand(arg->offset + in_bufsz)) {
-			fuse_reply_err(req, ENOMEM);
-			return;
-		}
-
-		memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
-		fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
-	}
-}
-
-static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
-			  struct fuse_file_info *fi, unsigned flags,
-			  const void *in_buf, size_t in_bufsz, size_t out_bufsz)
-{
-	int is_read = 0;
-
-	(void)fi;
-
-	if (flags & FUSE_IOCTL_COMPAT) {
-		fuse_reply_err(req, ENOSYS);
-		return;
-	}
-
-	switch (cmd) {
-	case FIOC_GET_SIZE:
-		if (!out_bufsz) {
-			struct iovec iov = { arg, sizeof(size_t) };
-
-			fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
-		} else
-			fuse_reply_ioctl(req, 0, &cusexmp_size,
-					 sizeof(cusexmp_size));
-		break;
-
-	case FIOC_SET_SIZE:
-		if (!in_bufsz) {
-			struct iovec iov = { arg, sizeof(size_t) };
-
-			fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
-		} else {
-			cusexmp_resize(*(size_t *)in_buf);
-			fuse_reply_ioctl(req, 0, NULL, 0);
-		}
-		break;
-
-	case FIOC_READ:
-		is_read = 1;
-		/* fall through */
-	case FIOC_WRITE:
-		fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
-		break;
-
-	default:
-		fuse_reply_err(req, EINVAL);
-	}
-}
-
-struct cusexmp_param {
-	unsigned		major;
-	unsigned		minor;
-	char			*dev_name;
-	int			is_help;
-};
-
-#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
-
-static const struct fuse_opt cusexmp_opts[] = {
-	CUSEXMP_OPT("-M %u",		major),
-	CUSEXMP_OPT("--maj=%u",		major),
-	CUSEXMP_OPT("-m %u",		minor),
-	CUSEXMP_OPT("--min=%u",		minor),
-	CUSEXMP_OPT("-n %s",		dev_name),
-	CUSEXMP_OPT("--name=%s",	dev_name),
-	FUSE_OPT_KEY("-h",		0),
-	FUSE_OPT_KEY("--help",		0),
-	FUSE_OPT_END
-};
-
-static int cusexmp_process_arg(void *data, const char *arg, int key,
-			       struct fuse_args *outargs)
-{
-	struct cusexmp_param *param = data;
-
-	(void)outargs;
-	(void)arg;
-
-	switch (key) {
-	case 0:
-		param->is_help = 1;
-		fprintf(stderr, "%s", usage);
-		return fuse_opt_add_arg(outargs, "-ho");
-	default:
-		return 1;
-	}
-}
-
-static const struct cuse_lowlevel_ops cusexmp_clop = {
-	.open		= cusexmp_open,
-	.read		= cusexmp_read,
-	.write		= cusexmp_write,
-	.ioctl		= cusexmp_ioctl,
-};
-
-int main(int argc, char **argv)
-{
-	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-	struct cusexmp_param param = { 0, 0, NULL, 0 };
-	char dev_name[128] = "DEVNAME=";
-	const char *dev_info_argv[] = { dev_name };
-	struct cuse_info ci;
-	int ret = 1;
-
-	if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
-		printf("failed to parse option\n");
-		free(param.dev_name);
-		goto out;
-	}
-
-	if (!param.is_help) {
-		if (!param.dev_name) {
-			fprintf(stderr, "Error: device name missing\n");
-			goto out;
-		}
-		strncat(dev_name, param.dev_name, sizeof(dev_name) - sizeof("DEVNAME="));
-		free(param.dev_name);
-	}
-
-	memset(&ci, 0, sizeof(ci));
-	ci.dev_major = param.major;
-	ci.dev_minor = param.minor;
-	ci.dev_info_argc = 1;
-	ci.dev_info_argv = dev_info_argv;
-	ci.flags = CUSE_UNRESTRICTED_IOCTL;
-
-	ret = cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop, NULL);
-
-out:
-	fuse_opt_free_args(&args);
-	return ret;
-}
diff --git a/example/cuse_client.c b/example/cuse_client.c
deleted file mode 100644
index 903ffc6..0000000
--- a/example/cuse_client.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
-  FUSE fioclient: FUSE ioctl example client
-  Copyright (C) 2008       SUSE Linux Products GmbH
-  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This program tests the cuse.c example file system.
- *
- * Example usage (assuming that /dev/foobar is a CUSE device provided
- * by the cuse.c example file system):
- *
- *     $ cuse_client /dev/foobar s
- *     0
- *
- *     $ echo "hello" | cuse_client /dev/foobar w 6
- *     Writing 6 bytes
- *     transferred 6 bytes (0 -> 6)
- *
- *     $ cuse_client /dev/foobar s
- *     6
- *
- *     $ cuse_client /dev/foobar r 10
- *     hello
- *     transferred 6 bytes (6 -> 6)
- *
- * Compiling this example
- *
- *     gcc -Wall cuse_client.c -o cuse_client
- *
- * ## Source Code ##
- * \include cuse_client.c
- */
-
-
-#include <sys/types.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <errno.h>
-#include <unistd.h>
-#include "ioctl.h"
-
-const char *usage =
-"Usage: cuse_client FIOC_FILE COMMAND\n"
-"\n"
-"COMMANDS\n"
-"  s [SIZE]     : get size if SIZE is omitted, set size otherwise\n"
-"  r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
-"  w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
-"\n";
-
-static int do_rw(int fd, int is_read, size_t size, off_t offset,
-		 size_t *prev_size, size_t *new_size)
-{
-	struct fioc_rw_arg arg = { .offset = offset };
-	ssize_t ret;
-
-	arg.buf = calloc(1, size);
-	if (!arg.buf) {
-		fprintf(stderr, "failed to allocated %zu bytes\n", size);
-		return -1;
-	}
-
-	if (is_read) {
-		arg.size = size;
-		ret = ioctl(fd, FIOC_READ, &arg);
-		if (ret >= 0)
-			fwrite(arg.buf, 1, ret, stdout);
-	} else {
-		arg.size = fread(arg.buf, 1, size, stdin);
-		fprintf(stderr, "Writing %zu bytes\n", arg.size);
-		ret = ioctl(fd, FIOC_WRITE, &arg);
-	}
-
-	if (ret >= 0) {
-		*prev_size = arg.prev_size;
-		*new_size = arg.new_size;
-	} else
-		perror("ioctl");
-
-	free(arg.buf);
-	return ret;
-}
-
-int main(int argc, char **argv)
-{
-	size_t param[2] = { };
-	size_t size, prev_size = 0, new_size = 0;
-	char cmd;
-	int fd, i, rc;
-
-	if (argc < 3)
-		goto usage;
-
-	fd = open(argv[1], O_RDWR);
-	if (fd < 0) {
-		perror("open");
-		return 1;
-	}
-
-	cmd = tolower(argv[2][0]);
-	argc -= 3;
-	argv += 3;
-
-	for (i = 0; i < argc; i++) {
-		char *endp;
-		param[i] = strtoul(argv[i], &endp, 0);
-		if (endp == argv[i] || *endp != '\0')
-			goto usage;
-	}
-
-	switch (cmd) {
-	case 's':
-		if (!argc) {
-			if (ioctl(fd, FIOC_GET_SIZE, &size)) {
-				perror("ioctl");
-				goto error;
-			}
-			printf("%zu\n", size);
-		} else {
-			size = param[0];
-			if (ioctl(fd, FIOC_SET_SIZE, &size)) {
-				perror("ioctl");
-				goto error;
-			}
-		}
-		close(fd);
-		return 0;
-
-	case 'r':
-	case 'w':
-		rc = do_rw(fd, cmd == 'r', param[0], param[1],
-			   &prev_size, &new_size);
-		if (rc < 0)
-			goto error;
-		fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
-			rc, prev_size, new_size);
-		close(fd);
-		return 0;
-	}
-
-usage:
-	fprintf(stderr, "%s", usage);
-	return 1;
-
-error:
-	close(fd);
-	return 1;
-}
diff --git a/example/cxxopts.hpp b/example/cxxopts.hpp
deleted file mode 100644
index 80b695a..0000000
--- a/example/cxxopts.hpp
+++ /dev/null
@@ -1,2114 +0,0 @@
-/*
-
-Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-*/
-
-#ifndef CXXOPTS_HPP_INCLUDED
-#define CXXOPTS_HPP_INCLUDED
-
-#include <cstring>
-#include <cctype>
-#include <exception>
-#include <iostream>
-#include <limits>
-#include <map>
-#include <memory>
-#include <regex>
-#include <sstream>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#ifdef __cpp_lib_optional
-#include <optional>
-#define CXXOPTS_HAS_OPTIONAL
-#endif
-
-#ifndef CXXOPTS_VECTOR_DELIMITER
-#define CXXOPTS_VECTOR_DELIMITER ','
-#endif
-
-#define CXXOPTS__VERSION_MAJOR 2
-#define CXXOPTS__VERSION_MINOR 2
-#define CXXOPTS__VERSION_PATCH 1
-
-namespace cxxopts
-{
-  static constexpr struct {
-    uint8_t major, minor, patch;
-  } version = {
-    CXXOPTS__VERSION_MAJOR,
-    CXXOPTS__VERSION_MINOR,
-    CXXOPTS__VERSION_PATCH
-  };
-}
-
-//when we ask cxxopts to use Unicode, help strings are processed using ICU,
-//which results in the correct lengths being computed for strings when they
-//are formatted for the help output
-//it is necessary to make sure that <unicode/unistr.h> can be found by the
-//compiler, and that icu-uc is linked in to the binary.
-
-#ifdef CXXOPTS_USE_UNICODE
-#include <unicode/unistr.h>
-
-namespace cxxopts
-{
-  typedef icu::UnicodeString String;
-
-  inline
-  String
-  toLocalString(std::string s)
-  {
-    return icu::UnicodeString::fromUTF8(std::move(s));
-  }
-
-  class UnicodeStringIterator : public
-    std::iterator<std::forward_iterator_tag, int32_t>
-  {
-    public:
-
-    UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
-    : s(string)
-    , i(pos)
-    {
-    }
-
-    value_type
-    operator*() const
-    {
-      return s->char32At(i);
-    }
-
-    bool
-    operator==(const UnicodeStringIterator& rhs) const
-    {
-      return s == rhs.s && i == rhs.i;
-    }
-
-    bool
-    operator!=(const UnicodeStringIterator& rhs) const
-    {
-      return !(*this == rhs);
-    }
-
-    UnicodeStringIterator&
-    operator++()
-    {
-      ++i;
-      return *this;
-    }
-
-    UnicodeStringIterator
-    operator+(int32_t v)
-    {
-      return UnicodeStringIterator(s, i + v);
-    }
-
-    private:
-    const icu::UnicodeString* s;
-    int32_t i;
-  };
-
-  inline
-  String&
-  stringAppend(String&s, String a)
-  {
-    return s.append(std::move(a));
-  }
-
-  inline
-  String&
-  stringAppend(String& s, int n, UChar32 c)
-  {
-    for (int i = 0; i != n; ++i)
-    {
-      s.append(c);
-    }
-
-    return s;
-  }
-
-  template <typename Iterator>
-  String&
-  stringAppend(String& s, Iterator begin, Iterator end)
-  {
-    while (begin != end)
-    {
-      s.append(*begin);
-      ++begin;
-    }
-
-    return s;
-  }
-
-  inline
-  size_t
-  stringLength(const String& s)
-  {
-    return s.length();
-  }
-
-  inline
-  std::string
-  toUTF8String(const String& s)
-  {
-    std::string result;
-    s.toUTF8String(result);
-
-    return result;
-  }
-
-  inline
-  bool
-  empty(const String& s)
-  {
-    return s.isEmpty();
-  }
-}
-
-namespace std
-{
-  inline
-  cxxopts::UnicodeStringIterator
-  begin(const icu::UnicodeString& s)
-  {
-    return cxxopts::UnicodeStringIterator(&s, 0);
-  }
-
-  inline
-  cxxopts::UnicodeStringIterator
-  end(const icu::UnicodeString& s)
-  {
-    return cxxopts::UnicodeStringIterator(&s, s.length());
-  }
-}
-
-//ifdef CXXOPTS_USE_UNICODE
-#else
-
-namespace cxxopts
-{
-  typedef std::string String;
-
-  template <typename T>
-  T
-  toLocalString(T&& t)
-  {
-    return std::forward<T>(t);
-  }
-
-  inline
-  size_t
-  stringLength(const String& s)
-  {
-    return s.length();
-  }
-
-  inline
-  String&
-  stringAppend(String&s, String a)
-  {
-    return s.append(std::move(a));
-  }
-
-  inline
-  String&
-  stringAppend(String& s, size_t n, char c)
-  {
-    return s.append(n, c);
-  }
-
-  template <typename Iterator>
-  String&
-  stringAppend(String& s, Iterator begin, Iterator end)
-  {
-    return s.append(begin, end);
-  }
-
-  template <typename T>
-  std::string
-  toUTF8String(T&& t)
-  {
-    return std::forward<T>(t);
-  }
-
-  inline
-  bool
-  empty(const std::string& s)
-  {
-    return s.empty();
-  }
-}
-
-//ifdef CXXOPTS_USE_UNICODE
-#endif
-
-namespace cxxopts
-{
-  namespace
-  {
-#ifdef _WIN32
-    const std::string LQUOTE("\'");
-    const std::string RQUOTE("\'");
-#else
-    const std::string LQUOTE("‘");
-    const std::string RQUOTE("’");
-#endif
-  }
-
-  class Value : public std::enable_shared_from_this<Value>
-  {
-    public:
-
-    virtual ~Value() = default;
-
-    virtual
-    std::shared_ptr<Value>
-    clone() const = 0;
-
-    virtual void
-    parse(const std::string& text) const = 0;
-
-    virtual void
-    parse() const = 0;
-
-    virtual bool
-    has_default() const = 0;
-
-    virtual bool
-    is_container() const = 0;
-
-    virtual bool
-    has_implicit() const = 0;
-
-    virtual std::string
-    get_default_value() const = 0;
-
-    virtual std::string
-    get_implicit_value() const = 0;
-
-    virtual std::shared_ptr<Value>
-    default_value(const std::string& value) = 0;
-
-    virtual std::shared_ptr<Value>
-    implicit_value(const std::string& value) = 0;
-
-    virtual std::shared_ptr<Value>
-    no_implicit_value() = 0;
-
-    virtual bool
-    is_boolean() const = 0;
-  };
-
-  class OptionException : public std::exception
-  {
-    public:
-    OptionException(const std::string& message)
-    : m_message(message)
-    {
-    }
-
-    virtual const char*
-    what() const noexcept
-    {
-      return m_message.c_str();
-    }
-
-    private:
-    std::string m_message;
-  };
-
-  class OptionSpecException : public OptionException
-  {
-    public:
-
-    OptionSpecException(const std::string& message)
-    : OptionException(message)
-    {
-    }
-  };
-
-  class OptionParseException : public OptionException
-  {
-    public:
-    OptionParseException(const std::string& message)
-    : OptionException(message)
-    {
-    }
-  };
-
-  class option_exists_error : public OptionSpecException
-  {
-    public:
-    option_exists_error(const std::string& option)
-    : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists")
-    {
-    }
-  };
-
-  class invalid_option_format_error : public OptionSpecException
-  {
-    public:
-    invalid_option_format_error(const std::string& format)
-    : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE)
-    {
-    }
-  };
-
-  class option_syntax_exception : public OptionParseException {
-    public:
-    option_syntax_exception(const std::string& text)
-    : OptionParseException("Argument " + LQUOTE + text + RQUOTE +
-        " starts with a - but has incorrect syntax")
-    {
-    }
-  };
-
-  class option_not_exists_exception : public OptionParseException
-  {
-    public:
-    option_not_exists_exception(const std::string& option)
-    : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")
-    {
-    }
-  };
-
-  class missing_argument_exception : public OptionParseException
-  {
-    public:
-    missing_argument_exception(const std::string& option)
-    : OptionParseException(
-        "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
-      )
-    {
-    }
-  };
-
-  class option_requires_argument_exception : public OptionParseException
-  {
-    public:
-    option_requires_argument_exception(const std::string& option)
-    : OptionParseException(
-        "Option " + LQUOTE + option + RQUOTE + " requires an argument"
-      )
-    {
-    }
-  };
-
-  class option_not_has_argument_exception : public OptionParseException
-  {
-    public:
-    option_not_has_argument_exception
-    (
-      const std::string& option,
-      const std::string& arg
-    )
-    : OptionParseException(
-        "Option " + LQUOTE + option + RQUOTE +
-        " does not take an argument, but argument " +
-        LQUOTE + arg + RQUOTE + " given"
-      )
-    {
-    }
-  };
-
-  class option_not_present_exception : public OptionParseException
-  {
-    public:
-    option_not_present_exception(const std::string& option)
-    : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")
-    {
-    }
-  };
-
-  class argument_incorrect_type : public OptionParseException
-  {
-    public:
-    argument_incorrect_type
-    (
-      const std::string& arg
-    )
-    : OptionParseException(
-        "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
-      )
-    {
-    }
-  };
-
-  class option_required_exception : public OptionParseException
-  {
-    public:
-    option_required_exception(const std::string& option)
-    : OptionParseException(
-        "Option " + LQUOTE + option + RQUOTE + " is required but not present"
-      )
-    {
-    }
-  };
-
-  namespace values
-  {
-    namespace
-    {
-      std::basic_regex<char> integer_pattern
-        ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
-      std::basic_regex<char> truthy_pattern
-        ("(t|T)(rue)?|1");
-      std::basic_regex<char> falsy_pattern
-        ("(f|F)(alse)?|0");
-    }
-
-    namespace detail
-    {
-      template <typename T, bool B>
-      struct SignedCheck;
-
-      template <typename T>
-      struct SignedCheck<T, true>
-      {
-        template <typename U>
-        void
-        operator()(bool negative, U u, const std::string& text)
-        {
-          if (negative)
-          {
-            if (u > static_cast<U>((std::numeric_limits<T>::min)()))
-            {
-              throw argument_incorrect_type(text);
-            }
-          }
-          else
-          {
-            if (u > static_cast<U>((std::numeric_limits<T>::max)()))
-            {
-              throw argument_incorrect_type(text);
-            }
-          }
-        }
-      };
-
-      template <typename T>
-      struct SignedCheck<T, false>
-      {
-        template <typename U>
-        void
-        operator()(bool, U, const std::string&) {}
-      };
-
-      template <typename T, typename U>
-      void
-      check_signed_range(bool negative, U value, const std::string& text)
-      {
-        SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
-      }
-    }
-
-    template <typename R, typename T>
-    R
-    checked_negate(T&& t, const std::string&, std::true_type)
-    {
-      // if we got to here, then `t` is a positive number that fits into
-      // `R`. So to avoid MSVC C4146, we first cast it to `R`.
-      // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
-      return -static_cast<R>(t-1)-1;
-    }
-
-    template <typename R, typename T>
-    T
-    checked_negate(T&&, const std::string& text, std::false_type)
-    {
-      throw argument_incorrect_type(text);
-    }
-
-    template <typename T>
-    void
-    integer_parser(const std::string& text, T& value)
-    {
-      std::smatch match;
-      std::regex_match(text, match, integer_pattern);
-
-      if (match.length() == 0)
-      {
-        throw argument_incorrect_type(text);
-      }
-
-      if (match.length(4) > 0)
-      {
-        value = 0;
-        return;
-      }
-
-      using US = typename std::make_unsigned<T>::type;
-
-      constexpr bool is_signed = std::numeric_limits<T>::is_signed;
-      const bool negative = match.length(1) > 0;
-      const uint8_t base = match.length(2) > 0 ? 16 : 10;
-
-      auto value_match = match[3];
-
-      US result = 0;
-
-      for (auto iter = value_match.first; iter != value_match.second; ++iter)
-      {
-        US digit = 0;
-
-        if (*iter >= '0' && *iter <= '9')
-        {
-          digit = static_cast<US>(*iter - '0');
-        }
-        else if (base == 16 && *iter >= 'a' && *iter <= 'f')
-        {
-          digit = static_cast<US>(*iter - 'a' + 10);
-        }
-        else if (base == 16 && *iter >= 'A' && *iter <= 'F')
-        {
-          digit = static_cast<US>(*iter - 'A' + 10);
-        }
-        else
-        {
-          throw argument_incorrect_type(text);
-        }
-
-        US next = result * base + digit;
-        if (result > next)
-        {
-          throw argument_incorrect_type(text);
-        }
-
-        result = next;
-      }
-
-      detail::check_signed_range<T>(negative, result, text);
-
-      if (negative)
-      {
-        value = checked_negate<T>(result,
-          text,
-          std::integral_constant<bool, is_signed>());
-      }
-      else
-      {
-        value = static_cast<T>(result);
-      }
-    }
-
-    template <typename T>
-    void stringstream_parser(const std::string& text, T& value)
-    {
-      std::stringstream in(text);
-      in >> value;
-      if (!in) {
-        throw argument_incorrect_type(text);
-      }
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, uint8_t& value)
-    {
-      integer_parser(text, value);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, int8_t& value)
-    {
-      integer_parser(text, value);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, uint16_t& value)
-    {
-      integer_parser(text, value);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, int16_t& value)
-    {
-      integer_parser(text, value);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, uint32_t& value)
-    {
-      integer_parser(text, value);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, int32_t& value)
-    {
-      integer_parser(text, value);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, uint64_t& value)
-    {
-      integer_parser(text, value);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, int64_t& value)
-    {
-      integer_parser(text, value);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, bool& value)
-    {
-      std::smatch result;
-      std::regex_match(text, result, truthy_pattern);
-
-      if (!result.empty())
-      {
-        value = true;
-        return;
-      }
-
-      std::regex_match(text, result, falsy_pattern);
-      if (!result.empty())
-      {
-        value = false;
-        return;
-      }
-
-      throw argument_incorrect_type(text);
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, std::string& value)
-    {
-      value = text;
-    }
-
-    // The fallback parser. It uses the stringstream parser to parse all types
-    // that have not been overloaded explicitly.  It has to be placed in the
-    // source code before all other more specialized templates.
-    template <typename T>
-    void
-    parse_value(const std::string& text, T& value) {
-      stringstream_parser(text, value);
-    }
-
-    template <typename T>
-    void
-    parse_value(const std::string& text, std::vector<T>& value)
-    {
-      std::stringstream in(text);
-      std::string token;
-      while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
-        T v;
-        parse_value(token, v);
-        value.emplace_back(std::move(v));
-      }
-    }
-
-#ifdef CXXOPTS_HAS_OPTIONAL
-    template <typename T>
-    void
-    parse_value(const std::string& text, std::optional<T>& value)
-    {
-      T result;
-      parse_value(text, result);
-      value = std::move(result);
-    }
-#endif
-
-    template <typename T>
-    struct type_is_container
-    {
-      static constexpr bool value = false;
-    };
-
-    template <typename T>
-    struct type_is_container<std::vector<T>>
-    {
-      static constexpr bool value = true;
-    };
-
-    template <typename T>
-    class abstract_value : public Value
-    {
-      using Self = abstract_value<T>;
-
-      public:
-      abstract_value()
-      : m_result(std::make_shared<T>())
-      , m_store(m_result.get())
-      {
-      }
-
-      abstract_value(T* t)
-      : m_store(t)
-      {
-      }
-
-      virtual ~abstract_value() = default;
-
-      abstract_value(const abstract_value& rhs)
-      {
-        if (rhs.m_result)
-        {
-          m_result = std::make_shared<T>();
-          m_store = m_result.get();
-        }
-        else
-        {
-          m_store = rhs.m_store;
-        }
-
-        m_default = rhs.m_default;
-        m_implicit = rhs.m_implicit;
-        m_default_value = rhs.m_default_value;
-        m_implicit_value = rhs.m_implicit_value;
-      }
-
-      void
-      parse(const std::string& text) const
-      {
-        parse_value(text, *m_store);
-      }
-
-      bool
-      is_container() const
-      {
-        return type_is_container<T>::value;
-      }
-
-      void
-      parse() const
-      {
-        parse_value(m_default_value, *m_store);
-      }
-
-      bool
-      has_default() const
-      {
-        return m_default;
-      }
-
-      bool
-      has_implicit() const
-      {
-        return m_implicit;
-      }
-
-      std::shared_ptr<Value>
-      default_value(const std::string& value)
-      {
-        m_default = true;
-        m_default_value = value;
-        return shared_from_this();
-      }
-
-      std::shared_ptr<Value>
-      implicit_value(const std::string& value)
-      {
-        m_implicit = true;
-        m_implicit_value = value;
-        return shared_from_this();
-      }
-
-      std::shared_ptr<Value>
-      no_implicit_value()
-      {
-        m_implicit = false;
-        return shared_from_this();
-      }
-
-      std::string
-      get_default_value() const
-      {
-        return m_default_value;
-      }
-
-      std::string
-      get_implicit_value() const
-      {
-        return m_implicit_value;
-      }
-
-      bool
-      is_boolean() const
-      {
-        return std::is_same<T, bool>::value;
-      }
-
-      const T&
-      get() const
-      {
-        if (m_store == nullptr)
-        {
-          return *m_result;
-        }
-        else
-        {
-          return *m_store;
-        }
-      }
-
-      protected:
-      std::shared_ptr<T> m_result;
-      T* m_store;
-
-      bool m_default = false;
-      bool m_implicit = false;
-
-      std::string m_default_value;
-      std::string m_implicit_value;
-    };
-
-    template <typename T>
-    class standard_value : public abstract_value<T>
-    {
-      public:
-      using abstract_value<T>::abstract_value;
-
-      std::shared_ptr<Value>
-      clone() const
-      {
-        return std::make_shared<standard_value<T>>(*this);
-      }
-    };
-
-    template <>
-    class standard_value<bool> : public abstract_value<bool>
-    {
-      public:
-      ~standard_value() = default;
-
-      standard_value()
-      {
-        set_default_and_implicit();
-      }
-
-      standard_value(bool* b)
-      : abstract_value(b)
-      {
-        set_default_and_implicit();
-      }
-
-      std::shared_ptr<Value>
-      clone() const
-      {
-        return std::make_shared<standard_value<bool>>(*this);
-      }
-
-      private:
-
-      void
-      set_default_and_implicit()
-      {
-        m_default = true;
-        m_default_value = "false";
-        m_implicit = true;
-        m_implicit_value = "true";
-      }
-    };
-  }
-
-  template <typename T>
-  std::shared_ptr<Value>
-  value()
-  {
-    return std::make_shared<values::standard_value<T>>();
-  }
-
-  template <typename T>
-  std::shared_ptr<Value>
-  value(T& t)
-  {
-    return std::make_shared<values::standard_value<T>>(&t);
-  }
-
-  class OptionAdder;
-
-  class OptionDetails
-  {
-    public:
-    OptionDetails
-    (
-      const std::string& short_,
-      const std::string& long_,
-      const String& desc,
-      std::shared_ptr<const Value> val
-    )
-    : m_short(short_)
-    , m_long(long_)
-    , m_desc(desc)
-    , m_value(val)
-    , m_count(0)
-    {
-    }
-
-    OptionDetails(const OptionDetails& rhs)
-    : m_desc(rhs.m_desc)
-    , m_count(rhs.m_count)
-    {
-      m_value = rhs.m_value->clone();
-    }
-
-    OptionDetails(OptionDetails&& rhs) = default;
-
-    const String&
-    description() const
-    {
-      return m_desc;
-    }
-
-    const Value& value() const {
-        return *m_value;
-    }
-
-    std::shared_ptr<Value>
-    make_storage() const
-    {
-      return m_value->clone();
-    }
-
-    const std::string&
-    short_name() const
-    {
-      return m_short;
-    }
-
-    const std::string&
-    long_name() const
-    {
-      return m_long;
-    }
-
-    private:
-    std::string m_short;
-    std::string m_long;
-    String m_desc;
-    std::shared_ptr<const Value> m_value;
-    int m_count;
-  };
-
-  struct HelpOptionDetails
-  {
-    std::string s;
-    std::string l;
-    String desc;
-    bool has_default;
-    std::string default_value;
-    bool has_implicit;
-    std::string implicit_value;
-    std::string arg_help;
-    bool is_container;
-    bool is_boolean;
-  };
-
-  struct HelpGroupDetails
-  {
-    std::string name;
-    std::string description;
-    std::vector<HelpOptionDetails> options;
-  };
-
-  class OptionValue
-  {
-    public:
-    void
-    parse
-    (
-      std::shared_ptr<const OptionDetails> details,
-      const std::string& text
-    )
-    {
-      ensure_value(details);
-      ++m_count;
-      m_value->parse(text);
-    }
-
-    void
-    parse_default(std::shared_ptr<const OptionDetails> details)
-    {
-      ensure_value(details);
-      m_default = true;
-      m_value->parse();
-    }
-
-    size_t
-    count() const noexcept
-    {
-      return m_count;
-    }
-
-    // TODO: maybe default options should count towards the number of arguments
-    bool
-    has_default() const noexcept
-    {
-      return m_default;
-    }
-
-    template <typename T>
-    const T&
-    as() const
-    {
-      if (m_value == nullptr) {
-        throw std::domain_error("No value");
-      }
-
-#ifdef CXXOPTS_NO_RTTI
-      return static_cast<const values::standard_value<T>&>(*m_value).get();
-#else
-      return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
-#endif
-    }
-
-    private:
-    void
-    ensure_value(std::shared_ptr<const OptionDetails> details)
-    {
-      if (m_value == nullptr)
-      {
-        m_value = details->make_storage();
-      }
-    }
-
-    std::shared_ptr<Value> m_value;
-    size_t m_count = 0;
-    bool m_default = false;
-  };
-
-  class KeyValue
-  {
-    public:
-    KeyValue(std::string key_, std::string value_)
-    : m_key(std::move(key_))
-    , m_value(std::move(value_))
-    {
-    }
-
-    const
-    std::string&
-    key() const
-    {
-      return m_key;
-    }
-
-    const
-    std::string&
-    value() const
-    {
-      return m_value;
-    }
-
-    template <typename T>
-    T
-    as() const
-    {
-      T result;
-      values::parse_value(m_value, result);
-      return result;
-    }
-
-    private:
-    std::string m_key;
-    std::string m_value;
-  };
-
-  class ParseResult
-  {
-    public:
-
-    ParseResult(
-      const std::shared_ptr<
-        std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
-      >,
-      std::vector<std::string>,
-      bool allow_unrecognised,
-      int&, char**&);
-
-    size_t
-    count(const std::string& o) const
-    {
-      auto iter = m_options->find(o);
-      if (iter == m_options->end())
-      {
-        return 0;
-      }
-
-      auto riter = m_results.find(iter->second);
-
-      return riter->second.count();
-    }
-
-    const OptionValue&
-    operator[](const std::string& option) const
-    {
-      auto iter = m_options->find(option);
-
-      if (iter == m_options->end())
-      {
-        throw option_not_present_exception(option);
-      }
-
-      auto riter = m_results.find(iter->second);
-
-      return riter->second;
-    }
-
-    const std::vector<KeyValue>&
-    arguments() const
-    {
-      return m_sequential;
-    }
-
-    private:
-
-    void
-    parse(int& argc, char**& argv);
-
-    void
-    add_to_option(const std::string& option, const std::string& arg);
-
-    bool
-    consume_positional(std::string a);
-
-    void
-    parse_option
-    (
-      std::shared_ptr<OptionDetails> value,
-      const std::string& name,
-      const std::string& arg = ""
-    );
-
-    void
-    parse_default(std::shared_ptr<OptionDetails> details);
-
-    void
-    checked_parse_arg
-    (
-      int argc,
-      char* argv[],
-      int& current,
-      std::shared_ptr<OptionDetails> value,
-      const std::string& name
-    );
-
-    const std::shared_ptr<
-      std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
-    > m_options;
-    std::vector<std::string> m_positional;
-    std::vector<std::string>::iterator m_next_positional;
-    std::unordered_set<std::string> m_positional_set;
-    std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results;
-
-    bool m_allow_unrecognised;
-
-    std::vector<KeyValue> m_sequential;
-  };
-
-  class Options
-  {
-    typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
-      OptionMap;
-    public:
-
-    Options(std::string program, std::string help_string = "")
-    : m_program(std::move(program))
-    , m_help_string(toLocalString(std::move(help_string)))
-    , m_custom_help("[OPTION...]")
-    , m_positional_help("positional parameters")
-    , m_show_positional(false)
-    , m_allow_unrecognised(false)
-    , m_options(std::make_shared<OptionMap>())
-    , m_next_positional(m_positional.end())
-    {
-    }
-
-    Options&
-    positional_help(std::string help_text)
-    {
-      m_positional_help = std::move(help_text);
-      return *this;
-    }
-
-    Options&
-    custom_help(std::string help_text)
-    {
-      m_custom_help = std::move(help_text);
-      return *this;
-    }
-
-    Options&
-    show_positional_help()
-    {
-      m_show_positional = true;
-      return *this;
-    }
-
-    Options&
-    allow_unrecognised_options()
-    {
-      m_allow_unrecognised = true;
-      return *this;
-    }
-
-    ParseResult
-    parse(int& argc, char**& argv);
-
-    OptionAdder
-    add_options(std::string group = "");
-
-    void
-    add_option
-    (
-      const std::string& group,
-      const std::string& s,
-      const std::string& l,
-      std::string desc,
-      std::shared_ptr<const Value> value,
-      std::string arg_help
-    );
-
-    //parse positional arguments into the given option
-    void
-    parse_positional(std::string option);
-
-    void
-    parse_positional(std::vector<std::string> options);
-
-    void
-    parse_positional(std::initializer_list<std::string> options);
-
-    template <typename Iterator>
-    void
-    parse_positional(Iterator begin, Iterator end) {
-      parse_positional(std::vector<std::string>{begin, end});
-    }
-
-    std::string
-    help(const std::vector<std::string>& groups = {}) const;
-
-    const std::vector<std::string>
-    groups() const;
-
-    const HelpGroupDetails&
-    group_help(const std::string& group) const;
-
-    private:
-
-    void
-    add_one_option
-    (
-      const std::string& option,
-      std::shared_ptr<OptionDetails> details
-    );
-
-    String
-    help_one_group(const std::string& group) const;
-
-    void
-    generate_group_help
-    (
-      String& result,
-      const std::vector<std::string>& groups
-    ) const;
-
-    void
-    generate_all_groups_help(String& result) const;
-
-    std::string m_program;
-    String m_help_string;
-    std::string m_custom_help;
-    std::string m_positional_help;
-    bool m_show_positional;
-    bool m_allow_unrecognised;
-
-    std::shared_ptr<OptionMap> m_options;
-    std::vector<std::string> m_positional;
-    std::vector<std::string>::iterator m_next_positional;
-    std::unordered_set<std::string> m_positional_set;
-
-    //mapping from groups to help options
-    std::map<std::string, HelpGroupDetails> m_help;
-  };
-
-  class OptionAdder
-  {
-    public:
-
-    OptionAdder(Options& options, std::string group)
-    : m_options(options), m_group(std::move(group))
-    {
-    }
-
-    OptionAdder&
-    operator()
-    (
-      const std::string& opts,
-      const std::string& desc,
-      std::shared_ptr<const Value> value
-        = ::cxxopts::value<bool>(),
-      std::string arg_help = ""
-    );
-
-    private:
-    Options& m_options;
-    std::string m_group;
-  };
-
-  namespace
-  {
-    constexpr int OPTION_LONGEST = 30;
-    constexpr int OPTION_DESC_GAP = 2;
-
-    std::basic_regex<char> option_matcher
-      ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
-
-    std::basic_regex<char> option_specifier
-      ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
-
-    String
-    format_option
-    (
-      const HelpOptionDetails& o
-    )
-    {
-      auto& s = o.s;
-      auto& l = o.l;
-
-      String result = "  ";
-
-      if (s.size() > 0)
-      {
-        result += "-" + toLocalString(s) + ",";
-      }
-      else
-      {
-        result += "   ";
-      }
-
-      if (l.size() > 0)
-      {
-        result += " --" + toLocalString(l);
-      }
-
-      auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
-
-      if (!o.is_boolean)
-      {
-        if (o.has_implicit)
-        {
-          result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
-        }
-        else
-        {
-          result += " " + arg;
-        }
-      }
-
-      return result;
-    }
-
-    String
-    format_description
-    (
-      const HelpOptionDetails& o,
-      size_t start,
-      size_t width
-    )
-    {
-      auto desc = o.desc;
-
-      if (o.has_default && (!o.is_boolean || o.default_value != "false"))
-      {
-        desc += toLocalString(" (default: " + o.default_value + ")");
-      }
-
-      String result;
-
-      auto current = std::begin(desc);
-      auto startLine = current;
-      auto lastSpace = current;
-
-      auto size = size_t{};
-
-      while (current != std::end(desc))
-      {
-        if (*current == ' ')
-        {
-          lastSpace = current;
-        }
-
-        if (*current == '\n')
-        {
-          startLine = current + 1;
-          lastSpace = startLine;
-        }
-        else if (size > width)
-        {
-          if (lastSpace == startLine)
-          {
-            stringAppend(result, startLine, current + 1);
-            stringAppend(result, "\n");
-            stringAppend(result, start, ' ');
-            startLine = current + 1;
-            lastSpace = startLine;
-          }
-          else
-          {
-            stringAppend(result, startLine, lastSpace);
-            stringAppend(result, "\n");
-            stringAppend(result, start, ' ');
-            startLine = lastSpace + 1;
-            lastSpace = startLine;
-          }
-          size = 0;
-        }
-        else
-        {
-          ++size;
-        }
-
-        ++current;
-      }
-
-      //append whatever is left
-      stringAppend(result, startLine, current);
-
-      return result;
-    }
-  }
-
-inline
-ParseResult::ParseResult
-(
-  const std::shared_ptr<
-    std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
-  > options,
-  std::vector<std::string> positional,
-  bool allow_unrecognised,
-  int& argc, char**& argv
-)
-: m_options(options)
-, m_positional(std::move(positional))
-, m_next_positional(m_positional.begin())
-, m_allow_unrecognised(allow_unrecognised)
-{
-  parse(argc, argv);
-}
-
-inline
-OptionAdder
-Options::add_options(std::string group)
-{
-  return OptionAdder(*this, std::move(group));
-}
-
-inline
-OptionAdder&
-OptionAdder::operator()
-(
-  const std::string& opts,
-  const std::string& desc,
-  std::shared_ptr<const Value> value,
-  std::string arg_help
-)
-{
-  std::match_results<const char*> result;
-  std::regex_match(opts.c_str(), result, option_specifier);
-
-  if (result.empty())
-  {
-    throw invalid_option_format_error(opts);
-  }
-
-  const auto& short_match = result[2];
-  const auto& long_match = result[3];
-
-  if (!short_match.length() && !long_match.length())
-  {
-    throw invalid_option_format_error(opts);
-  } else if (long_match.length() == 1 && short_match.length())
-  {
-    throw invalid_option_format_error(opts);
-  }
-
-  auto option_names = []
-  (
-    const std::sub_match<const char*>& short_,
-    const std::sub_match<const char*>& long_
-  )
-  {
-    if (long_.length() == 1)
-    {
-      return std::make_tuple(long_.str(), short_.str());
-    }
-    else
-    {
-      return std::make_tuple(short_.str(), long_.str());
-    }
-  }(short_match, long_match);
-
-  m_options.add_option
-  (
-    m_group,
-    std::get<0>(option_names),
-    std::get<1>(option_names),
-    desc,
-    value,
-    std::move(arg_help)
-  );
-
-  return *this;
-}
-
-inline
-void
-ParseResult::parse_default(std::shared_ptr<OptionDetails> details)
-{
-  m_results[details].parse_default(details);
-}
-
-inline
-void
-ParseResult::parse_option
-(
-  std::shared_ptr<OptionDetails> value,
-  const std::string& /*name*/,
-  const std::string& arg
-)
-{
-  auto& result = m_results[value];
-  result.parse(value, arg);
-
-  m_sequential.emplace_back(value->long_name(), arg);
-}
-
-inline
-void
-ParseResult::checked_parse_arg
-(
-  int argc,
-  char* argv[],
-  int& current,
-  std::shared_ptr<OptionDetails> value,
-  const std::string& name
-)
-{
-  if (current + 1 >= argc)
-  {
-    if (value->value().has_implicit())
-    {
-      parse_option(value, name, value->value().get_implicit_value());
-    }
-    else
-    {
-      throw missing_argument_exception(name);
-    }
-  }
-  else
-  {
-    if (value->value().has_implicit())
-    {
-      parse_option(value, name, value->value().get_implicit_value());
-    }
-    else
-    {
-      parse_option(value, name, argv[current + 1]);
-      ++current;
-    }
-  }
-}
-
-inline
-void
-ParseResult::add_to_option(const std::string& option, const std::string& arg)
-{
-  auto iter = m_options->find(option);
-
-  if (iter == m_options->end())
-  {
-    throw option_not_exists_exception(option);
-  }
-
-  parse_option(iter->second, option, arg);
-}
-
-inline
-bool
-ParseResult::consume_positional(std::string a)
-{
-  while (m_next_positional != m_positional.end())
-  {
-    auto iter = m_options->find(*m_next_positional);
-    if (iter != m_options->end())
-    {
-      auto& result = m_results[iter->second];
-      if (!iter->second->value().is_container())
-      {
-        if (result.count() == 0)
-        {
-          add_to_option(*m_next_positional, a);
-          ++m_next_positional;
-          return true;
-        }
-        else
-        {
-          ++m_next_positional;
-          continue;
-        }
-      }
-      else
-      {
-        add_to_option(*m_next_positional, a);
-        return true;
-      }
-    }
-    else
-    {
-      throw option_not_exists_exception(*m_next_positional);
-    }
-  }
-
-  return false;
-}
-
-inline
-void
-Options::parse_positional(std::string option)
-{
-  parse_positional(std::vector<std::string>{std::move(option)});
-}
-
-inline
-void
-Options::parse_positional(std::vector<std::string> options)
-{
-  m_positional = std::move(options);
-  m_next_positional = m_positional.begin();
-
-  m_positional_set.insert(m_positional.begin(), m_positional.end());
-}
-
-inline
-void
-Options::parse_positional(std::initializer_list<std::string> options)
-{
-  parse_positional(std::vector<std::string>(std::move(options)));
-}
-
-inline
-ParseResult
-Options::parse(int& argc, char**& argv)
-{
-  ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);
-  return result;
-}
-
-inline
-void
-ParseResult::parse(int& argc, char**& argv)
-{
-  int current = 1;
-
-  int nextKeep = 1;
-
-  bool consume_remaining = false;
-
-  while (current != argc)
-  {
-    if (strcmp(argv[current], "--") == 0)
-    {
-      consume_remaining = true;
-      ++current;
-      break;
-    }
-
-    std::match_results<const char*> result;
-    std::regex_match(argv[current], result, option_matcher);
-
-    if (result.empty())
-    {
-      //not a flag
-
-      // but if it starts with a `-`, then it's an error
-      if (argv[current][0] == '-' && argv[current][1] != '\0') {
-        if (!m_allow_unrecognised) {
-          throw option_syntax_exception(argv[current]);
-        }
-      }
-
-      //if true is returned here then it was consumed, otherwise it is
-      //ignored
-      if (consume_positional(argv[current]))
-      {
-      }
-      else
-      {
-        argv[nextKeep] = argv[current];
-        ++nextKeep;
-      }
-      //if we return from here then it was parsed successfully, so continue
-    }
-    else
-    {
-      //short or long option?
-      if (result[4].length() != 0)
-      {
-        const std::string& s = result[4];
-
-        for (std::size_t i = 0; i != s.size(); ++i)
-        {
-          std::string name(1, s[i]);
-          auto iter = m_options->find(name);
-
-          if (iter == m_options->end())
-          {
-            if (m_allow_unrecognised)
-            {
-              continue;
-            }
-            else
-            {
-              //error
-              throw option_not_exists_exception(name);
-            }
-          }
-
-          auto value = iter->second;
-
-          if (i + 1 == s.size())
-          {
-            //it must be the last argument
-            checked_parse_arg(argc, argv, current, value, name);
-          }
-          else if (value->value().has_implicit())
-          {
-            parse_option(value, name, value->value().get_implicit_value());
-          }
-          else
-          {
-            //error
-            throw option_requires_argument_exception(name);
-          }
-        }
-      }
-      else if (result[1].length() != 0)
-      {
-        const std::string& name = result[1];
-
-        auto iter = m_options->find(name);
-
-        if (iter == m_options->end())
-        {
-          if (m_allow_unrecognised)
-          {
-            // keep unrecognised options in argument list, skip to next argument
-            argv[nextKeep] = argv[current];
-            ++nextKeep;
-            ++current;
-            continue;
-          }
-          else
-          {
-            //error
-            throw option_not_exists_exception(name);
-          }
-        }
-
-        auto opt = iter->second;
-
-        //equals provided for long option?
-        if (result[2].length() != 0)
-        {
-          //parse the option given
-
-          parse_option(opt, name, result[3]);
-        }
-        else
-        {
-          //parse the next argument
-          checked_parse_arg(argc, argv, current, opt, name);
-        }
-      }
-
-    }
-
-    ++current;
-  }
-
-  for (auto& opt : *m_options)
-  {
-    auto& detail = opt.second;
-    auto& value = detail->value();
-
-    auto& store = m_results[detail];
-
-    if(value.has_default() && !store.count() && !store.has_default()){
-      parse_default(detail);
-    }
-  }
-
-  if (consume_remaining)
-  {
-    while (current < argc)
-    {
-      if (!consume_positional(argv[current])) {
-        break;
-      }
-      ++current;
-    }
-
-    //adjust argv for any that couldn't be swallowed
-    while (current != argc) {
-      argv[nextKeep] = argv[current];
-      ++nextKeep;
-      ++current;
-    }
-  }
-
-  argc = nextKeep;
-
-}
-
-inline
-void
-Options::add_option
-(
-  const std::string& group,
-  const std::string& s,
-  const std::string& l,
-  std::string desc,
-  std::shared_ptr<const Value> value,
-  std::string arg_help
-)
-{
-  auto stringDesc = toLocalString(std::move(desc));
-  auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
-
-  if (s.size() > 0)
-  {
-    add_one_option(s, option);
-  }
-
-  if (l.size() > 0)
-  {
-    add_one_option(l, option);
-  }
-
-  //add the help details
-  auto& options = m_help[group];
-
-  options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
-      value->has_default(), value->get_default_value(),
-      value->has_implicit(), value->get_implicit_value(),
-      std::move(arg_help),
-      value->is_container(),
-      value->is_boolean()});
-}
-
-inline
-void
-Options::add_one_option
-(
-  const std::string& option,
-  std::shared_ptr<OptionDetails> details
-)
-{
-  auto in = m_options->emplace(option, details);
-
-  if (!in.second)
-  {
-    throw option_exists_error(option);
-  }
-}
-
-inline
-String
-Options::help_one_group(const std::string& g) const
-{
-  typedef std::vector<std::pair<String, String>> OptionHelp;
-
-  auto group = m_help.find(g);
-  if (group == m_help.end())
-  {
-    return "";
-  }
-
-  OptionHelp format;
-
-  size_t longest = 0;
-
-  String result;
-
-  if (!g.empty())
-  {
-    result += toLocalString(" " + g + " options:\n");
-  }
-
-  for (const auto& o : group->second.options)
-  {
-    if (m_positional_set.find(o.l) != m_positional_set.end() &&
-        !m_show_positional)
-    {
-      continue;
-    }
-
-    auto s = format_option(o);
-    longest = (std::max)(longest, stringLength(s));
-    format.push_back(std::make_pair(s, String()));
-  }
-
-  longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST));
-
-  //widest allowed description
-  auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
-
-  auto fiter = format.begin();
-  for (const auto& o : group->second.options)
-  {
-    if (m_positional_set.find(o.l) != m_positional_set.end() &&
-        !m_show_positional)
-    {
-      continue;
-    }
-
-    auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
-
-    result += fiter->first;
-    if (stringLength(fiter->first) > longest)
-    {
-      result += '\n';
-      result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
-    }
-    else
-    {
-      result += toLocalString(std::string(longest + OPTION_DESC_GAP -
-        stringLength(fiter->first),
-        ' '));
-    }
-    result += d;
-    result += '\n';
-
-    ++fiter;
-  }
-
-  return result;
-}
-
-inline
-void
-Options::generate_group_help
-(
-  String& result,
-  const std::vector<std::string>& print_groups
-) const
-{
-  for (size_t i = 0; i != print_groups.size(); ++i)
-  {
-    const String& group_help_text = help_one_group(print_groups[i]);
-    if (empty(group_help_text))
-    {
-      continue;
-    }
-    result += group_help_text;
-    if (i < print_groups.size() - 1)
-    {
-      result += '\n';
-    }
-  }
-}
-
-inline
-void
-Options::generate_all_groups_help(String& result) const
-{
-  std::vector<std::string> all_groups;
-  all_groups.reserve(m_help.size());
-
-  for (auto& group : m_help)
-  {
-    all_groups.push_back(group.first);
-  }
-
-  generate_group_help(result, all_groups);
-}
-
-inline
-std::string
-Options::help(const std::vector<std::string>& help_groups) const
-{
-  String result = m_help_string + "\nUsage:\n  " +
-    toLocalString(m_program) + " " + toLocalString(m_custom_help);
-
-  if (m_positional.size() > 0 && m_positional_help.size() > 0) {
-    result += " " + toLocalString(m_positional_help);
-  }
-
-  result += "\n\n";
-
-  if (help_groups.size() == 0)
-  {
-    generate_all_groups_help(result);
-  }
-  else
-  {
-    generate_group_help(result, help_groups);
-  }
-
-  return toUTF8String(result);
-}
-
-inline
-const std::vector<std::string>
-Options::groups() const
-{
-  std::vector<std::string> g;
-
-  std::transform(
-    m_help.begin(),
-    m_help.end(),
-    std::back_inserter(g),
-    [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
-    {
-      return pair.first;
-    }
-  );
-
-  return g;
-}
-
-inline
-const HelpGroupDetails&
-Options::group_help(const std::string& group) const
-{
-  return m_help.at(group);
-}
-
-}
-
-#endif //CXXOPTS_HPP_INCLUDED
diff --git a/example/hello.c b/example/hello.c
deleted file mode 100644
index b24ebfe..0000000
--- a/example/hello.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * minimal example filesystem using high-level API
- *
- * Compile with:
- *
- *     gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello
- *
- * ## Source code ##
- * \include hello.c
- */
-
-
-#define FUSE_USE_VERSION 31
-
-#include <fuse.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <assert.h>
-
-/*
- * Command line options
- *
- * We can't set default values for the char* fields here because
- * fuse_opt_parse would attempt to free() them when the user specifies
- * different values on the command line.
- */
-static struct options {
-	const char *filename;
-	const char *contents;
-	int show_help;
-} options;
-
-#define OPTION(t, p)                           \
-    { t, offsetof(struct options, p), 1 }
-static const struct fuse_opt option_spec[] = {
-	OPTION("--name=%s", filename),
-	OPTION("--contents=%s", contents),
-	OPTION("-h", show_help),
-	OPTION("--help", show_help),
-	FUSE_OPT_END
-};
-
-static void *hello_init(struct fuse_conn_info *conn,
-			struct fuse_config *cfg)
-{
-	(void) conn;
-	cfg->kernel_cache = 1;
-	return NULL;
-}
-
-static int hello_getattr(const char *path, struct stat *stbuf,
-			 struct fuse_file_info *fi)
-{
-	(void) fi;
-	int res = 0;
-
-	memset(stbuf, 0, sizeof(struct stat));
-	if (strcmp(path, "/") == 0) {
-		stbuf->st_mode = S_IFDIR | 0755;
-		stbuf->st_nlink = 2;
-	} else if (strcmp(path+1, options.filename) == 0) {
-		stbuf->st_mode = S_IFREG | 0444;
-		stbuf->st_nlink = 1;
-		stbuf->st_size = strlen(options.contents);
-	} else
-		res = -ENOENT;
-
-	return res;
-}
-
-static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-			 off_t offset, struct fuse_file_info *fi,
-			 enum fuse_readdir_flags flags)
-{
-	(void) offset;
-	(void) fi;
-	(void) flags;
-
-	if (strcmp(path, "/") != 0)
-		return -ENOENT;
-
-	filler(buf, ".", NULL, 0, 0);
-	filler(buf, "..", NULL, 0, 0);
-	filler(buf, options.filename, NULL, 0, 0);
-
-	return 0;
-}
-
-static int hello_open(const char *path, struct fuse_file_info *fi)
-{
-	if (strcmp(path+1, options.filename) != 0)
-		return -ENOENT;
-
-	if ((fi->flags & O_ACCMODE) != O_RDONLY)
-		return -EACCES;
-
-	return 0;
-}
-
-static int hello_read(const char *path, char *buf, size_t size, off_t offset,
-		      struct fuse_file_info *fi)
-{
-	size_t len;
-	(void) fi;
-	if(strcmp(path+1, options.filename) != 0)
-		return -ENOENT;
-
-	len = strlen(options.contents);
-	if (offset < len) {
-		if (offset + size > len)
-			size = len - offset;
-		memcpy(buf, options.contents + offset, size);
-	} else
-		size = 0;
-
-	return size;
-}
-
-static const struct fuse_operations hello_oper = {
-	.init           = hello_init,
-	.getattr	= hello_getattr,
-	.readdir	= hello_readdir,
-	.open		= hello_open,
-	.read		= hello_read,
-};
-
-static void show_help(const char *progname)
-{
-	printf("usage: %s [options] <mountpoint>\n\n", progname);
-	printf("File-system specific options:\n"
-	       "    --name=<s>          Name of the \"hello\" file\n"
-	       "                        (default: \"hello\")\n"
-	       "    --contents=<s>      Contents \"hello\" file\n"
-	       "                        (default \"Hello, World!\\n\")\n"
-	       "\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int ret;
-	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-
-	/* Set defaults -- we have to use strdup so that
-	   fuse_opt_parse can free the defaults if other
-	   values are specified */
-	options.filename = strdup("hello");
-	options.contents = strdup("Hello World!\n");
-
-	/* Parse options */
-	if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
-		return 1;
-
-	/* When --help is specified, first print our own file-system
-	   specific help text, then signal fuse_main to show
-	   additional help (by adding `--help` to the options again)
-	   without usage: line (by setting argv[0] to the empty
-	   string) */
-	if (options.show_help) {
-		show_help(argv[0]);
-		assert(fuse_opt_add_arg(&args, "--help") == 0);
-		args.argv[0][0] = '\0';
-	}
-
-	ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
-	fuse_opt_free_args(&args);
-	return ret;
-}
diff --git a/example/hello_ll.c b/example/hello_ll.c
deleted file mode 100644
index 1db5eff..0000000
--- a/example/hello_ll.c
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * minimal example filesystem using low-level API
- *
- * Compile with:
- *
- *     gcc -Wall hello_ll.c `pkg-config fuse3 --cflags --libs` -o hello_ll
- *
- * ## Source code ##
- * \include hello_ll.c
- */
-
-#define FUSE_USE_VERSION 34
-
-#include <fuse_lowlevel.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <assert.h>
-
-static const char *hello_str = "Hello World!\n";
-static const char *hello_name = "hello";
-
-static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
-{
-	stbuf->st_ino = ino;
-	switch (ino) {
-	case 1:
-		stbuf->st_mode = S_IFDIR | 0755;
-		stbuf->st_nlink = 2;
-		break;
-
-	case 2:
-		stbuf->st_mode = S_IFREG | 0444;
-		stbuf->st_nlink = 1;
-		stbuf->st_size = strlen(hello_str);
-		break;
-
-	default:
-		return -1;
-	}
-	return 0;
-}
-
-static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
-			     struct fuse_file_info *fi)
-{
-	struct stat stbuf;
-
-	(void) fi;
-
-	memset(&stbuf, 0, sizeof(stbuf));
-	if (hello_stat(ino, &stbuf) == -1)
-		fuse_reply_err(req, ENOENT);
-	else
-		fuse_reply_attr(req, &stbuf, 1.0);
-}
-
-static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
-{
-	struct fuse_entry_param e;
-
-	if (parent != 1 || strcmp(name, hello_name) != 0)
-		fuse_reply_err(req, ENOENT);
-	else {
-		memset(&e, 0, sizeof(e));
-		e.ino = 2;
-		e.attr_timeout = 1.0;
-		e.entry_timeout = 1.0;
-		hello_stat(e.ino, &e.attr);
-
-		fuse_reply_entry(req, &e);
-	}
-}
-
-struct dirbuf {
-	char *p;
-	size_t size;
-};
-
-static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
-		       fuse_ino_t ino)
-{
-	struct stat stbuf;
-	size_t oldsize = b->size;
-	b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
-	b->p = (char *) realloc(b->p, b->size);
-	memset(&stbuf, 0, sizeof(stbuf));
-	stbuf.st_ino = ino;
-	fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
-			  b->size);
-}
-
-#define min(x, y) ((x) < (y) ? (x) : (y))
-
-static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
-			     off_t off, size_t maxsize)
-{
-	if (off < bufsize)
-		return fuse_reply_buf(req, buf + off,
-				      min(bufsize - off, maxsize));
-	else
-		return fuse_reply_buf(req, NULL, 0);
-}
-
-static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
-			     off_t off, struct fuse_file_info *fi)
-{
-	(void) fi;
-
-	if (ino != 1)
-		fuse_reply_err(req, ENOTDIR);
-	else {
-		struct dirbuf b;
-
-		memset(&b, 0, sizeof(b));
-		dirbuf_add(req, &b, ".", 1);
-		dirbuf_add(req, &b, "..", 1);
-		dirbuf_add(req, &b, hello_name, 2);
-		reply_buf_limited(req, b.p, b.size, off, size);
-		free(b.p);
-	}
-}
-
-static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
-			  struct fuse_file_info *fi)
-{
-	if (ino != 2)
-		fuse_reply_err(req, EISDIR);
-	else if ((fi->flags & O_ACCMODE) != O_RDONLY)
-		fuse_reply_err(req, EACCES);
-	else
-		fuse_reply_open(req, fi);
-}
-
-static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
-			  off_t off, struct fuse_file_info *fi)
-{
-	(void) fi;
-
-	assert(ino == 2);
-	reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
-}
-
-static const struct fuse_lowlevel_ops hello_ll_oper = {
-	.lookup		= hello_ll_lookup,
-	.getattr	= hello_ll_getattr,
-	.readdir	= hello_ll_readdir,
-	.open		= hello_ll_open,
-	.read		= hello_ll_read,
-};
-
-int main(int argc, char *argv[])
-{
-	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-	struct fuse_session *se;
-	struct fuse_cmdline_opts opts;
-	struct fuse_loop_config config;
-	int ret = -1;
-
-	if (fuse_parse_cmdline(&args, &opts) != 0)
-		return 1;
-	if (opts.show_help) {
-		printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
-		fuse_cmdline_help();
-		fuse_lowlevel_help();
-		ret = 0;
-		goto err_out1;
-	} else if (opts.show_version) {
-		printf("FUSE library version %s\n", fuse_pkgversion());
-		fuse_lowlevel_version();
-		ret = 0;
-		goto err_out1;
-	}
-
-	if(opts.mountpoint == NULL) {
-		printf("usage: %s [options] <mountpoint>\n", argv[0]);
-		printf("       %s --help\n", argv[0]);
-		ret = 1;
-		goto err_out1;
-	}
-
-	se = fuse_session_new(&args, &hello_ll_oper,
-			      sizeof(hello_ll_oper), NULL);
-	if (se == NULL)
-	    goto err_out1;
-
-	if (fuse_set_signal_handlers(se) != 0)
-	    goto err_out2;
-
-	if (fuse_session_mount(se, opts.mountpoint) != 0)
-	    goto err_out3;
-
-	fuse_daemonize(opts.foreground);
-
-	/* Block until ctrl+c or fusermount -u */
-	if (opts.singlethread)
-		ret = fuse_session_loop(se);
-	else {
-		config.clone_fd = opts.clone_fd;
-		config.max_idle_threads = opts.max_idle_threads;
-		ret = fuse_session_loop_mt(se, &config);
-	}
-
-	fuse_session_unmount(se);
-err_out3:
-	fuse_remove_signal_handlers(se);
-err_out2:
-	fuse_session_destroy(se);
-err_out1:
-	free(opts.mountpoint);
-	fuse_opt_free_args(&args);
-
-	return ret ? 1 : 0;
-}
diff --git a/example/invalidate_path.c b/example/invalidate_path.c
deleted file mode 100644
index 9159b2f..0000000
--- a/example/invalidate_path.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
-            (C) 2017 EditShare LLC <slawek.rudnicki@editshare.com>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
- */
-
-/** @file
- *
- * This example implements a file system with two files:
- *   * 'current-time', whose contents change dynamically:
- *     it always contains the current time (same as in
- *     notify_inval_inode.c).
- *   * 'growing', whose size changes dynamically, growing
- *     by 1 byte after each update. This aims to check
- *     if cached file metadata is also invalidated.
- *
- * ## Compilation ##
- *
- *     gcc -Wall invalidate_path.c `pkg-config fuse3 --cflags --libs` -o invalidate_path
- *
- * ## Source code ##
- * \include invalidate_path.c
- */
-
-#define FUSE_USE_VERSION 34
-
-#include <fuse.h>
-#include <fuse_lowlevel.h>  /* for fuse_cmdline_opts */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <pthread.h>
-
-/* We can't actually tell the kernel that there is no
-   timeout, so we just send a big value */
-#define NO_TIMEOUT 500000
-
-#define MAX_STR_LEN 128
-#define TIME_FILE_NAME "current_time"
-#define TIME_FILE_INO 2
-#define GROW_FILE_NAME "growing"
-#define GROW_FILE_INO 3
-
-static char time_file_contents[MAX_STR_LEN];
-static size_t grow_file_size;
-
-/* Command line parsing */
-struct options {
-	int no_notify;
-	int update_interval;
-};
-static struct options options = {
-		.no_notify = 0,
-		.update_interval = 1,
-};
-
-#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
-static const struct fuse_opt option_spec[] = {
-		OPTION("--no-notify", no_notify),
-		OPTION("--update-interval=%d", update_interval),
-		FUSE_OPT_END
-};
-
-static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
-{
-	(void) conn;
-	cfg->entry_timeout = NO_TIMEOUT;
-	cfg->attr_timeout = NO_TIMEOUT;
-	cfg->negative_timeout = 0;
-
-	return NULL;
-}
-
-static int xmp_getattr(const char *path,
-		struct stat *stbuf, struct fuse_file_info* fi) {
-	(void) fi;
-	if (strcmp(path, "/") == 0) {
-		stbuf->st_ino = 1;
-		stbuf->st_mode = S_IFDIR | 0755;
-		stbuf->st_nlink = 1;
-	} else if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
-		stbuf->st_ino = TIME_FILE_INO;
-		stbuf->st_mode = S_IFREG | 0444;
-		stbuf->st_nlink = 1;
-		stbuf->st_size = strlen(time_file_contents);
-	} else if (strcmp(path, "/" GROW_FILE_NAME) == 0) {
-		stbuf->st_ino = GROW_FILE_INO;
-		stbuf->st_mode = S_IFREG | 0444;
-		stbuf->st_nlink = 1;
-		stbuf->st_size = grow_file_size;
-	} else {
-		return -ENOENT;
-	}
-
-	return 0;
-}
-
-static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-		off_t offset, struct fuse_file_info *fi,
-		enum fuse_readdir_flags flags) {
-	(void) fi;
-	(void) offset;
-	(void) flags;
-	if (strcmp(path, "/") != 0) {
-		return -ENOTDIR;
-	} else {
-		(void) filler;
-		(void) buf;
-		struct stat file_stat;
-		xmp_getattr("/" TIME_FILE_NAME, &file_stat, NULL);
-		filler(buf, TIME_FILE_NAME, &file_stat, 0, 0);
-		xmp_getattr("/" GROW_FILE_NAME, &file_stat, NULL);
-		filler(buf, GROW_FILE_NAME, &file_stat, 0, 0);
-		return 0;
-	}
-}
-
-static int xmp_open(const char *path, struct fuse_file_info *fi) {
-	(void) path;
-	/* Make cache persistent even if file is closed,
-       this makes it easier to see the effects */
-	fi->keep_cache = 1;
-	return 0;
-}
-
-static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
-		struct fuse_file_info *fi) {
-	(void) fi;
-	(void) offset;
-	if (strcmp(path, "/" TIME_FILE_NAME) == 0) {
-		int file_length = strlen(time_file_contents);
-		int to_copy = offset + size <= file_length
-				? size
-						: file_length - offset;
-		memcpy(buf, time_file_contents, to_copy);
-		return to_copy;
-	} else {
-		assert(strcmp(path, "/" GROW_FILE_NAME) == 0);
-		int to_copy = offset + size <= grow_file_size
-				? size
-						: grow_file_size - offset;
-		memset(buf, 'x', to_copy);
-		return to_copy;
-	}
-}
-
-static const struct fuse_operations xmp_oper = {
-		.init     = xmp_init,
-		.getattr  = xmp_getattr,
-		.readdir  = xmp_readdir,
-		.open     = xmp_open,
-		.read     = xmp_read,
-};
-
-static void update_fs(void) {
-	static int count = 0;
-	struct tm *now;
-	time_t t;
-	t = time(NULL);
-	now = localtime(&t);
-	assert(now != NULL);
-
-	int time_file_size = strftime(time_file_contents, MAX_STR_LEN,
-			"The current time is %H:%M:%S\n", now);
-	assert(time_file_size != 0);
-
-	grow_file_size = count++;
-}
-
-static int invalidate(struct fuse *fuse, const char *path) {
-	int status = fuse_invalidate_path(fuse, path);
-	if (status == -ENOENT) {
-		return 0;
-	} else {
-		return status;
-	}
-}
-
-static void* update_fs_loop(void *data) {
-	struct fuse *fuse = (struct fuse*) data;
-
-	while (1) {
-		update_fs();
-		if (!options.no_notify) {
-			assert(invalidate(fuse, "/" TIME_FILE_NAME) == 0);
-			assert(invalidate(fuse, "/" GROW_FILE_NAME) == 0);
-		}
-		sleep(options.update_interval);
-	}
-	return NULL;
-}
-
-static void show_help(const char *progname)
-{
-	printf("usage: %s [options] <mountpoint>\n\n", progname);
-	printf("File-system specific options:\n"
-			"    --update-interval=<secs>  Update-rate of file system contents\n"
-			"    --no-notify            Disable kernel notifications\n"
-			"\n");
-}
-
-int main(int argc, char *argv[]) {
-	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-	struct fuse *fuse;
-	struct fuse_cmdline_opts opts;
-	struct fuse_loop_config config;
-	int res;
-
-	/* Initialize the files */
-	update_fs();
-
-	if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
-		return 1;
-
-	if (fuse_parse_cmdline(&args, &opts) != 0)
-		return 1;
-
-	if (opts.show_version) {
-		printf("FUSE library version %s\n", fuse_pkgversion());
-		fuse_lowlevel_version();
-		res = 0;
-		goto out1;
-	} else if (opts.show_help) {
-		show_help(argv[0]);
-		fuse_cmdline_help();
-		fuse_lib_help(&args);
-		res = 0;
-		goto out1;
-	} else if (!opts.mountpoint) {
-		fprintf(stderr, "error: no mountpoint specified\n");
-		res = 1;
-		goto out1;
-	}
-
-	fuse = fuse_new(&args, &xmp_oper, sizeof(xmp_oper), NULL);
-	if (fuse == NULL) {
-		res = 1;
-		goto out1;
-	}
-
-	if (fuse_mount(fuse,opts.mountpoint) != 0) {
-		res = 1;
-		goto out2;
-	}
-
-	if (fuse_daemonize(opts.foreground) != 0) {
-		res = 1;
-		goto out3;
-	}
-
-	pthread_t updater;     /* Start thread to update file contents */
-	int ret = pthread_create(&updater, NULL, update_fs_loop, (void *) fuse);
-	if (ret != 0) {
-		fprintf(stderr, "pthread_create failed with %s\n", strerror(ret));
-		return 1;
-	};
-
-	struct fuse_session *se = fuse_get_session(fuse);
-	if (fuse_set_signal_handlers(se) != 0) {
-		res = 1;
-		goto out3;
-	}
-
-	if (opts.singlethread)
-		res = fuse_loop(fuse);
-	else {
-		config.clone_fd = opts.clone_fd;
-		config.max_idle_threads = opts.max_idle_threads;
-		res = fuse_loop_mt(fuse, &config);
-	}
-	if (res)
-		res = 1;
-
-	fuse_remove_signal_handlers(se);
-out3:
-	fuse_unmount(fuse);
-out2:
-	fuse_destroy(fuse);
-out1:
-	free(opts.mountpoint);
-	fuse_opt_free_args(&args);
-	return res;
-}
diff --git a/example/ioctl.c b/example/ioctl.c
deleted file mode 100644
index b8dab00..0000000
--- a/example/ioctl.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
-  FUSE fioc: FUSE ioctl example
-  Copyright (C) 2008       SUSE Linux Products GmbH
-  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- * @tableofcontents
- *
- * This example illustrates how to write a FUSE file system that can
- * process (a restricted set of) ioctls. It can be tested with the
- * ioctl_client.c program.
- *
- * Compile with:
- *
- *     gcc -Wall ioctl.c `pkg-config fuse3 --cflags --libs` -o ioctl
- *
- * ## Source code ##
- * \include ioctl.c
- */
-
-#define FUSE_USE_VERSION 35
-
-#include <fuse.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
-
-#include "ioctl.h"
-
-#define FIOC_NAME	"fioc"
-
-enum {
-	FIOC_NONE,
-	FIOC_ROOT,
-	FIOC_FILE,
-};
-
-static void *fioc_buf;
-static size_t fioc_size;
-
-static int fioc_resize(size_t new_size)
-{
-	void *new_buf;
-
-	if (new_size == fioc_size)
-		return 0;
-
-	new_buf = realloc(fioc_buf, new_size);
-	if (!new_buf && new_size)
-		return -ENOMEM;
-
-	if (new_size > fioc_size)
-		memset(new_buf + fioc_size, 0, new_size - fioc_size);
-
-	fioc_buf = new_buf;
-	fioc_size = new_size;
-
-	return 0;
-}
-
-static int fioc_expand(size_t new_size)
-{
-	if (new_size > fioc_size)
-		return fioc_resize(new_size);
-	return 0;
-}
-
-static int fioc_file_type(const char *path)
-{
-	if (strcmp(path, "/") == 0)
-		return FIOC_ROOT;
-	if (strcmp(path, "/" FIOC_NAME) == 0)
-		return FIOC_FILE;
-	return FIOC_NONE;
-}
-
-static int fioc_getattr(const char *path, struct stat *stbuf,
-			struct fuse_file_info *fi)
-{
-	(void) fi;
-	stbuf->st_uid = getuid();
-	stbuf->st_gid = getgid();
-	stbuf->st_atime = stbuf->st_mtime = time(NULL);
-
-	switch (fioc_file_type(path)) {
-	case FIOC_ROOT:
-		stbuf->st_mode = S_IFDIR | 0755;
-		stbuf->st_nlink = 2;
-		break;
-	case FIOC_FILE:
-		stbuf->st_mode = S_IFREG | 0644;
-		stbuf->st_nlink = 1;
-		stbuf->st_size = fioc_size;
-		break;
-	case FIOC_NONE:
-		return -ENOENT;
-	}
-
-	return 0;
-}
-
-static int fioc_open(const char *path, struct fuse_file_info *fi)
-{
-	(void) fi;
-
-	if (fioc_file_type(path) != FIOC_NONE)
-		return 0;
-	return -ENOENT;
-}
-
-static int fioc_do_read(char *buf, size_t size, off_t offset)
-{
-	if (offset >= fioc_size)
-		return 0;
-
-	if (size > fioc_size - offset)
-		size = fioc_size - offset;
-
-	memcpy(buf, fioc_buf + offset, size);
-
-	return size;
-}
-
-static int fioc_read(const char *path, char *buf, size_t size,
-		     off_t offset, struct fuse_file_info *fi)
-{
-	(void) fi;
-
-	if (fioc_file_type(path) != FIOC_FILE)
-		return -EINVAL;
-
-	return fioc_do_read(buf, size, offset);
-}
-
-static int fioc_do_write(const char *buf, size_t size, off_t offset)
-{
-	if (fioc_expand(offset + size))
-		return -ENOMEM;
-
-	memcpy(fioc_buf + offset, buf, size);
-
-	return size;
-}
-
-static int fioc_write(const char *path, const char *buf, size_t size,
-		      off_t offset, struct fuse_file_info *fi)
-{
-	(void) fi;
-
-	if (fioc_file_type(path) != FIOC_FILE)
-		return -EINVAL;
-
-	return fioc_do_write(buf, size, offset);
-}
-
-static int fioc_truncate(const char *path, off_t size,
-			 struct fuse_file_info *fi)
-{
-	(void) fi;
-	if (fioc_file_type(path) != FIOC_FILE)
-		return -EINVAL;
-
-	return fioc_resize(size);
-}
-
-static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-			off_t offset, struct fuse_file_info *fi,
-			enum fuse_readdir_flags flags)
-{
-	(void) fi;
-	(void) offset;
-	(void) flags;
-
-	if (fioc_file_type(path) != FIOC_ROOT)
-		return -ENOENT;
-
-	filler(buf, ".", NULL, 0, 0);
-	filler(buf, "..", NULL, 0, 0);
-	filler(buf, FIOC_NAME, NULL, 0, 0);
-
-	return 0;
-}
-
-static int fioc_ioctl(const char *path, unsigned int cmd, void *arg,
-		      struct fuse_file_info *fi, unsigned int flags, void *data)
-{
-	(void) arg;
-	(void) fi;
-	(void) flags;
-
-	if (fioc_file_type(path) != FIOC_FILE)
-		return -EINVAL;
-
-	if (flags & FUSE_IOCTL_COMPAT)
-		return -ENOSYS;
-
-	switch (cmd) {
-	case FIOC_GET_SIZE:
-		*(size_t *)data = fioc_size;
-		return 0;
-
-	case FIOC_SET_SIZE:
-		fioc_resize(*(size_t *)data);
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-static const struct fuse_operations fioc_oper = {
-	.getattr	= fioc_getattr,
-	.readdir	= fioc_readdir,
-	.truncate	= fioc_truncate,
-	.open		= fioc_open,
-	.read		= fioc_read,
-	.write		= fioc_write,
-	.ioctl		= fioc_ioctl,
-};
-
-int main(int argc, char *argv[])
-{
-	return fuse_main(argc, argv, &fioc_oper, NULL);
-}
diff --git a/example/ioctl.h b/example/ioctl.h
deleted file mode 100644
index a4f054c..0000000
--- a/example/ioctl.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-  FUSE-ioctl: ioctl support for FUSE
-  Copyright (C) 2008       SUSE Linux Products GmbH
-  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- * @tableofcontents
- *
- * Header file to share definitions between the ioctl.c example file
- * system and the ioctl_client.c test program.
- *
- * \include ioctl.h
- */
-
-
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/ioctl.h>
-
-enum {
-	FIOC_GET_SIZE	= _IOR('E', 0, size_t),
-	FIOC_SET_SIZE	= _IOW('E', 1, size_t),
-
-	/*
-	 * The following two ioctls don't follow usual encoding rules
-	 * and transfer variable amount of data.
-	 */
-	FIOC_READ	= _IO('E', 2),
-	FIOC_WRITE	= _IO('E', 3),
-};
-
-struct fioc_rw_arg {
-	off_t		offset;
-	void		*buf;
-	size_t		size;
-	size_t		prev_size;	/* out param for previous total size */
-	size_t		new_size;	/* out param for new total size */
-};
diff --git a/example/ioctl_client.c b/example/ioctl_client.c
deleted file mode 100644
index 019d030..0000000
--- a/example/ioctl_client.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-  FUSE fioclient: FUSE ioctl example client
-  Copyright (C) 2008       SUSE Linux Products GmbH
-  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
-
-  This program tests the ioctl.c example file systsem.
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This program tests the ioctl.c example file systsem.
- *
- * Compile with:
- *
- *     gcc -Wall ioctl_client.c -o ioctl_client
- *
- * ## Source code ##
- * \include ioctl_client.c
- */
-
-#include <sys/types.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <errno.h>
-#include <unistd.h>
-#include "ioctl.h"
-
-const char *usage =
-"Usage: fioclient FIOC_FILE [size]\n"
-"\n"
-"Get size if <size> is omitted, set size otherwise\n"
-"\n";
-
-int main(int argc, char **argv)
-{
-	size_t size;
-	int fd;
-	int ret = 0;
-
-	if (argc < 2) {
-		fprintf(stderr, "%s", usage);
-		return 1;
-	}
-
-	fd = open(argv[1], O_RDWR);
-	if (fd < 0) {
-		perror("open");
-		return 1;
-	}
-
-	if (argc == 2) {
-		if (ioctl(fd, FIOC_GET_SIZE, &size)) {
-			perror("ioctl");
-			ret = 1;
-			goto out;
-		}
-		printf("%zu\n", size);
-	} else {
-		size = strtoul(argv[2], NULL, 0);
-		if (ioctl(fd, FIOC_SET_SIZE, &size)) {
-			perror("ioctl");
-			ret = 1;
-			goto out;
-		}
-	}
-out:
-	close(fd);
-	return ret;
-}
diff --git a/example/meson.build b/example/meson.build
deleted file mode 100644
index 2342df9..0000000
--- a/example/meson.build
+++ /dev/null
@@ -1,40 +0,0 @@
-examples = [ 'passthrough', 'passthrough_fh',
-             'hello', 'hello_ll', 'printcap',
-             'ioctl_client', 'poll_client', 'ioctl',
-             'cuse', 'cuse_client' ]
-
-if not platform.endswith('bsd') and platform != 'dragonfly'
-    examples += 'passthrough_ll'
-
-    # According to Conrad Meyer <cem@freebsd.org>, FreeBSD doesn't
-    # support mounting files, This is enforced in vfs_domount_first()
-    # with the v_type != VDIR check.
-    examples += [ 'null' ]
-endif
-
-threaded_examples = [ 'notify_inval_inode',
-                      'invalidate_path',
-                      'notify_store_retrieve',
-                      'notify_inval_entry',
-                      'poll' ]
-
-foreach ex : examples
-    executable(ex, ex + '.c',
-               dependencies: [ libfuse_dep ],
-               install: false)
-endforeach
-
-
-foreach ex : threaded_examples
-    executable(ex, ex + '.c',
-               dependencies: [ thread_dep, libfuse_dep ],
-               install: false)
-endforeach
-
-if not platform.endswith('bsd') and platform != 'dragonfly' and add_languages('cpp', required : false)
-    executable('passthrough_hp', 'passthrough_hp.cc',
-               dependencies: [ thread_dep, libfuse_dep ],
-               install: false)
-endif
-
-# TODO: Link passthrough_fh with ulockmgr if available
diff --git a/example/notify_inval_entry.c b/example/notify_inval_entry.c
deleted file mode 100644
index 9f50992..0000000
--- a/example/notify_inval_entry.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This example implements a file system with a single file whose
- * file name changes dynamically to reflect the current time.
- *
- * It illustrates the use of the fuse_lowlevel_notify_inval_entry()
- * function.
- *
- * To see the effect, first start the file system with the
- * ``--no-notify``
- *
- *     $ notify_inval_entry --update-interval=1 --timeout 30 --no-notify mnt/
- *
- * Observe that `ls` always prints the correct directory contents
- * (since `readdir` output is not cached)::
- *
- *     $ ls mnt; sleep 1; ls mnt; sleep 1; ls mnt
- *     Time_is_15h_48m_33s  current_time
- *     Time_is_15h_48m_34s  current_time
- *     Time_is_15h_48m_35s  current_time
- *
- * However, if you try to access a file by name the kernel will
- * report that it still exists:
- *
- *     $ file=$(ls mnt/); echo $file
- *     Time_is_15h_50m_09s
- *     $ sleep 5; stat mnt/$file
- *       File: ‘mnt/Time_is_15h_50m_09s’
- *       Size: 32                Blocks: 0          IO Block: 4096   regular file
- *     Device: 2ah/42d	Inode: 3           Links: 1
- *     Access: (0444/-r--r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
- *     Access: 1969-12-31 16:00:00.000000000 -0800
- *     Modify: 1969-12-31 16:00:00.000000000 -0800
- *     Change: 1969-12-31 16:00:00.000000000 -0800
- *      Birth: -
- *
- * Only once the kernel cache timeout has been reached will the stat
- * call fail:
- *
- *     $ sleep 30; stat mnt/$file
- *     stat: cannot stat ‘mnt/Time_is_15h_50m_09s’: No such file or directory
- *
- * In contrast, if you enable notifications you will be unable to stat
- * the file as soon as the file system updates its name:
- *
- *     $ notify_inval_entry --update-interval=1 --timeout 30 --no-notify mnt/
- *     $ file=$(ls mnt/); stat mnt/$file
- *       File: ‘mnt/Time_is_20h_42m_11s’
- *       Size: 0                 Blocks: 0          IO Block: 4096   regular empty file
- *     Device: 2ah/42d	Inode: 2           Links: 1
- *     Access: (0000/----------)  Uid: (    0/    root)   Gid: (    0/    root)
- *     Access: 1969-12-31 16:00:00.000000000 -0800
- *     Modify: 1969-12-31 16:00:00.000000000 -0800
- *     Change: 1969-12-31 16:00:00.000000000 -0800
- *      Birth: -
- *     $ sleep 1; stat mnt/$file
- *     stat: cannot stat ‘mnt/Time_is_20h_42m_11s’: No such file or directory
- *
- * ## Compilation ##
- *
- *     gcc -Wall notify_inval_entry.c `pkg-config fuse3 --cflags --libs` -o notify_inval_entry
- *
- * ## Source code ##
- * \include notify_inval_entry.c
- */
-
-
-#define FUSE_USE_VERSION 34
-
-#include <fuse_lowlevel.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <pthread.h>
-
-#define MAX_STR_LEN 128
-static char file_name[MAX_STR_LEN];
-static fuse_ino_t file_ino = 2;
-static int lookup_cnt = 0;
-
-/* Command line parsing */
-struct options {
-    int no_notify;
-    float timeout;
-    int update_interval;
-};
-static struct options options = {
-    .timeout = 5,
-    .no_notify = 0,
-    .update_interval = 1,
-};
-
-#define OPTION(t, p)                           \
-    { t, offsetof(struct options, p), 1 }
-static const struct fuse_opt option_spec[] = {
-    OPTION("--no-notify", no_notify),
-    OPTION("--update-interval=%d", update_interval),
-    OPTION("--timeout=%f", timeout),
-    FUSE_OPT_END
-};
-
-static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
-    stbuf->st_ino = ino;
-    if (ino == FUSE_ROOT_ID) {
-        stbuf->st_mode = S_IFDIR | 0755;
-        stbuf->st_nlink = 1;
-    }
-
-    else if (ino == file_ino) {
-        stbuf->st_mode = S_IFREG | 0000;
-        stbuf->st_nlink = 1;
-        stbuf->st_size = 0;
-    }
-
-    else
-        return -1;
-
-    return 0;
-}
-
-static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
-                       const char *name) {
-    struct fuse_entry_param e;
-    memset(&e, 0, sizeof(e));
-
-    if (parent != FUSE_ROOT_ID)
-        goto err_out;
-    else if (strcmp(name, file_name) == 0) {
-        e.ino = file_ino;
-        lookup_cnt++;
-    } else
-        goto err_out;
-
-    e.attr_timeout = options.timeout;
-    e.entry_timeout = options.timeout;
-    if (tfs_stat(e.ino, &e.attr) != 0)
-        goto err_out;
-    fuse_reply_entry(req, &e);
-    return;
-
-err_out:
-    fuse_reply_err(req, ENOENT);
-}
-
-static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
-                        uint64_t nlookup) {
-    (void) req;
-    if(ino == file_ino)
-        lookup_cnt -= nlookup;
-    else
-        assert(ino == FUSE_ROOT_ID);
-    fuse_reply_none(req);
-}
-
-static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
-                        struct fuse_file_info *fi) {
-    struct stat stbuf;
-
-    (void) fi;
-
-    memset(&stbuf, 0, sizeof(stbuf));
-    if (tfs_stat(ino, &stbuf) != 0)
-        fuse_reply_err(req, ENOENT);
-    else
-        fuse_reply_attr(req, &stbuf, options.timeout);
-}
-
-struct dirbuf {
-    char *p;
-    size_t size;
-};
-
-static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
-                       fuse_ino_t ino) {
-    struct stat stbuf;
-    size_t oldsize = b->size;
-    b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
-    b->p = (char *) realloc(b->p, b->size);
-    memset(&stbuf, 0, sizeof(stbuf));
-    stbuf.st_ino = ino;
-    fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
-                      b->size);
-}
-
-#define min(x, y) ((x) < (y) ? (x) : (y))
-
-static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
-                             off_t off, size_t maxsize) {
-    if (off < bufsize)
-        return fuse_reply_buf(req, buf + off,
-                              min(bufsize - off, maxsize));
-    else
-        return fuse_reply_buf(req, NULL, 0);
-}
-
-static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
-                        off_t off, struct fuse_file_info *fi) {
-    (void) fi;
-
-    if (ino != FUSE_ROOT_ID)
-        fuse_reply_err(req, ENOTDIR);
-    else {
-        struct dirbuf b;
-
-        memset(&b, 0, sizeof(b));
-        dirbuf_add(req, &b, file_name, file_ino);
-        reply_buf_limited(req, b.p, b.size, off, size);
-        free(b.p);
-    }
-}
-
-static const struct fuse_lowlevel_ops tfs_oper = {
-    .lookup	= tfs_lookup,
-    .getattr	= tfs_getattr,
-    .readdir	= tfs_readdir,
-    .forget     = tfs_forget,
-};
-
-static void update_fs(void) {
-    time_t t;
-    struct tm *now;
-    ssize_t ret;
-
-    t = time(NULL);
-    now = localtime(&t);
-    assert(now != NULL);
-
-    ret = strftime(file_name, MAX_STR_LEN,
-                   "Time_is_%Hh_%Mm_%Ss", now);
-    assert(ret != 0);
-}
-
-static void* update_fs_loop(void *data) {
-    struct fuse_session *se = (struct fuse_session*) data;
-    char *old_name;
-
-    while(1) {
-        old_name = strdup(file_name);
-        update_fs();
-        if (!options.no_notify && lookup_cnt)
-            assert(fuse_lowlevel_notify_inval_entry
-                   (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
-        free(old_name);
-        sleep(options.update_interval);
-    }
-    return NULL;
-}
-
-static void show_help(const char *progname)
-{
-    printf("usage: %s [options] <mountpoint>\n\n", progname);
-    printf("File-system specific options:\n"
-               "    --timeout=<secs>       Timeout for kernel caches\n"
-               "    --update-interval=<secs>  Update-rate of file system contents\n"
-               "    --no-notify            Disable kernel notifications\n"
-               "\n");
-}
-
-int main(int argc, char *argv[]) {
-    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-    struct fuse_session *se;
-    struct fuse_cmdline_opts opts;
-    struct fuse_loop_config config;
-    pthread_t updater;
-    int ret = -1;
-
-    if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
-        return 1;
-
-    if (fuse_parse_cmdline(&args, &opts) != 0)
-        return 1;
-    if (opts.show_help) {
-        show_help(argv[0]);
-        fuse_cmdline_help();
-        fuse_lowlevel_help();
-        ret = 0;
-        goto err_out1;
-    } else if (opts.show_version) {
-        printf("FUSE library version %s\n", fuse_pkgversion());
-        fuse_lowlevel_version();
-        ret = 0;
-        goto err_out1;
-    }
-
-    /* Initial contents */
-    update_fs();
-
-    se = fuse_session_new(&args, &tfs_oper,
-                          sizeof(tfs_oper), NULL);
-    if (se == NULL)
-        goto err_out1;
-
-    if (fuse_set_signal_handlers(se) != 0)
-        goto err_out2;
-
-    if (fuse_session_mount(se, opts.mountpoint) != 0)
-        goto err_out3;
-
-    fuse_daemonize(opts.foreground);
-
-    /* Start thread to update file contents */
-    ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
-    if (ret != 0) {
-        fprintf(stderr, "pthread_create failed with %s\n",
-                strerror(ret));
-        goto err_out3;
-    }
-
-    /* Block until ctrl+c or fusermount -u */
-    if (opts.singlethread)
-        ret = fuse_session_loop(se);
-    else {
-        config.clone_fd = opts.clone_fd;
-        config.max_idle_threads = opts.max_idle_threads;
-        ret = fuse_session_loop_mt(se, &config);
-    }
-
-    fuse_session_unmount(se);
-err_out3:
-    fuse_remove_signal_handlers(se);
-err_out2:
-    fuse_session_destroy(se);
-err_out1:
-    free(opts.mountpoint);
-    fuse_opt_free_args(&args);
-
-    return ret ? 1 : 0;
-}
-
-
-/**
- * Local Variables:
- * mode: c
- * indent-tabs-mode: nil
- * c-basic-offset: 4
- * End:
- */
diff --git a/example/notify_inval_inode.c b/example/notify_inval_inode.c
deleted file mode 100644
index b3b50aa..0000000
--- a/example/notify_inval_inode.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This example implements a file system with a single file whose
- * contents change dynamically: it always contains the current time.
- *
- * While notify_store_retrieve.c uses fuse_lowlevel_notify_store() to
- * actively push the updated data into the kernel cache, this example
- * uses fuse_lowlevel_notify_inval_inode() to notify the kernel that
- * the cache has to be invalidated - but the kernel still has to
- * explicitly request the updated data on the next read.
- *
- * To see the effect, first start the file system with the
- *  ``--no-notify`` option:
- *
- *     $ notify_inval_inode --update-interval=1 --no-notify mnt/
- *
- * Observe that the output never changes, even though the file system
- * updates it once per second. This is because the contents are cached
- * in the kernel:
- *
- *     $ for i in 1 2 3 4 5; do
- *     >     cat mnt/current_time
- *     >     sleep 1
- *     > done
- *     The current time is 15:58:18
- *     The current time is 15:58:18
- *     The current time is 15:58:18
- *     The current time is 15:58:18
- *     The current time is 15:58:18
- *
- * If you instead enable the notification functions, the changes become
- * visible:
- *
- *      $ notify_inval_inode --update-interval=1 mnt/
- *      $ for i in 1 2 3 4 5; do
- *      >     cat mnt/current_time
- *      >     sleep 1
- *      > done
- *      The current time is 15:58:40
- *      The current time is 15:58:41
- *      The current time is 15:58:42
- *      The current time is 15:58:43
- *      The current time is 15:58:44
- *
- * ## Compilation ##
- *
- *     gcc -Wall notify_inval_inode.c `pkg-config fuse3 --cflags --libs` -o notify_inval_inode
- *
- * ## Source code ##
- * \include notify_inval_inode.c
- */
-
-
-#define FUSE_USE_VERSION 34
-
-#include <fuse_lowlevel.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <pthread.h>
-
-/* We can't actually tell the kernel that there is no
-   timeout, so we just send a big value */
-#define NO_TIMEOUT 500000
-
-#define MAX_STR_LEN 128
-#define FILE_INO 2
-#define FILE_NAME "current_time"
-static char file_contents[MAX_STR_LEN];
-static int lookup_cnt = 0;
-static size_t file_size;
-
-/* Command line parsing */
-struct options {
-    int no_notify;
-    int update_interval;
-};
-static struct options options = {
-    .no_notify = 0,
-    .update_interval = 1,
-};
-
-#define OPTION(t, p)                           \
-    { t, offsetof(struct options, p), 1 }
-static const struct fuse_opt option_spec[] = {
-    OPTION("--no-notify", no_notify),
-    OPTION("--update-interval=%d", update_interval),
-    FUSE_OPT_END
-};
-
-static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
-    stbuf->st_ino = ino;
-    if (ino == FUSE_ROOT_ID) {
-        stbuf->st_mode = S_IFDIR | 0755;
-        stbuf->st_nlink = 1;
-    }
-
-    else if (ino == FILE_INO) {
-        stbuf->st_mode = S_IFREG | 0444;
-        stbuf->st_nlink = 1;
-        stbuf->st_size = file_size;
-    }
-
-    else
-        return -1;
-
-    return 0;
-}
-
-static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
-                       const char *name) {
-    struct fuse_entry_param e;
-    memset(&e, 0, sizeof(e));
-
-    if (parent != FUSE_ROOT_ID)
-        goto err_out;
-    else if (strcmp(name, FILE_NAME) == 0) {
-        e.ino = FILE_INO;
-        lookup_cnt++;
-    } else
-        goto err_out;
-
-    e.attr_timeout = NO_TIMEOUT;
-    e.entry_timeout = NO_TIMEOUT;
-    if (tfs_stat(e.ino, &e.attr) != 0)
-        goto err_out;
-    fuse_reply_entry(req, &e);
-    return;
-
-err_out:
-    fuse_reply_err(req, ENOENT);
-}
-
-static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
-                        uint64_t nlookup) {
-    (void) req;
-    if(ino == FILE_INO)
-        lookup_cnt -= nlookup;
-    else
-        assert(ino == FUSE_ROOT_ID);
-    fuse_reply_none(req);
-}
-
-static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
-                        struct fuse_file_info *fi) {
-    struct stat stbuf;
-
-    (void) fi;
-
-    memset(&stbuf, 0, sizeof(stbuf));
-    if (tfs_stat(ino, &stbuf) != 0)
-        fuse_reply_err(req, ENOENT);
-    else
-        fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
-}
-
-struct dirbuf {
-    char *p;
-    size_t size;
-};
-
-static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
-                       fuse_ino_t ino) {
-    struct stat stbuf;
-    size_t oldsize = b->size;
-    b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
-    b->p = (char *) realloc(b->p, b->size);
-    memset(&stbuf, 0, sizeof(stbuf));
-    stbuf.st_ino = ino;
-    fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
-                      b->size);
-}
-
-#define min(x, y) ((x) < (y) ? (x) : (y))
-
-static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
-                             off_t off, size_t maxsize) {
-    if (off < bufsize)
-        return fuse_reply_buf(req, buf + off,
-                              min(bufsize - off, maxsize));
-    else
-        return fuse_reply_buf(req, NULL, 0);
-}
-
-static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
-                        off_t off, struct fuse_file_info *fi) {
-    (void) fi;
-
-    if (ino != FUSE_ROOT_ID)
-        fuse_reply_err(req, ENOTDIR);
-    else {
-        struct dirbuf b;
-
-        memset(&b, 0, sizeof(b));
-        dirbuf_add(req, &b, FILE_NAME, FILE_INO);
-        reply_buf_limited(req, b.p, b.size, off, size);
-        free(b.p);
-    }
-}
-
-static void tfs_open(fuse_req_t req, fuse_ino_t ino,
-                     struct fuse_file_info *fi) {
-
-    /* Make cache persistent even if file is closed,
-       this makes it easier to see the effects */
-    fi->keep_cache = 1;
-
-    if (ino == FUSE_ROOT_ID)
-        fuse_reply_err(req, EISDIR);
-    else if ((fi->flags & O_ACCMODE) != O_RDONLY)
-        fuse_reply_err(req, EACCES);
-    else if (ino == FILE_INO)
-        fuse_reply_open(req, fi);
-    else {
-        // This should not happen
-        fprintf(stderr, "Got open for non-existing inode!\n");
-        fuse_reply_err(req, ENOENT);
-    }
-}
-
-static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
-                     off_t off, struct fuse_file_info *fi) {
-    (void) fi;
-
-    assert(ino == FILE_INO);
-    reply_buf_limited(req, file_contents, file_size, off, size);
-}
-
-static const struct fuse_lowlevel_ops tfs_oper = {
-    .lookup	= tfs_lookup,
-    .getattr	= tfs_getattr,
-    .readdir	= tfs_readdir,
-    .open	= tfs_open,
-    .read	= tfs_read,
-    .forget     = tfs_forget,
-};
-
-static void update_fs(void) {
-    struct tm *now;
-    time_t t;
-    t = time(NULL);
-    now = localtime(&t);
-    assert(now != NULL);
-
-    file_size = strftime(file_contents, MAX_STR_LEN,
-                         "The current time is %H:%M:%S\n", now);
-    assert(file_size != 0);
-}
-
-static void* update_fs_loop(void *data) {
-    struct fuse_session *se = (struct fuse_session*) data;
-
-    while(1) {
-        update_fs();
-        if (!options.no_notify && lookup_cnt) {
-            /* Only send notification if the kernel
-               is aware of the inode */
-            assert(fuse_lowlevel_notify_inval_inode
-                   (se, FILE_INO, 0, 0) == 0);
-        }
-        sleep(options.update_interval);
-    }
-    return NULL;
-}
-
-static void show_help(const char *progname)
-{
-    printf("usage: %s [options] <mountpoint>\n\n", progname);
-    printf("File-system specific options:\n"
-               "    --update-interval=<secs>  Update-rate of file system contents\n"
-               "    --no-notify            Disable kernel notifications\n"
-               "\n");
-}
-
-int main(int argc, char *argv[]) {
-    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-    struct fuse_session *se;
-    struct fuse_cmdline_opts opts;
-    struct fuse_loop_config config;
-    pthread_t updater;
-    int ret = -1;
-
-    if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
-        return 1;
-
-    if (fuse_parse_cmdline(&args, &opts) != 0) {
-        ret = 1;
-        goto err_out1;
-    }
-
-    if (opts.show_help) {
-        show_help(argv[0]);
-        fuse_cmdline_help();
-        fuse_lowlevel_help();
-        ret = 0;
-        goto err_out1;
-    } else if (opts.show_version) {
-        printf("FUSE library version %s\n", fuse_pkgversion());
-        fuse_lowlevel_version();
-        ret = 0;
-        goto err_out1;
-    }
-
-    /* Initial contents */
-    update_fs();
-
-    se = fuse_session_new(&args, &tfs_oper,
-                          sizeof(tfs_oper), NULL);
-    if (se == NULL)
-        goto err_out1;
-
-    if (fuse_set_signal_handlers(se) != 0)
-        goto err_out2;
-
-    if (fuse_session_mount(se, opts.mountpoint) != 0)
-        goto err_out3;
-
-    fuse_daemonize(opts.foreground);
-
-    /* Start thread to update file contents */
-    ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
-    if (ret != 0) {
-        fprintf(stderr, "pthread_create failed with %s\n",
-                strerror(ret));
-        goto err_out3;
-    }
-
-    /* Block until ctrl+c or fusermount -u */
-    if (opts.singlethread)
-        ret = fuse_session_loop(se);
-    else {
-        config.clone_fd = opts.clone_fd;
-        config.max_idle_threads = opts.max_idle_threads;
-        ret = fuse_session_loop_mt(se, &config);
-    }
-
-    fuse_session_unmount(se);
-err_out3:
-    fuse_remove_signal_handlers(se);
-err_out2:
-    fuse_session_destroy(se);
-err_out1:
-    fuse_opt_free_args(&args);
-    free(opts.mountpoint);
-
-    return ret ? 1 : 0;
-}
-
-
-/**
- * Local Variables:
- * mode: c
- * indent-tabs-mode: nil
- * c-basic-offset: 4
- * End:
- */
diff --git a/example/notify_store_retrieve.c b/example/notify_store_retrieve.c
deleted file mode 100644
index 0a165c5..0000000
--- a/example/notify_store_retrieve.c
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- *  This example implements a file system with a single file whose
- *  contents change dynamically: it always contains the current time.
- *
- *  While notify_inval_inode.c uses fuse_lowlevel_notify_inval_inode()
- *  to let the kernel know that it has to invalidate the cache, this
- *  example actively pushes the updated data into the kernel cache
- *  using fuse_lowlevel_notify_store().
- *
- *  To see the effect, first start the file system with the
- *  ``--no-notify`` option:
- *
- *      $ notify_store_retrieve --update-interval=1 --no-notify mnt/
- *
- *  Observe that the output never changes, even though the file system
- *  updates it once per second. This is because the contents are cached
- *  in the kernel:
- *
- *      $ for i in 1 2 3 4 5; do
- *      >     cat mnt/current_time
- *      >     sleep 1
- *      > done
- *      The current time is 15:58:18
- *      The current time is 15:58:18
- *      The current time is 15:58:18
- *      The current time is 15:58:18
- *      The current time is 15:58:18
- *
- *  If you instead enable the notification functions, the changes become
- *  visible:
- *
- *      $ notify_store_retrieve --update-interval=1 mnt/
- *      $ for i in 1 2 3 4 5; do
- *      >     cat mnt/current_time
- *      >     sleep 1
- *      > done
- *      The current time is 15:58:40
- *      The current time is 15:58:41
- *      The current time is 15:58:42
- *      The current time is 15:58:43
- *      The current time is 15:58:44
- *
- * ## Compilation ##
- *
- *     gcc -Wall notify_store_retrieve.c `pkg-config fuse3 --cflags --libs` -o notify_store_retrieve
- *
- * ## Source code ##
- * \include notify_store_retrieve.c
- */
-
-
-#define FUSE_USE_VERSION 34
-
-#include <fuse_lowlevel.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <pthread.h>
-
-/* We can't actually tell the kernel that there is no
-   timeout, so we just send a big value */
-#define NO_TIMEOUT 500000
-
-#define MAX_STR_LEN 128
-#define FILE_INO 2
-#define FILE_NAME "current_time"
-static char file_contents[MAX_STR_LEN];
-static int lookup_cnt = 0;
-static size_t file_size;
-
-/* Keep track if we ever stored data (==1), and
-   received it back correctly (==2) */
-static int retrieve_status = 0;
-
-/* Command line parsing */
-struct options {
-    int no_notify;
-    int update_interval;
-};
-static struct options options = {
-    .no_notify = 0,
-    .update_interval = 1,
-};
-
-#define OPTION(t, p)                           \
-    { t, offsetof(struct options, p), 1 }
-static const struct fuse_opt option_spec[] = {
-    OPTION("--no-notify", no_notify),
-    OPTION("--update-interval=%d", update_interval),
-    FUSE_OPT_END
-};
-
-static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
-    stbuf->st_ino = ino;
-    if (ino == FUSE_ROOT_ID) {
-        stbuf->st_mode = S_IFDIR | 0755;
-        stbuf->st_nlink = 1;
-    }
-
-    else if (ino == FILE_INO) {
-        stbuf->st_mode = S_IFREG | 0444;
-        stbuf->st_nlink = 1;
-        stbuf->st_size = file_size;
-    }
-
-    else
-        return -1;
-
-    return 0;
-}
-
-static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
-                       const char *name) {
-    struct fuse_entry_param e;
-    memset(&e, 0, sizeof(e));
-
-    if (parent != FUSE_ROOT_ID)
-        goto err_out;
-    else if (strcmp(name, FILE_NAME) == 0) {
-        e.ino = FILE_INO;
-        lookup_cnt++;
-    } else
-        goto err_out;
-
-    e.attr_timeout = NO_TIMEOUT;
-    e.entry_timeout = NO_TIMEOUT;
-    if (tfs_stat(e.ino, &e.attr) != 0)
-        goto err_out;
-    fuse_reply_entry(req, &e);
-    return;
-
-err_out:
-    fuse_reply_err(req, ENOENT);
-}
-
-static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
-                        uint64_t nlookup) {
-    (void) req;
-    if(ino == FILE_INO)
-        lookup_cnt -= nlookup;
-    else
-        assert(ino == FUSE_ROOT_ID);
-    fuse_reply_none(req);
-}
-
-static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
-                        struct fuse_file_info *fi) {
-    struct stat stbuf;
-
-    (void) fi;
-
-    memset(&stbuf, 0, sizeof(stbuf));
-    if (tfs_stat(ino, &stbuf) != 0)
-        fuse_reply_err(req, ENOENT);
-    else
-        fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
-}
-
-struct dirbuf {
-    char *p;
-    size_t size;
-};
-
-static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
-                       fuse_ino_t ino) {
-    struct stat stbuf;
-    size_t oldsize = b->size;
-    b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
-    b->p = (char *) realloc(b->p, b->size);
-    memset(&stbuf, 0, sizeof(stbuf));
-    stbuf.st_ino = ino;
-    fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
-                      b->size);
-}
-
-#define min(x, y) ((x) < (y) ? (x) : (y))
-
-static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
-                             off_t off, size_t maxsize) {
-    if (off < bufsize)
-        return fuse_reply_buf(req, buf + off,
-                              min(bufsize - off, maxsize));
-    else
-        return fuse_reply_buf(req, NULL, 0);
-}
-
-static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
-                        off_t off, struct fuse_file_info *fi) {
-    (void) fi;
-
-    if (ino != FUSE_ROOT_ID)
-        fuse_reply_err(req, ENOTDIR);
-    else {
-        struct dirbuf b;
-
-        memset(&b, 0, sizeof(b));
-        dirbuf_add(req, &b, FILE_NAME, FILE_INO);
-        reply_buf_limited(req, b.p, b.size, off, size);
-        free(b.p);
-    }
-}
-
-static void tfs_open(fuse_req_t req, fuse_ino_t ino,
-                     struct fuse_file_info *fi) {
-
-    /* Make cache persistent even if file is closed,
-       this makes it easier to see the effects */
-    fi->keep_cache = 1;
-
-    if (ino == FUSE_ROOT_ID)
-        fuse_reply_err(req, EISDIR);
-    else if ((fi->flags & O_ACCMODE) != O_RDONLY)
-        fuse_reply_err(req, EACCES);
-    else if (ino == FILE_INO)
-        fuse_reply_open(req, fi);
-    else {
-        // This should not happen
-        fprintf(stderr, "Got open for non-existing inode!\n");
-        fuse_reply_err(req, ENOENT);
-    }
-}
-
-static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
-                     off_t off, struct fuse_file_info *fi) {
-    (void) fi;
-
-    assert(ino == FILE_INO);
-    reply_buf_limited(req, file_contents, file_size, off, size);
-}
-
-static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
-                               off_t offset, struct fuse_bufvec *data) {
-    struct fuse_bufvec bufv;
-    char buf[MAX_STR_LEN];
-    char *expected;
-    ssize_t ret;
-
-    assert(ino == FILE_INO);
-    assert(offset == 0);
-    expected = (char*) cookie;
-
-    bufv.count = 1;
-    bufv.idx = 0;
-    bufv.off = 0;
-    bufv.buf[0].size = MAX_STR_LEN;
-    bufv.buf[0].mem = buf;
-    bufv.buf[0].flags = 0;
-
-    ret = fuse_buf_copy(&bufv, data, 0);
-    assert(ret > 0);
-    assert(strncmp(buf, expected, ret) == 0);
-    free(expected);
-    retrieve_status = 2;
-    fuse_reply_none(req);
-}
-
-
-static const struct fuse_lowlevel_ops tfs_oper = {
-    .lookup	= tfs_lookup,
-    .getattr	= tfs_getattr,
-    .readdir	= tfs_readdir,
-    .open	= tfs_open,
-    .read	= tfs_read,
-    .forget     = tfs_forget,
-    .retrieve_reply = tfs_retrieve_reply,
-};
-
-static void update_fs(void) {
-    struct tm *now;
-    time_t t;
-    t = time(NULL);
-    now = localtime(&t);
-    assert(now != NULL);
-
-    file_size = strftime(file_contents, MAX_STR_LEN,
-                         "The current time is %H:%M:%S\n", now);
-    assert(file_size != 0);
-}
-
-static void* update_fs_loop(void *data) {
-    struct fuse_session *se = (struct fuse_session*) data;
-    struct fuse_bufvec bufv;
-    int ret;
-
-    while(1) {
-        update_fs();
-        if (!options.no_notify && lookup_cnt) {
-            /* Only send notification if the kernel
-               is aware of the inode */
-            bufv.count = 1;
-            bufv.idx = 0;
-            bufv.off = 0;
-            bufv.buf[0].size = file_size;
-            bufv.buf[0].mem = file_contents;
-            bufv.buf[0].flags = 0;
-
-            /* This shouldn't fail, but apparently it sometimes
-               does - see https://github.com/libfuse/libfuse/issues/105 */
-            ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
-            if (-ret == ENODEV) {
-                // File system was unmounted
-                break;
-            }
-            else if (ret != 0) {
-                fprintf(stderr, "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
-                        strerror(-ret), -ret);
-                abort();
-            }
-
-            /* To make sure that everything worked correctly, ask the
-               kernel to send us back the stored data */
-            ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
-                                                0, (void*) strdup(file_contents));
-            if (-ret == ENODEV) { // File system was unmounted
-                break;
-            }
-            assert(ret == 0);
-            if(retrieve_status == 0)
-                retrieve_status = 1;
-        }
-        sleep(options.update_interval);
-    }
-    return NULL;
-}
-
-static void show_help(const char *progname)
-{
-    printf("usage: %s [options] <mountpoint>\n\n", progname);
-    printf("File-system specific options:\n"
-               "    --update-interval=<secs>  Update-rate of file system contents\n"
-               "    --no-notify            Disable kernel notifications\n"
-               "\n");
-}
-
-int main(int argc, char *argv[]) {
-    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-    struct fuse_session *se;
-    struct fuse_cmdline_opts opts;
-    struct fuse_loop_config config;
-    pthread_t updater;
-    int ret = -1;
-
-    if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
-        return 1;
-
-    if (fuse_parse_cmdline(&args, &opts) != 0)
-        return 1;
-    if (opts.show_help) {
-        show_help(argv[0]);
-        fuse_cmdline_help();
-        fuse_lowlevel_help();
-        ret = 0;
-        goto err_out1;
-    } else if (opts.show_version) {
-        printf("FUSE library version %s\n", fuse_pkgversion());
-        fuse_lowlevel_version();
-        ret = 0;
-        goto err_out1;
-    }
-
-    /* Initial contents */
-    update_fs();
-
-    se = fuse_session_new(&args, &tfs_oper,
-                          sizeof(tfs_oper), NULL);
-    if (se == NULL)
-        goto err_out1;
-
-    if (fuse_set_signal_handlers(se) != 0)
-        goto err_out2;
-
-    if (fuse_session_mount(se, opts.mountpoint) != 0)
-        goto err_out3;
-
-    fuse_daemonize(opts.foreground);
-
-    /* Start thread to update file contents */
-    ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
-    if (ret != 0) {
-        fprintf(stderr, "pthread_create failed with %s\n",
-                strerror(ret));
-        goto err_out3;
-    }
-
-    /* Block until ctrl+c or fusermount -u */
-    if (opts.singlethread)
-        ret = fuse_session_loop(se);
-    else {
-        config.clone_fd = opts.clone_fd;
-        config.max_idle_threads = opts.max_idle_threads;
-        ret = fuse_session_loop_mt(se, &config);
-    }
-
-    assert(retrieve_status != 1);
-    fuse_session_unmount(se);
-err_out3:
-    fuse_remove_signal_handlers(se);
-err_out2:
-    fuse_session_destroy(se);
-err_out1:
-    free(opts.mountpoint);
-    fuse_opt_free_args(&args);
-
-    return ret ? 1 : 0;
-}
-
-
-/**
- * Local Variables:
- * mode: c
- * indent-tabs-mode: nil
- * c-basic-offset: 4
- * End:
- */
diff --git a/example/null.c b/example/null.c
deleted file mode 100644
index 2dd5e8b..0000000
--- a/example/null.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This "filesystem" provides only a single file. The mountpoint
- * needs to be a file rather than a directory. All writes to the
- * file will be discarded, and reading the file always returns
- * \0.
- *
- * Compile with:
- *
- *     gcc -Wall null.c `pkg-config fuse3 --cflags --libs` -o null
- *
- * ## Source code ##
- * \include passthrough_fh.c
- */
-
-
-#define FUSE_USE_VERSION 31
-
-#include <fuse.h>
-#include <fuse_lowlevel.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
-
-static int null_getattr(const char *path, struct stat *stbuf,
-			struct fuse_file_info *fi)
-{
-	(void) fi;
-
-	if(strcmp(path, "/") != 0)
-		return -ENOENT;
-
-	stbuf->st_mode = S_IFREG | 0644;
-	stbuf->st_nlink = 1;
-	stbuf->st_uid = getuid();
-	stbuf->st_gid = getgid();
-	stbuf->st_size = (1ULL << 32); /* 4G */
-	stbuf->st_blocks = 0;
-	stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
-
-	return 0;
-}
-
-static int null_truncate(const char *path, off_t size,
-			 struct fuse_file_info *fi)
-{
-	(void) size;
-	(void) fi;
-
-	if(strcmp(path, "/") != 0)
-		return -ENOENT;
-
-	return 0;
-}
-
-static int null_open(const char *path, struct fuse_file_info *fi)
-{
-	(void) fi;
-
-	if(strcmp(path, "/") != 0)
-		return -ENOENT;
-
-	return 0;
-}
-
-static int null_read(const char *path, char *buf, size_t size,
-		     off_t offset, struct fuse_file_info *fi)
-{
-	(void) buf;
-	(void) offset;
-	(void) fi;
-
-	if(strcmp(path, "/") != 0)
-		return -ENOENT;
-
-	if (offset >= (1ULL << 32))
-		return 0;
-
-	memset(buf, 0, size);
-	return size;
-}
-
-static int null_write(const char *path, const char *buf, size_t size,
-		      off_t offset, struct fuse_file_info *fi)
-{
-	(void) buf;
-	(void) offset;
-	(void) fi;
-
-	if(strcmp(path, "/") != 0)
-		return -ENOENT;
-
-	return size;
-}
-
-static const struct fuse_operations null_oper = {
-	.getattr	= null_getattr,
-	.truncate	= null_truncate,
-	.open		= null_open,
-	.read		= null_read,
-	.write		= null_write,
-};
-
-int main(int argc, char *argv[])
-{
-	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-	struct fuse_cmdline_opts opts;
-	struct stat stbuf;
-
-	if (fuse_parse_cmdline(&args, &opts) != 0)
-		return 1;
-	fuse_opt_free_args(&args);
-
-	if (!opts.mountpoint) {
-		fprintf(stderr, "missing mountpoint parameter\n");
-		return 1;
-	}
-
-	if (stat(opts.mountpoint, &stbuf) == -1) {
-		fprintf(stderr ,"failed to access mountpoint %s: %s\n",
-			opts.mountpoint, strerror(errno));
-		free(opts.mountpoint);
-		return 1;
-	}
-	free(opts.mountpoint);
-	if (!S_ISREG(stbuf.st_mode)) {
-		fprintf(stderr, "mountpoint is not a regular file\n");
-		return 1;
-	}
-
-	return fuse_main(argc, argv, &null_oper, NULL);
-}
diff --git a/example/passthrough.c b/example/passthrough.c
deleted file mode 100644
index ae13225..0000000
--- a/example/passthrough.c
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-  Copyright (C) 2011       Sebastian Pipping <sebastian@pipping.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This file system mirrors the existing file system hierarchy of the
- * system, starting at the root file system. This is implemented by
- * just "passing through" all requests to the corresponding user-space
- * libc functions. Its performance is terrible.
- *
- * Compile with
- *
- *     gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough
- *
- * ## Source code ##
- * \include passthrough.c
- */
-
-
-#define FUSE_USE_VERSION 31
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#define _GNU_SOURCE
-
-#ifdef linux
-/* For pread()/pwrite()/utimensat() */
-#define _XOPEN_SOURCE 700
-#endif
-
-#include <fuse.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-#ifdef __FreeBSD__
-#include <sys/socket.h>
-#include <sys/un.h>
-#endif
-#include <sys/time.h>
-#ifdef HAVE_SETXATTR
-#include <sys/xattr.h>
-#endif
-
-#include "passthrough_helpers.h"
-
-static int fill_dir_plus = 0;
-
-static void *xmp_init(struct fuse_conn_info *conn,
-		      struct fuse_config *cfg)
-{
-	(void) conn;
-	cfg->use_ino = 1;
-
-	/* Pick up changes from lower filesystem right away. This is
-	   also necessary for better hardlink support. When the kernel
-	   calls the unlink() handler, it does not know the inode of
-	   the to-be-removed entry and can therefore not invalidate
-	   the cache of the associated inode - resulting in an
-	   incorrect st_nlink value being reported for any remaining
-	   hardlinks to this inode. */
-	cfg->entry_timeout = 0;
-	cfg->attr_timeout = 0;
-	cfg->negative_timeout = 0;
-
-	return NULL;
-}
-
-static int xmp_getattr(const char *path, struct stat *stbuf,
-		       struct fuse_file_info *fi)
-{
-	(void) fi;
-	int res;
-
-	res = lstat(path, stbuf);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_access(const char *path, int mask)
-{
-	int res;
-
-	res = access(path, mask);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_readlink(const char *path, char *buf, size_t size)
-{
-	int res;
-
-	res = readlink(path, buf, size - 1);
-	if (res == -1)
-		return -errno;
-
-	buf[res] = '\0';
-	return 0;
-}
-
-
-static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-		       off_t offset, struct fuse_file_info *fi,
-		       enum fuse_readdir_flags flags)
-{
-	DIR *dp;
-	struct dirent *de;
-
-	(void) offset;
-	(void) fi;
-	(void) flags;
-
-	dp = opendir(path);
-	if (dp == NULL)
-		return -errno;
-
-	while ((de = readdir(dp)) != NULL) {
-		struct stat st;
-		memset(&st, 0, sizeof(st));
-		st.st_ino = de->d_ino;
-		st.st_mode = de->d_type << 12;
-		if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
-			break;
-	}
-
-	closedir(dp);
-	return 0;
-}
-
-static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
-{
-	int res;
-
-	res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_mkdir(const char *path, mode_t mode)
-{
-	int res;
-
-	res = mkdir(path, mode);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_unlink(const char *path)
-{
-	int res;
-
-	res = unlink(path);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_rmdir(const char *path)
-{
-	int res;
-
-	res = rmdir(path);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_symlink(const char *from, const char *to)
-{
-	int res;
-
-	res = symlink(from, to);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_rename(const char *from, const char *to, unsigned int flags)
-{
-	int res;
-
-	if (flags)
-		return -EINVAL;
-
-	res = rename(from, to);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_link(const char *from, const char *to)
-{
-	int res;
-
-	res = link(from, to);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_chmod(const char *path, mode_t mode,
-		     struct fuse_file_info *fi)
-{
-	(void) fi;
-	int res;
-
-	res = chmod(path, mode);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_chown(const char *path, uid_t uid, gid_t gid,
-		     struct fuse_file_info *fi)
-{
-	(void) fi;
-	int res;
-
-	res = lchown(path, uid, gid);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_truncate(const char *path, off_t size,
-			struct fuse_file_info *fi)
-{
-	int res;
-
-	if (fi != NULL)
-		res = ftruncate(fi->fh, size);
-	else
-		res = truncate(path, size);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-#ifdef HAVE_UTIMENSAT
-static int xmp_utimens(const char *path, const struct timespec ts[2],
-		       struct fuse_file_info *fi)
-{
-	(void) fi;
-	int res;
-
-	/* don't use utime/utimes since they follow symlinks */
-	res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-#endif
-
-static int xmp_create(const char *path, mode_t mode,
-		      struct fuse_file_info *fi)
-{
-	int res;
-
-	res = open(path, fi->flags, mode);
-	if (res == -1)
-		return -errno;
-
-	fi->fh = res;
-	return 0;
-}
-
-static int xmp_open(const char *path, struct fuse_file_info *fi)
-{
-	int res;
-
-	res = open(path, fi->flags);
-	if (res == -1)
-		return -errno;
-
-	fi->fh = res;
-	return 0;
-}
-
-static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
-		    struct fuse_file_info *fi)
-{
-	int fd;
-	int res;
-
-	if(fi == NULL)
-		fd = open(path, O_RDONLY);
-	else
-		fd = fi->fh;
-	
-	if (fd == -1)
-		return -errno;
-
-	res = pread(fd, buf, size, offset);
-	if (res == -1)
-		res = -errno;
-
-	if(fi == NULL)
-		close(fd);
-	return res;
-}
-
-static int xmp_write(const char *path, const char *buf, size_t size,
-		     off_t offset, struct fuse_file_info *fi)
-{
-	int fd;
-	int res;
-
-	(void) fi;
-	if(fi == NULL)
-		fd = open(path, O_WRONLY);
-	else
-		fd = fi->fh;
-	
-	if (fd == -1)
-		return -errno;
-
-	res = pwrite(fd, buf, size, offset);
-	if (res == -1)
-		res = -errno;
-
-	if(fi == NULL)
-		close(fd);
-	return res;
-}
-
-static int xmp_statfs(const char *path, struct statvfs *stbuf)
-{
-	int res;
-
-	res = statvfs(path, stbuf);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_release(const char *path, struct fuse_file_info *fi)
-{
-	(void) path;
-	close(fi->fh);
-	return 0;
-}
-
-static int xmp_fsync(const char *path, int isdatasync,
-		     struct fuse_file_info *fi)
-{
-	/* Just a stub.	 This method is optional and can safely be left
-	   unimplemented */
-
-	(void) path;
-	(void) isdatasync;
-	(void) fi;
-	return 0;
-}
-
-#ifdef HAVE_POSIX_FALLOCATE
-static int xmp_fallocate(const char *path, int mode,
-			off_t offset, off_t length, struct fuse_file_info *fi)
-{
-	int fd;
-	int res;
-
-	(void) fi;
-
-	if (mode)
-		return -EOPNOTSUPP;
-
-	if(fi == NULL)
-		fd = open(path, O_WRONLY);
-	else
-		fd = fi->fh;
-	
-	if (fd == -1)
-		return -errno;
-
-	res = -posix_fallocate(fd, offset, length);
-
-	if(fi == NULL)
-		close(fd);
-	return res;
-}
-#endif
-
-#ifdef HAVE_SETXATTR
-/* xattr operations are optional and can safely be left unimplemented */
-static int xmp_setxattr(const char *path, const char *name, const char *value,
-			size_t size, int flags)
-{
-	int res = lsetxattr(path, name, value, size, flags);
-	if (res == -1)
-		return -errno;
-	return 0;
-}
-
-static int xmp_getxattr(const char *path, const char *name, char *value,
-			size_t size)
-{
-	int res = lgetxattr(path, name, value, size);
-	if (res == -1)
-		return -errno;
-	return res;
-}
-
-static int xmp_listxattr(const char *path, char *list, size_t size)
-{
-	int res = llistxattr(path, list, size);
-	if (res == -1)
-		return -errno;
-	return res;
-}
-
-static int xmp_removexattr(const char *path, const char *name)
-{
-	int res = lremovexattr(path, name);
-	if (res == -1)
-		return -errno;
-	return 0;
-}
-#endif /* HAVE_SETXATTR */
-
-#ifdef HAVE_COPY_FILE_RANGE
-static ssize_t xmp_copy_file_range(const char *path_in,
-				   struct fuse_file_info *fi_in,
-				   off_t offset_in, const char *path_out,
-				   struct fuse_file_info *fi_out,
-				   off_t offset_out, size_t len, int flags)
-{
-	int fd_in, fd_out;
-	ssize_t res;
-
-	if(fi_in == NULL)
-		fd_in = open(path_in, O_RDONLY);
-	else
-		fd_in = fi_in->fh;
-
-	if (fd_in == -1)
-		return -errno;
-
-	if(fi_out == NULL)
-		fd_out = open(path_out, O_WRONLY);
-	else
-		fd_out = fi_out->fh;
-
-	if (fd_out == -1) {
-		close(fd_in);
-		return -errno;
-	}
-
-	res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
-			      flags);
-	if (res == -1)
-		res = -errno;
-
-	if (fi_out == NULL)
-		close(fd_out);
-	if (fi_in == NULL)
-		close(fd_in);
-
-	return res;
-}
-#endif
-
-static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
-{
-	int fd;
-	off_t res;
-
-	if (fi == NULL)
-		fd = open(path, O_RDONLY);
-	else
-		fd = fi->fh;
-
-	if (fd == -1)
-		return -errno;
-
-	res = lseek(fd, off, whence);
-	if (res == -1)
-		res = -errno;
-
-	if (fi == NULL)
-		close(fd);
-	return res;
-}
-
-static const struct fuse_operations xmp_oper = {
-	.init           = xmp_init,
-	.getattr	= xmp_getattr,
-	.access		= xmp_access,
-	.readlink	= xmp_readlink,
-	.readdir	= xmp_readdir,
-	.mknod		= xmp_mknod,
-	.mkdir		= xmp_mkdir,
-	.symlink	= xmp_symlink,
-	.unlink		= xmp_unlink,
-	.rmdir		= xmp_rmdir,
-	.rename		= xmp_rename,
-	.link		= xmp_link,
-	.chmod		= xmp_chmod,
-	.chown		= xmp_chown,
-	.truncate	= xmp_truncate,
-#ifdef HAVE_UTIMENSAT
-	.utimens	= xmp_utimens,
-#endif
-	.open		= xmp_open,
-	.create 	= xmp_create,
-	.read		= xmp_read,
-	.write		= xmp_write,
-	.statfs		= xmp_statfs,
-	.release	= xmp_release,
-	.fsync		= xmp_fsync,
-#ifdef HAVE_POSIX_FALLOCATE
-	.fallocate	= xmp_fallocate,
-#endif
-#ifdef HAVE_SETXATTR
-	.setxattr	= xmp_setxattr,
-	.getxattr	= xmp_getxattr,
-	.listxattr	= xmp_listxattr,
-	.removexattr	= xmp_removexattr,
-#endif
-#ifdef HAVE_COPY_FILE_RANGE
-	.copy_file_range = xmp_copy_file_range,
-#endif
-	.lseek		= xmp_lseek,
-};
-
-int main(int argc, char *argv[])
-{
-	enum { MAX_ARGS = 10 };
-	int i,new_argc;
-	char *new_argv[MAX_ARGS];
-
-	umask(0);
-			/* Process the "--plus" option apart */
-	for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
-		if (!strcmp(argv[i], "--plus")) {
-			fill_dir_plus = FUSE_FILL_DIR_PLUS;
-		} else {
-			new_argv[new_argc++] = argv[i];
-		}
-	}
-	return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
-}
diff --git a/example/passthrough_fh.c b/example/passthrough_fh.c
deleted file mode 100644
index bdc5ecd..0000000
--- a/example/passthrough_fh.c
+++ /dev/null
@@ -1,665 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-  Copyright (C) 2011       Sebastian Pipping <sebastian@pipping.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This file system mirrors the existing file system hierarchy of the
- * system, starting at the root file system. This is implemented by
- * just "passing through" all requests to the corresponding user-space
- * libc functions. This implementation is a little more sophisticated
- * than the one in passthrough.c, so performance is not quite as bad.
- *
- * Compile with:
- *
- *     gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh
- *
- * ## Source code ##
- * \include passthrough_fh.c
- */
-
-#define FUSE_USE_VERSION 31
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#define _GNU_SOURCE
-
-#include <fuse.h>
-
-#ifdef HAVE_LIBULOCKMGR
-#include <ulockmgr.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-#include <sys/time.h>
-#ifdef HAVE_SETXATTR
-#include <sys/xattr.h>
-#endif
-#include <sys/file.h> /* flock(2) */
-
-static void *xmp_init(struct fuse_conn_info *conn,
-		      struct fuse_config *cfg)
-{
-	(void) conn;
-	cfg->use_ino = 1;
-	cfg->nullpath_ok = 1;
-
-	/* Pick up changes from lower filesystem right away. This is
-	   also necessary for better hardlink support. When the kernel
-	   calls the unlink() handler, it does not know the inode of
-	   the to-be-removed entry and can therefore not invalidate
-	   the cache of the associated inode - resulting in an
-	   incorrect st_nlink value being reported for any remaining
-	   hardlinks to this inode. */
-	cfg->entry_timeout = 0;
-	cfg->attr_timeout = 0;
-	cfg->negative_timeout = 0;
-
-	return NULL;
-}
-
-static int xmp_getattr(const char *path, struct stat *stbuf,
-			struct fuse_file_info *fi)
-{
-	int res;
-
-	(void) path;
-
-	if(fi)
-		res = fstat(fi->fh, stbuf);
-	else
-		res = lstat(path, stbuf);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_access(const char *path, int mask)
-{
-	int res;
-
-	res = access(path, mask);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_readlink(const char *path, char *buf, size_t size)
-{
-	int res;
-
-	res = readlink(path, buf, size - 1);
-	if (res == -1)
-		return -errno;
-
-	buf[res] = '\0';
-	return 0;
-}
-
-struct xmp_dirp {
-	DIR *dp;
-	struct dirent *entry;
-	off_t offset;
-};
-
-static int xmp_opendir(const char *path, struct fuse_file_info *fi)
-{
-	int res;
-	struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
-	if (d == NULL)
-		return -ENOMEM;
-
-	d->dp = opendir(path);
-	if (d->dp == NULL) {
-		res = -errno;
-		free(d);
-		return res;
-	}
-	d->offset = 0;
-	d->entry = NULL;
-
-	fi->fh = (unsigned long) d;
-	return 0;
-}
-
-static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
-{
-	return (struct xmp_dirp *) (uintptr_t) fi->fh;
-}
-
-static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-		       off_t offset, struct fuse_file_info *fi,
-		       enum fuse_readdir_flags flags)
-{
-	struct xmp_dirp *d = get_dirp(fi);
-
-	(void) path;
-	if (offset != d->offset) {
-#ifndef __FreeBSD__
-		seekdir(d->dp, offset);
-#else
-		/* Subtract the one that we add when calling
-		   telldir() below */
-		seekdir(d->dp, offset-1);
-#endif
-		d->entry = NULL;
-		d->offset = offset;
-	}
-	while (1) {
-		struct stat st;
-		off_t nextoff;
-		enum fuse_fill_dir_flags fill_flags = 0;
-
-		if (!d->entry) {
-			d->entry = readdir(d->dp);
-			if (!d->entry)
-				break;
-		}
-#ifdef HAVE_FSTATAT
-		if (flags & FUSE_READDIR_PLUS) {
-			int res;
-
-			res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
-				      AT_SYMLINK_NOFOLLOW);
-			if (res != -1)
-				fill_flags |= FUSE_FILL_DIR_PLUS;
-		}
-#endif
-		if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
-			memset(&st, 0, sizeof(st));
-			st.st_ino = d->entry->d_ino;
-			st.st_mode = d->entry->d_type << 12;
-		}
-		nextoff = telldir(d->dp);
-#ifdef __FreeBSD__		
-		/* Under FreeBSD, telldir() may return 0 the first time
-		   it is called. But for libfuse, an offset of zero
-		   means that offsets are not supported, so we shift
-		   everything by one. */
-		nextoff++;
-#endif
-		if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
-			break;
-
-		d->entry = NULL;
-		d->offset = nextoff;
-	}
-
-	return 0;
-}
-
-static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
-{
-	struct xmp_dirp *d = get_dirp(fi);
-	(void) path;
-	closedir(d->dp);
-	free(d);
-	return 0;
-}
-
-static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
-{
-	int res;
-
-	if (S_ISFIFO(mode))
-		res = mkfifo(path, mode);
-	else
-		res = mknod(path, mode, rdev);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_mkdir(const char *path, mode_t mode)
-{
-	int res;
-
-	res = mkdir(path, mode);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_unlink(const char *path)
-{
-	int res;
-
-	res = unlink(path);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_rmdir(const char *path)
-{
-	int res;
-
-	res = rmdir(path);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_symlink(const char *from, const char *to)
-{
-	int res;
-
-	res = symlink(from, to);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_rename(const char *from, const char *to, unsigned int flags)
-{
-	int res;
-
-	/* When we have renameat2() in libc, then we can implement flags */
-	if (flags)
-		return -EINVAL;
-
-	res = rename(from, to);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_link(const char *from, const char *to)
-{
-	int res;
-
-	res = link(from, to);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_chmod(const char *path, mode_t mode,
-		     struct fuse_file_info *fi)
-{
-	int res;
-
-	if(fi)
-		res = fchmod(fi->fh, mode);
-	else
-		res = chmod(path, mode);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_chown(const char *path, uid_t uid, gid_t gid,
-		     struct fuse_file_info *fi)
-{
-	int res;
-
-	if (fi)
-		res = fchown(fi->fh, uid, gid);
-	else
-		res = lchown(path, uid, gid);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_truncate(const char *path, off_t size,
-			 struct fuse_file_info *fi)
-{
-	int res;
-
-	if(fi)
-		res = ftruncate(fi->fh, size);
-	else
-		res = truncate(path, size);
-
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-#ifdef HAVE_UTIMENSAT
-static int xmp_utimens(const char *path, const struct timespec ts[2],
-		       struct fuse_file_info *fi)
-{
-	int res;
-
-	/* don't use utime/utimes since they follow symlinks */
-	if (fi)
-		res = futimens(fi->fh, ts);
-	else
-		res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-#endif
-
-static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
-{
-	int fd;
-
-	fd = open(path, fi->flags, mode);
-	if (fd == -1)
-		return -errno;
-
-	fi->fh = fd;
-	return 0;
-}
-
-static int xmp_open(const char *path, struct fuse_file_info *fi)
-{
-	int fd;
-
-	fd = open(path, fi->flags);
-	if (fd == -1)
-		return -errno;
-
-	fi->fh = fd;
-	return 0;
-}
-
-static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
-		    struct fuse_file_info *fi)
-{
-	int res;
-
-	(void) path;
-	res = pread(fi->fh, buf, size, offset);
-	if (res == -1)
-		res = -errno;
-
-	return res;
-}
-
-static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
-			size_t size, off_t offset, struct fuse_file_info *fi)
-{
-	struct fuse_bufvec *src;
-
-	(void) path;
-
-	src = malloc(sizeof(struct fuse_bufvec));
-	if (src == NULL)
-		return -ENOMEM;
-
-	*src = FUSE_BUFVEC_INIT(size);
-
-	src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
-	src->buf[0].fd = fi->fh;
-	src->buf[0].pos = offset;
-
-	*bufp = src;
-
-	return 0;
-}
-
-static int xmp_write(const char *path, const char *buf, size_t size,
-		     off_t offset, struct fuse_file_info *fi)
-{
-	int res;
-
-	(void) path;
-	res = pwrite(fi->fh, buf, size, offset);
-	if (res == -1)
-		res = -errno;
-
-	return res;
-}
-
-static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
-		     off_t offset, struct fuse_file_info *fi)
-{
-	struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
-
-	(void) path;
-
-	dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
-	dst.buf[0].fd = fi->fh;
-	dst.buf[0].pos = offset;
-
-	return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
-}
-
-static int xmp_statfs(const char *path, struct statvfs *stbuf)
-{
-	int res;
-
-	res = statvfs(path, stbuf);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_flush(const char *path, struct fuse_file_info *fi)
-{
-	int res;
-
-	(void) path;
-	/* This is called from every close on an open file, so call the
-	   close on the underlying filesystem.	But since flush may be
-	   called multiple times for an open file, this must not really
-	   close the file.  This is important if used on a network
-	   filesystem like NFS which flush the data/metadata on close() */
-	res = close(dup(fi->fh));
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-static int xmp_release(const char *path, struct fuse_file_info *fi)
-{
-	(void) path;
-	close(fi->fh);
-
-	return 0;
-}
-
-static int xmp_fsync(const char *path, int isdatasync,
-		     struct fuse_file_info *fi)
-{
-	int res;
-	(void) path;
-
-#ifndef HAVE_FDATASYNC
-	(void) isdatasync;
-#else
-	if (isdatasync)
-		res = fdatasync(fi->fh);
-	else
-#endif
-		res = fsync(fi->fh);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-#ifdef HAVE_POSIX_FALLOCATE
-static int xmp_fallocate(const char *path, int mode,
-			off_t offset, off_t length, struct fuse_file_info *fi)
-{
-	(void) path;
-
-	if (mode)
-		return -EOPNOTSUPP;
-
-	return -posix_fallocate(fi->fh, offset, length);
-}
-#endif
-
-#ifdef HAVE_SETXATTR
-/* xattr operations are optional and can safely be left unimplemented */
-static int xmp_setxattr(const char *path, const char *name, const char *value,
-			size_t size, int flags)
-{
-	int res = lsetxattr(path, name, value, size, flags);
-	if (res == -1)
-		return -errno;
-	return 0;
-}
-
-static int xmp_getxattr(const char *path, const char *name, char *value,
-			size_t size)
-{
-	int res = lgetxattr(path, name, value, size);
-	if (res == -1)
-		return -errno;
-	return res;
-}
-
-static int xmp_listxattr(const char *path, char *list, size_t size)
-{
-	int res = llistxattr(path, list, size);
-	if (res == -1)
-		return -errno;
-	return res;
-}
-
-static int xmp_removexattr(const char *path, const char *name)
-{
-	int res = lremovexattr(path, name);
-	if (res == -1)
-		return -errno;
-	return 0;
-}
-#endif /* HAVE_SETXATTR */
-
-#ifdef HAVE_LIBULOCKMGR
-static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
-		    struct flock *lock)
-{
-	(void) path;
-
-	return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
-			   sizeof(fi->lock_owner));
-}
-#endif
-
-static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
-{
-	int res;
-	(void) path;
-
-	res = flock(fi->fh, op);
-	if (res == -1)
-		return -errno;
-
-	return 0;
-}
-
-#ifdef HAVE_COPY_FILE_RANGE
-static ssize_t xmp_copy_file_range(const char *path_in,
-				   struct fuse_file_info *fi_in,
-				   off_t off_in, const char *path_out,
-				   struct fuse_file_info *fi_out,
-				   off_t off_out, size_t len, int flags)
-{
-	ssize_t res;
-	(void) path_in;
-	(void) path_out;
-
-	res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
-			      flags);
-	if (res == -1)
-		return -errno;
-
-	return res;
-}
-#endif
-
-static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
-{
-	off_t res;
-	(void) path;
-
-	res = lseek(fi->fh, off, whence);
-	if (res == -1)
-		return -errno;
-
-	return res;
-}
-
-static const struct fuse_operations xmp_oper = {
-	.init           = xmp_init,
-	.getattr	= xmp_getattr,
-	.access		= xmp_access,
-	.readlink	= xmp_readlink,
-	.opendir	= xmp_opendir,
-	.readdir	= xmp_readdir,
-	.releasedir	= xmp_releasedir,
-	.mknod		= xmp_mknod,
-	.mkdir		= xmp_mkdir,
-	.symlink	= xmp_symlink,
-	.unlink		= xmp_unlink,
-	.rmdir		= xmp_rmdir,
-	.rename		= xmp_rename,
-	.link		= xmp_link,
-	.chmod		= xmp_chmod,
-	.chown		= xmp_chown,
-	.truncate	= xmp_truncate,
-#ifdef HAVE_UTIMENSAT
-	.utimens	= xmp_utimens,
-#endif
-	.create		= xmp_create,
-	.open		= xmp_open,
-	.read		= xmp_read,
-	.read_buf	= xmp_read_buf,
-	.write		= xmp_write,
-	.write_buf	= xmp_write_buf,
-	.statfs		= xmp_statfs,
-	.flush		= xmp_flush,
-	.release	= xmp_release,
-	.fsync		= xmp_fsync,
-#ifdef HAVE_POSIX_FALLOCATE
-	.fallocate	= xmp_fallocate,
-#endif
-#ifdef HAVE_SETXATTR
-	.setxattr	= xmp_setxattr,
-	.getxattr	= xmp_getxattr,
-	.listxattr	= xmp_listxattr,
-	.removexattr	= xmp_removexattr,
-#endif
-#ifdef HAVE_LIBULOCKMGR
-	.lock		= xmp_lock,
-#endif
-	.flock		= xmp_flock,
-#ifdef HAVE_COPY_FILE_RANGE
-	.copy_file_range = xmp_copy_file_range,
-#endif
-	.lseek		= xmp_lseek,
-};
-
-int main(int argc, char *argv[])
-{
-	umask(0);
-	return fuse_main(argc, argv, &xmp_oper, NULL);
-}
diff --git a/example/passthrough_helpers.h b/example/passthrough_helpers.h
deleted file mode 100644
index 6b77c33..0000000
--- a/example/passthrough_helpers.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * FUSE: Filesystem in Userspace
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE
- */
-
-/*
- * Creates files on the underlying file system in response to a FUSE_MKNOD
- * operation
- */
-static int mknod_wrapper(int dirfd, const char *path, const char *link,
-	int mode, dev_t rdev)
-{
-	int res;
-
-	if (S_ISREG(mode)) {
-		res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode);
-		if (res >= 0)
-			res = close(res);
-	} else if (S_ISDIR(mode)) {
-		res = mkdirat(dirfd, path, mode);
-	} else if (S_ISLNK(mode) && link != NULL) {
-		res = symlinkat(link, dirfd, path);
-	} else if (S_ISFIFO(mode)) {
-		res = mkfifoat(dirfd, path, mode);
-#ifdef __FreeBSD__
-	} else if (S_ISSOCK(mode)) {
-		struct sockaddr_un su;
-		int fd;
-
-		if (strlen(path) >= sizeof(su.sun_path)) {
-			errno = ENAMETOOLONG;
-			return -1;
-		}
-		fd = socket(AF_UNIX, SOCK_STREAM, 0);
-		if (fd >= 0) {
-			/*
-			 * We must bind the socket to the underlying file
-			 * system to create the socket file, even though
-			 * we'll never listen on this socket.
-			 */
-			su.sun_family = AF_UNIX;
-			strncpy(su.sun_path, path, sizeof(su.sun_path));
-			res = bindat(dirfd, fd, (struct sockaddr*)&su,
-				sizeof(su));
-			if (res == 0)
-				close(fd);
-		} else {
-			res = -1;
-		}
-#endif
-	} else {
-		res = mknodat(dirfd, path, mode, rdev);
-	}
-
-	return res;
-}
diff --git a/example/passthrough_hp.cc b/example/passthrough_hp.cc
deleted file mode 100644
index 872fc73..0000000
--- a/example/passthrough_hp.cc
+++ /dev/null
@@ -1,1265 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-  Copyright (C) 2017       Nikolaus Rath <Nikolaus@rath.org>
-  Copyright (C) 2018       Valve, Inc
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This is a "high-performance" version of passthrough_ll.c. While
- * passthrough_ll.c is designed to be as simple as possible, this
- * example intended to be as efficient and correct as possible.
- *
- * passthrough_hp.cc mirrors a specified "source" directory under a
- * specified the mountpoint with as much fidelity and performance as
- * possible.
- *
- * If --nocache is specified, the source directory may be changed
- * directly even while mounted and the filesystem will continue
- * to work correctly.
- *
- * Without --nocache, the source directory is assumed to be modified
- * only through the passthrough filesystem. This enables much better
- * performance, but if changes are made directly to the source, they
- * may not be immediately visible under the mountpoint and further
- * access to the mountpoint may result in incorrect behavior,
- * including data-loss.
- *
- * On its own, this filesystem fulfills no practical purpose. It is
- * intended as a template upon which additional functionality can be
- * built.
- *
- * Unless --nocache is specified, is only possible to write to files
- * for which the mounting user has read permissions. This is because
- * the writeback cache requires the kernel to be able to issue read
- * requests for all files (which the passthrough filesystem cannot
- * satisfy if it can't read the file in the underlying filesystem).
- *
- * ## Source code ##
- * \include passthrough_hp.cc
- */
-
-#define FUSE_USE_VERSION 35
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-// C includes
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <ftw.h>
-#include <fuse_lowlevel.h>
-#include <inttypes.h>
-#include <string.h>
-#include <sys/file.h>
-#include <sys/resource.h>
-#include <sys/xattr.h>
-#include <time.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <limits.h>
-
-// C++ includes
-#include <cstddef>
-#include <cstdio>
-#include <cstdlib>
-#include <list>
-#include "cxxopts.hpp"
-#include <mutex>
-#include <fstream>
-#include <thread>
-#include <iomanip>
-
-using namespace std;
-
-/* We are re-using pointers to our `struct sfs_inode` and `struct
-   sfs_dirp` elements as inodes and file handles. This means that we
-   must be able to store pointer a pointer in both a fuse_ino_t
-   variable and a uint64_t variable (used for file handles). */
-static_assert(sizeof(fuse_ino_t) >= sizeof(void*),
-              "void* must fit into fuse_ino_t");
-static_assert(sizeof(fuse_ino_t) >= sizeof(uint64_t),
-              "fuse_ino_t must be at least 64 bits");
-
-
-/* Forward declarations */
-struct Inode;
-static Inode& get_inode(fuse_ino_t ino);
-static void forget_one(fuse_ino_t ino, uint64_t n);
-
-// Uniquely identifies a file in the source directory tree. This could
-// be simplified to just ino_t since we require the source directory
-// not to contain any mountpoints. This hasn't been done yet in case
-// we need to reconsider this constraint (but relaxing this would have
-// the drawback that we can no longer re-use inode numbers, and thus
-// readdir() would need to do a full lookup() in order to report the
-// right inode number).
-typedef std::pair<ino_t, dev_t> SrcId;
-
-// Define a hash function for SrcId
-namespace std {
-    template<>
-    struct hash<SrcId> {
-        size_t operator()(const SrcId& id) const {
-            return hash<ino_t>{}(id.first) ^ hash<dev_t>{}(id.second);
-        }
-    };
-}
-
-// Maps files in the source directory tree to inodes
-typedef std::unordered_map<SrcId, Inode> InodeMap;
-
-struct Inode {
-    int fd {-1};
-    dev_t src_dev {0};
-    ino_t src_ino {0};
-    int generation {0};
-    uint64_t nopen {0};
-    uint64_t nlookup {0};
-    std::mutex m;
-
-    // Delete copy constructor and assignments. We could implement
-    // move if we need it.
-    Inode() = default;
-    Inode(const Inode&) = delete;
-    Inode(Inode&& inode) = delete;
-    Inode& operator=(Inode&& inode) = delete;
-    Inode& operator=(const Inode&) = delete;
-
-    ~Inode() {
-        if(fd > 0)
-            close(fd);
-    }
-};
-
-struct Fs {
-    // Must be acquired *after* any Inode.m locks.
-    std::mutex mutex;
-    InodeMap inodes; // protected by mutex
-    Inode root;
-    double timeout;
-    bool debug;
-    std::string source;
-    size_t blocksize;
-    dev_t src_dev;
-    bool nosplice;
-    bool nocache;
-};
-static Fs fs{};
-
-
-#define FUSE_BUF_COPY_FLAGS                      \
-        (fs.nosplice ?                           \
-            FUSE_BUF_NO_SPLICE :                 \
-            static_cast<fuse_buf_copy_flags>(0))
-
-
-static Inode& get_inode(fuse_ino_t ino) {
-    if (ino == FUSE_ROOT_ID)
-        return fs.root;
-
-    Inode* inode = reinterpret_cast<Inode*>(ino);
-    if(inode->fd == -1) {
-        cerr << "INTERNAL ERROR: Unknown inode " << ino << endl;
-        abort();
-    }
-    return *inode;
-}
-
-
-static int get_fs_fd(fuse_ino_t ino) {
-    int fd = get_inode(ino).fd;
-    return fd;
-}
-
-
-static void sfs_init(void *userdata, fuse_conn_info *conn) {
-    (void)userdata;
-    if (conn->capable & FUSE_CAP_EXPORT_SUPPORT)
-        conn->want |= FUSE_CAP_EXPORT_SUPPORT;
-
-    if (fs.timeout && conn->capable & FUSE_CAP_WRITEBACK_CACHE)
-        conn->want |= FUSE_CAP_WRITEBACK_CACHE;
-
-    if (conn->capable & FUSE_CAP_FLOCK_LOCKS)
-        conn->want |= FUSE_CAP_FLOCK_LOCKS;
-
-    // Use splicing if supported. Since we are using writeback caching
-    // and readahead, individual requests should have a decent size so
-    // that splicing between fd's is well worth it.
-    if (conn->capable & FUSE_CAP_SPLICE_WRITE && !fs.nosplice)
-        conn->want |= FUSE_CAP_SPLICE_WRITE;
-    if (conn->capable & FUSE_CAP_SPLICE_READ && !fs.nosplice)
-        conn->want |= FUSE_CAP_SPLICE_READ;
-}
-
-
-static void sfs_getattr(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
-    (void)fi;
-    Inode& inode = get_inode(ino);
-    struct stat attr;
-    auto res = fstatat(inode.fd, "", &attr,
-                       AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
-    if (res == -1) {
-        fuse_reply_err(req, errno);
-        return;
-    }
-    fuse_reply_attr(req, &attr, fs.timeout);
-}
-
-
-static void do_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
-                       int valid, struct fuse_file_info* fi) {
-    Inode& inode = get_inode(ino);
-    int ifd = inode.fd;
-    int res;
-
-    if (valid & FUSE_SET_ATTR_MODE) {
-        if (fi) {
-            res = fchmod(fi->fh, attr->st_mode);
-        } else {
-            char procname[64];
-            sprintf(procname, "/proc/self/fd/%i", ifd);
-            res = chmod(procname, attr->st_mode);
-        }
-        if (res == -1)
-            goto out_err;
-    }
-    if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
-        uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : static_cast<uid_t>(-1);
-        gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : static_cast<gid_t>(-1);
-
-        res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
-        if (res == -1)
-            goto out_err;
-    }
-    if (valid & FUSE_SET_ATTR_SIZE) {
-        if (fi) {
-            res = ftruncate(fi->fh, attr->st_size);
-        } else {
-            char procname[64];
-            sprintf(procname, "/proc/self/fd/%i", ifd);
-            res = truncate(procname, attr->st_size);
-        }
-        if (res == -1)
-            goto out_err;
-    }
-    if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
-        struct timespec tv[2];
-
-        tv[0].tv_sec = 0;
-        tv[1].tv_sec = 0;
-        tv[0].tv_nsec = UTIME_OMIT;
-        tv[1].tv_nsec = UTIME_OMIT;
-
-        if (valid & FUSE_SET_ATTR_ATIME_NOW)
-            tv[0].tv_nsec = UTIME_NOW;
-        else if (valid & FUSE_SET_ATTR_ATIME)
-            tv[0] = attr->st_atim;
-
-        if (valid & FUSE_SET_ATTR_MTIME_NOW)
-            tv[1].tv_nsec = UTIME_NOW;
-        else if (valid & FUSE_SET_ATTR_MTIME)
-            tv[1] = attr->st_mtim;
-
-        if (fi)
-            res = futimens(fi->fh, tv);
-        else {
-#ifdef HAVE_UTIMENSAT
-            char procname[64];
-            sprintf(procname, "/proc/self/fd/%i", ifd);
-            res = utimensat(AT_FDCWD, procname, tv, 0);
-#else
-            res = -1;
-            errno = EOPNOTSUPP;
-#endif
-        }
-        if (res == -1)
-            goto out_err;
-    }
-    return sfs_getattr(req, ino, fi);
-
-out_err:
-    fuse_reply_err(req, errno);
-}
-
-
-static void sfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
-                        int valid, fuse_file_info *fi) {
-    (void) ino;
-    do_setattr(req, ino, attr, valid, fi);
-}
-
-
-static int do_lookup(fuse_ino_t parent, const char *name,
-                     fuse_entry_param *e) {
-    if (fs.debug)
-        cerr << "DEBUG: lookup(): name=" << name
-             << ", parent=" << parent << endl;
-    memset(e, 0, sizeof(*e));
-    e->attr_timeout = fs.timeout;
-    e->entry_timeout = fs.timeout;
-
-    auto newfd = openat(get_fs_fd(parent), name, O_PATH | O_NOFOLLOW);
-    if (newfd == -1)
-        return errno;
-
-    auto res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
-    if (res == -1) {
-        auto saveerr = errno;
-        close(newfd);
-        if (fs.debug)
-            cerr << "DEBUG: lookup(): fstatat failed" << endl;
-        return saveerr;
-    }
-
-    if (e->attr.st_dev != fs.src_dev) {
-        cerr << "WARNING: Mountpoints in the source directory tree will be hidden." << endl;
-        return ENOTSUP;
-    } else if (e->attr.st_ino == FUSE_ROOT_ID) {
-        cerr << "ERROR: Source directory tree must not include inode "
-             << FUSE_ROOT_ID << endl;
-        return EIO;
-    }
-
-    SrcId id {e->attr.st_ino, e->attr.st_dev};
-    unique_lock<mutex> fs_lock {fs.mutex};
-    Inode* inode_p;
-    try {
-        inode_p = &fs.inodes[id];
-    } catch (std::bad_alloc&) {
-        return ENOMEM;
-    }
-    e->ino = reinterpret_cast<fuse_ino_t>(inode_p);
-    Inode& inode {*inode_p};
-    e->generation = inode.generation;
-
-    if (inode.fd == -ENOENT) { // found unlinked inode
-        if (fs.debug)
-            cerr << "DEBUG: lookup(): inode " << e->attr.st_ino
-                 << " recycled; generation=" << inode.generation << endl;
-	/* fallthrough to new inode but keep existing inode.nlookup */
-    }
-
-    if (inode.fd > 0) { // found existing inode
-        fs_lock.unlock();
-        if (fs.debug)
-            cerr << "DEBUG: lookup(): inode " << e->attr.st_ino
-                 << " (userspace) already known; fd = " << inode.fd << endl;
-        lock_guard<mutex> g {inode.m};
-        inode.nlookup++;
-        close(newfd);
-    } else { // no existing inode
-        /* This is just here to make Helgrind happy. It violates the
-           lock ordering requirement (inode.m must be acquired before
-           fs.mutex), but this is of no consequence because at this
-           point no other thread has access to the inode mutex */
-        lock_guard<mutex> g {inode.m};
-        inode.src_ino = e->attr.st_ino;
-        inode.src_dev = e->attr.st_dev;
-        inode.nlookup++;
-        inode.fd = newfd;
-        fs_lock.unlock();
-
-        if (fs.debug)
-            cerr << "DEBUG: lookup(): created userspace inode " << e->attr.st_ino
-                 << "; fd = " << inode.fd << endl;
-    }
-
-    return 0;
-}
-
-
-static void sfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
-    fuse_entry_param e {};
-    auto err = do_lookup(parent, name, &e);
-    if (err == ENOENT) {
-        e.attr_timeout = fs.timeout;
-        e.entry_timeout = fs.timeout;
-        e.ino = e.attr.st_ino = 0;
-        fuse_reply_entry(req, &e);
-    } else if (err) {
-        if (err == ENFILE || err == EMFILE)
-            cerr << "ERROR: Reached maximum number of file descriptors." << endl;
-        fuse_reply_err(req, err);
-    } else {
-        fuse_reply_entry(req, &e);
-    }
-}
-
-
-static void mknod_symlink(fuse_req_t req, fuse_ino_t parent,
-                              const char *name, mode_t mode, dev_t rdev,
-                              const char *link) {
-    int res;
-    Inode& inode_p = get_inode(parent);
-    auto saverr = ENOMEM;
-
-    if (S_ISDIR(mode))
-        res = mkdirat(inode_p.fd, name, mode);
-    else if (S_ISLNK(mode))
-        res = symlinkat(link, inode_p.fd, name);
-    else
-        res = mknodat(inode_p.fd, name, mode, rdev);
-    saverr = errno;
-    if (res == -1)
-        goto out;
-
-    fuse_entry_param e;
-    saverr = do_lookup(parent, name, &e);
-    if (saverr)
-        goto out;
-
-    fuse_reply_entry(req, &e);
-    return;
-
-out:
-    if (saverr == ENFILE || saverr == EMFILE)
-        cerr << "ERROR: Reached maximum number of file descriptors." << endl;
-    fuse_reply_err(req, saverr);
-}
-
-
-static void sfs_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
-                      mode_t mode, dev_t rdev) {
-    mknod_symlink(req, parent, name, mode, rdev, nullptr);
-}
-
-
-static void sfs_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
-                      mode_t mode) {
-    mknod_symlink(req, parent, name, S_IFDIR | mode, 0, nullptr);
-}
-
-
-static void sfs_symlink(fuse_req_t req, const char *link, fuse_ino_t parent,
-                        const char *name) {
-    mknod_symlink(req, parent, name, S_IFLNK, 0, link);
-}
-
-
-static void sfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
-                     const char *name) {
-    Inode& inode = get_inode(ino);
-    Inode& inode_p = get_inode(parent);
-    fuse_entry_param e {};
-
-    e.attr_timeout = fs.timeout;
-    e.entry_timeout = fs.timeout;
-
-    char procname[64];
-    sprintf(procname, "/proc/self/fd/%i", inode.fd);
-    auto res = linkat(AT_FDCWD, procname, inode_p.fd, name, AT_SYMLINK_FOLLOW);
-    if (res == -1) {
-        fuse_reply_err(req, errno);
-        return;
-    }
-
-    res = fstatat(inode.fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
-    if (res == -1) {
-        fuse_reply_err(req, errno);
-        return;
-    }
-    e.ino = reinterpret_cast<fuse_ino_t>(&inode);
-    {
-        lock_guard<mutex> g {inode.m};
-        inode.nlookup++;
-    }
-
-    fuse_reply_entry(req, &e);
-    return;
-}
-
-
-static void sfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) {
-    Inode& inode_p = get_inode(parent);
-    lock_guard<mutex> g {inode_p.m};
-    auto res = unlinkat(inode_p.fd, name, AT_REMOVEDIR);
-    fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-
-static void sfs_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
-                       fuse_ino_t newparent, const char *newname,
-                       unsigned int flags) {
-    Inode& inode_p = get_inode(parent);
-    Inode& inode_np = get_inode(newparent);
-    if (flags) {
-        fuse_reply_err(req, EINVAL);
-        return;
-    }
-
-    auto res = renameat(inode_p.fd, name, inode_np.fd, newname);
-    fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-
-static void sfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) {
-    Inode& inode_p = get_inode(parent);
-    // Release inode.fd before last unlink like nfsd EXPORT_OP_CLOSE_BEFORE_UNLINK
-    // to test reused inode numbers.
-    // Skip this when inode has an open file and when writeback cache is enabled.
-    if (!fs.timeout) {
-	    fuse_entry_param e;
-	    auto err = do_lookup(parent, name, &e);
-	    if (err) {
-		    fuse_reply_err(req, err);
-		    return;
-	    }
-	    if (e.attr.st_nlink == 1) {
-		    Inode& inode = get_inode(e.ino);
-		    lock_guard<mutex> g {inode.m};
-		    if (inode.fd > 0 && !inode.nopen) {
-			    if (fs.debug)
-				    cerr << "DEBUG: unlink: release inode " << e.attr.st_ino
-					    << "; fd=" << inode.fd << endl;
-			    lock_guard<mutex> g_fs {fs.mutex};
-			    close(inode.fd);
-			    inode.fd = -ENOENT;
-			    inode.generation++;
-		    }
-	    }
-    }
-    auto res = unlinkat(inode_p.fd, name, 0);
-    fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-
-static void forget_one(fuse_ino_t ino, uint64_t n) {
-    Inode& inode = get_inode(ino);
-    unique_lock<mutex> l {inode.m};
-
-    if(n > inode.nlookup) {
-        cerr << "INTERNAL ERROR: Negative lookup count for inode "
-             << inode.src_ino << endl;
-        abort();
-    }
-    inode.nlookup -= n;
-    if (!inode.nlookup) {
-        if (fs.debug)
-            cerr << "DEBUG: forget: cleaning up inode " << inode.src_ino << endl;
-        {
-            lock_guard<mutex> g_fs {fs.mutex};
-            l.unlock();
-            fs.inodes.erase({inode.src_ino, inode.src_dev});
-        }
-    } else if (fs.debug)
-            cerr << "DEBUG: forget: inode " << inode.src_ino
-                 << " lookup count now " << inode.nlookup << endl;
-}
-
-static void sfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) {
-    forget_one(ino, nlookup);
-    fuse_reply_none(req);
-}
-
-
-static void sfs_forget_multi(fuse_req_t req, size_t count,
-                             fuse_forget_data *forgets) {
-    for (int i = 0; i < count; i++)
-        forget_one(forgets[i].ino, forgets[i].nlookup);
-    fuse_reply_none(req);
-}
-
-
-static void sfs_readlink(fuse_req_t req, fuse_ino_t ino) {
-    Inode& inode = get_inode(ino);
-    char buf[PATH_MAX + 1];
-    auto res = readlinkat(inode.fd, "", buf, sizeof(buf));
-    if (res == -1)
-        fuse_reply_err(req, errno);
-    else if (res == sizeof(buf))
-        fuse_reply_err(req, ENAMETOOLONG);
-    else {
-        buf[res] = '\0';
-        fuse_reply_readlink(req, buf);
-    }
-}
-
-
-struct DirHandle {
-    DIR *dp {nullptr};
-    off_t offset;
-
-    DirHandle() = default;
-    DirHandle(const DirHandle&) = delete;
-    DirHandle& operator=(const DirHandle&) = delete;
-
-    ~DirHandle() {
-        if(dp)
-            closedir(dp);
-    }
-};
-
-
-static DirHandle *get_dir_handle(fuse_file_info *fi) {
-    return reinterpret_cast<DirHandle*>(fi->fh);
-}
-
-
-static void sfs_opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
-    Inode& inode = get_inode(ino);
-    auto d = new (nothrow) DirHandle;
-    if (d == nullptr) {
-        fuse_reply_err(req, ENOMEM);
-        return;
-    }
-
-    // Make Helgrind happy - it can't know that there's an implicit
-    // synchronization due to the fact that other threads cannot
-    // access d until we've called fuse_reply_*.
-    lock_guard<mutex> g {inode.m};
-
-    auto fd = openat(inode.fd, ".", O_RDONLY);
-    if (fd == -1)
-        goto out_errno;
-
-    // On success, dir stream takes ownership of fd, so we
-    // do not have to close it.
-    d->dp = fdopendir(fd);
-    if(d->dp == nullptr)
-        goto out_errno;
-
-    d->offset = 0;
-
-    fi->fh = reinterpret_cast<uint64_t>(d);
-    if(fs.timeout) {
-        fi->keep_cache = 1;
-        fi->cache_readdir = 1;
-    }
-    fuse_reply_open(req, fi);
-    return;
-
-out_errno:
-    auto error = errno;
-    delete d;
-    if (error == ENFILE || error == EMFILE)
-        cerr << "ERROR: Reached maximum number of file descriptors." << endl;
-    fuse_reply_err(req, error);
-}
-
-
-static bool is_dot_or_dotdot(const char *name) {
-    return name[0] == '.' &&
-           (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
-}
-
-
-static void do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
-                    off_t offset, fuse_file_info *fi, int plus) {
-    auto d = get_dir_handle(fi);
-    Inode& inode = get_inode(ino);
-    lock_guard<mutex> g {inode.m};
-    char *p;
-    auto rem = size;
-    int err = 0, count = 0;
-
-    if (fs.debug)
-        cerr << "DEBUG: readdir(): started with offset "
-             << offset << endl;
-
-    auto buf = new (nothrow) char[size];
-    if (!buf) {
-        fuse_reply_err(req, ENOMEM);
-        return;
-    }
-    p = buf;
-
-    if (offset != d->offset) {
-        if (fs.debug)
-            cerr << "DEBUG: readdir(): seeking to " << offset << endl;
-        seekdir(d->dp, offset);
-        d->offset = offset;
-    }
-
-    while (1) {
-        struct dirent *entry;
-        errno = 0;
-        entry = readdir(d->dp);
-        if (!entry) {
-            if(errno) {
-                err = errno;
-                if (fs.debug)
-                    warn("DEBUG: readdir(): readdir failed with");
-                goto error;
-            }
-            break; // End of stream
-        }
-        d->offset = entry->d_off;
-        if (is_dot_or_dotdot(entry->d_name))
-            continue;
-
-        fuse_entry_param e{};
-        size_t entsize;
-        if(plus) {
-            err = do_lookup(ino, entry->d_name, &e);
-            if (err)
-                goto error;
-            entsize = fuse_add_direntry_plus(req, p, rem, entry->d_name, &e, entry->d_off);
-
-            if (entsize > rem) {
-                if (fs.debug)
-                    cerr << "DEBUG: readdir(): buffer full, returning data. " << endl;
-                forget_one(e.ino, 1);
-                break;
-            }
-        } else {
-            e.attr.st_ino = entry->d_ino;
-            e.attr.st_mode = entry->d_type << 12;
-            entsize = fuse_add_direntry(req, p, rem, entry->d_name, &e.attr, entry->d_off);
-
-            if (entsize > rem) {
-                if (fs.debug)
-                    cerr << "DEBUG: readdir(): buffer full, returning data. " << endl;
-                break;
-            }
-        }
-
-        p += entsize;
-        rem -= entsize;
-        count++;
-        if (fs.debug) {
-            cerr << "DEBUG: readdir(): added to buffer: " << entry->d_name
-                 << ", ino " << e.attr.st_ino << ", offset " << entry->d_off << endl;
-        }
-    }
-    err = 0;
-error:
-
-    // If there's an error, we can only signal it if we haven't stored
-    // any entries yet - otherwise we'd end up with wrong lookup
-    // counts for the entries that are already in the buffer. So we
-    // return what we've collected until that point.
-    if (err && rem == size) {
-        if (err == ENFILE || err == EMFILE)
-            cerr << "ERROR: Reached maximum number of file descriptors." << endl;
-        fuse_reply_err(req, err);
-    } else {
-        if (fs.debug)
-            cerr << "DEBUG: readdir(): returning " << count
-                 << " entries, curr offset " << d->offset << endl;
-        fuse_reply_buf(req, buf, size - rem);
-    }
-    delete[] buf;
-    return;
-}
-
-
-static void sfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
-                        off_t offset, fuse_file_info *fi) {
-    // operation logging is done in readdir to reduce code duplication
-    do_readdir(req, ino, size, offset, fi, 0);
-}
-
-
-static void sfs_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
-                            off_t offset, fuse_file_info *fi) {
-    // operation logging is done in readdir to reduce code duplication
-    do_readdir(req, ino, size, offset, fi, 1);
-}
-
-
-static void sfs_releasedir(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
-    (void) ino;
-    auto d = get_dir_handle(fi);
-    delete d;
-    fuse_reply_err(req, 0);
-}
-
-
-static void sfs_create(fuse_req_t req, fuse_ino_t parent, const char *name,
-                       mode_t mode, fuse_file_info *fi) {
-    Inode& inode_p = get_inode(parent);
-
-    auto fd = openat(inode_p.fd, name,
-                     (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
-    if (fd == -1) {
-        auto err = errno;
-        if (err == ENFILE || err == EMFILE)
-            cerr << "ERROR: Reached maximum number of file descriptors." << endl;
-        fuse_reply_err(req, err);
-        return;
-    }
-
-    fi->fh = fd;
-    fuse_entry_param e;
-    auto err = do_lookup(parent, name, &e);
-    if (err) {
-        if (err == ENFILE || err == EMFILE)
-            cerr << "ERROR: Reached maximum number of file descriptors." << endl;
-        fuse_reply_err(req, err);
-	return;
-    }
-
-    Inode& inode = get_inode(e.ino);
-    lock_guard<mutex> g {inode.m};
-    inode.nopen++;
-    fuse_reply_create(req, &e, fi);
-}
-
-
-static void sfs_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
-                         fuse_file_info *fi) {
-    (void) ino;
-    int res;
-    int fd = dirfd(get_dir_handle(fi)->dp);
-    if (datasync)
-        res = fdatasync(fd);
-    else
-        res = fsync(fd);
-    fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-
-static void sfs_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
-    Inode& inode = get_inode(ino);
-
-    /* With writeback cache, kernel may send read requests even
-       when userspace opened write-only */
-    if (fs.timeout && (fi->flags & O_ACCMODE) == O_WRONLY) {
-        fi->flags &= ~O_ACCMODE;
-        fi->flags |= O_RDWR;
-    }
-
-    /* With writeback cache, O_APPEND is handled by the kernel.  This
-       breaks atomicity (since the file may change in the underlying
-       filesystem, so that the kernel's idea of the end of the file
-       isn't accurate anymore). However, no process should modify the
-       file in the underlying filesystem once it has been read, so
-       this is not a problem. */
-    if (fs.timeout && fi->flags & O_APPEND)
-        fi->flags &= ~O_APPEND;
-
-    /* Unfortunately we cannot use inode.fd, because this was opened
-       with O_PATH (so it doesn't allow read/write access). */
-    char buf[64];
-    sprintf(buf, "/proc/self/fd/%i", inode.fd);
-    auto fd = open(buf, fi->flags & ~O_NOFOLLOW);
-    if (fd == -1) {
-        auto err = errno;
-        if (err == ENFILE || err == EMFILE)
-            cerr << "ERROR: Reached maximum number of file descriptors." << endl;
-        fuse_reply_err(req, err);
-        return;
-    }
-
-    lock_guard<mutex> g {inode.m};
-    inode.nopen++;
-    fi->keep_cache = (fs.timeout != 0);
-    fi->fh = fd;
-    fuse_reply_open(req, fi);
-}
-
-
-static void sfs_release(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
-    Inode& inode = get_inode(ino);
-    lock_guard<mutex> g {inode.m};
-    inode.nopen--;
-    close(fi->fh);
-    fuse_reply_err(req, 0);
-}
-
-
-static void sfs_flush(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
-    (void) ino;
-    auto res = close(dup(fi->fh));
-    fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-
-static void sfs_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
-                      fuse_file_info *fi) {
-    (void) ino;
-    int res;
-    if (datasync)
-        res = fdatasync(fi->fh);
-    else
-        res = fsync(fi->fh);
-    fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-
-static void do_read(fuse_req_t req, size_t size, off_t off, fuse_file_info *fi) {
-
-    fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
-    buf.buf[0].flags = static_cast<fuse_buf_flags>(
-        FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
-    buf.buf[0].fd = fi->fh;
-    buf.buf[0].pos = off;
-
-    fuse_reply_data(req, &buf, FUSE_BUF_COPY_FLAGS);
-}
-
-static void sfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
-                     fuse_file_info *fi) {
-    (void) ino;
-    do_read(req, size, off, fi);
-}
-
-
-static void do_write_buf(fuse_req_t req, size_t size, off_t off,
-                         fuse_bufvec *in_buf, fuse_file_info *fi) {
-    fuse_bufvec out_buf = FUSE_BUFVEC_INIT(size);
-    out_buf.buf[0].flags = static_cast<fuse_buf_flags>(
-        FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
-    out_buf.buf[0].fd = fi->fh;
-    out_buf.buf[0].pos = off;
-
-    auto res = fuse_buf_copy(&out_buf, in_buf, FUSE_BUF_COPY_FLAGS);
-    if (res < 0)
-        fuse_reply_err(req, -res);
-    else
-        fuse_reply_write(req, (size_t)res);
-}
-
-
-static void sfs_write_buf(fuse_req_t req, fuse_ino_t ino, fuse_bufvec *in_buf,
-                          off_t off, fuse_file_info *fi) {
-    (void) ino;
-    auto size {fuse_buf_size(in_buf)};
-    do_write_buf(req, size, off, in_buf, fi);
-}
-
-
-static void sfs_statfs(fuse_req_t req, fuse_ino_t ino) {
-    struct statvfs stbuf;
-
-    auto res = fstatvfs(get_fs_fd(ino), &stbuf);
-    if (res == -1)
-        fuse_reply_err(req, errno);
-    else
-        fuse_reply_statfs(req, &stbuf);
-}
-
-
-#ifdef HAVE_POSIX_FALLOCATE
-static void sfs_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
-                          off_t offset, off_t length, fuse_file_info *fi) {
-    (void) ino;
-    if (mode) {
-        fuse_reply_err(req, EOPNOTSUPP);
-        return;
-    }
-
-    auto err = posix_fallocate(fi->fh, offset, length);
-    fuse_reply_err(req, err);
-}
-#endif
-
-static void sfs_flock(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi,
-                      int op) {
-    (void) ino;
-    auto res = flock(fi->fh, op);
-    fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-
-#ifdef HAVE_SETXATTR
-static void sfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
-                         size_t size) {
-    char *value = nullptr;
-    Inode& inode = get_inode(ino);
-    ssize_t ret;
-    int saverr;
-
-    char procname[64];
-    sprintf(procname, "/proc/self/fd/%i", inode.fd);
-
-    if (size) {
-        value = new (nothrow) char[size];
-        if (value == nullptr) {
-            saverr = ENOMEM;
-            goto out;
-        }
-
-        ret = getxattr(procname, name, value, size);
-        if (ret == -1)
-            goto out_err;
-        saverr = 0;
-        if (ret == 0)
-            goto out;
-
-        fuse_reply_buf(req, value, ret);
-    } else {
-        ret = getxattr(procname, name, nullptr, 0);
-        if (ret == -1)
-            goto out_err;
-
-        fuse_reply_xattr(req, ret);
-    }
-out_free:
-    delete[] value;
-    return;
-
-out_err:
-    saverr = errno;
-out:
-    fuse_reply_err(req, saverr);
-    goto out_free;
-}
-
-
-static void sfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
-    char *value = nullptr;
-    Inode& inode = get_inode(ino);
-    ssize_t ret;
-    int saverr;
-
-    char procname[64];
-    sprintf(procname, "/proc/self/fd/%i", inode.fd);
-
-    if (size) {
-        value = new (nothrow) char[size];
-        if (value == nullptr) {
-            saverr = ENOMEM;
-            goto out;
-        }
-
-        ret = listxattr(procname, value, size);
-        if (ret == -1)
-            goto out_err;
-        saverr = 0;
-        if (ret == 0)
-            goto out;
-
-        fuse_reply_buf(req, value, ret);
-    } else {
-        ret = listxattr(procname, nullptr, 0);
-        if (ret == -1)
-            goto out_err;
-
-        fuse_reply_xattr(req, ret);
-    }
-out_free:
-    delete[] value;
-    return;
-out_err:
-    saverr = errno;
-out:
-    fuse_reply_err(req, saverr);
-    goto out_free;
-}
-
-
-static void sfs_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
-                         const char *value, size_t size, int flags) {
-    Inode& inode = get_inode(ino);
-    ssize_t ret;
-    int saverr;
-
-    char procname[64];
-    sprintf(procname, "/proc/self/fd/%i", inode.fd);
-
-    ret = setxattr(procname, name, value, size, flags);
-    saverr = ret == -1 ? errno : 0;
-
-    fuse_reply_err(req, saverr);
-}
-
-
-static void sfs_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) {
-    char procname[64];
-    Inode& inode = get_inode(ino);
-    ssize_t ret;
-    int saverr;
-
-    sprintf(procname, "/proc/self/fd/%i", inode.fd);
-    ret = removexattr(procname, name);
-    saverr = ret == -1 ? errno : 0;
-
-    fuse_reply_err(req, saverr);
-}
-#endif
-
-
-static void assign_operations(fuse_lowlevel_ops &sfs_oper) {
-    sfs_oper.init = sfs_init;
-    sfs_oper.lookup = sfs_lookup;
-    sfs_oper.mkdir = sfs_mkdir;
-    sfs_oper.mknod = sfs_mknod;
-    sfs_oper.symlink = sfs_symlink;
-    sfs_oper.link = sfs_link;
-    sfs_oper.unlink = sfs_unlink;
-    sfs_oper.rmdir = sfs_rmdir;
-    sfs_oper.rename = sfs_rename;
-    sfs_oper.forget = sfs_forget;
-    sfs_oper.forget_multi = sfs_forget_multi;
-    sfs_oper.getattr = sfs_getattr;
-    sfs_oper.setattr = sfs_setattr;
-    sfs_oper.readlink = sfs_readlink;
-    sfs_oper.opendir = sfs_opendir;
-    sfs_oper.readdir = sfs_readdir;
-    sfs_oper.readdirplus = sfs_readdirplus;
-    sfs_oper.releasedir = sfs_releasedir;
-    sfs_oper.fsyncdir = sfs_fsyncdir;
-    sfs_oper.create = sfs_create;
-    sfs_oper.open = sfs_open;
-    sfs_oper.release = sfs_release;
-    sfs_oper.flush = sfs_flush;
-    sfs_oper.fsync = sfs_fsync;
-    sfs_oper.read = sfs_read;
-    sfs_oper.write_buf = sfs_write_buf;
-    sfs_oper.statfs = sfs_statfs;
-#ifdef HAVE_POSIX_FALLOCATE
-    sfs_oper.fallocate = sfs_fallocate;
-#endif
-    sfs_oper.flock = sfs_flock;
-#ifdef HAVE_SETXATTR
-    sfs_oper.setxattr = sfs_setxattr;
-    sfs_oper.getxattr = sfs_getxattr;
-    sfs_oper.listxattr = sfs_listxattr;
-    sfs_oper.removexattr = sfs_removexattr;
-#endif
-}
-
-static void print_usage(char *prog_name) {
-    cout << "Usage: " << prog_name << " --help\n"
-         << "       " << prog_name << " [options] <source> <mountpoint>\n";
-}
-
-static cxxopts::ParseResult parse_wrapper(cxxopts::Options& parser, int& argc, char**& argv) {
-    try {
-        return parser.parse(argc, argv);
-    } catch (cxxopts::option_not_exists_exception& exc) {
-        std::cout << argv[0] << ": " << exc.what() << std::endl;
-        print_usage(argv[0]);
-        exit(2);
-    }
-}
-
-
-static cxxopts::ParseResult parse_options(int argc, char **argv) {
-    cxxopts::Options opt_parser(argv[0]);
-    opt_parser.add_options()
-        ("debug", "Enable filesystem debug messages")
-        ("debug-fuse", "Enable libfuse debug messages")
-        ("help", "Print help")
-        ("nocache", "Disable all caching")
-        ("nosplice", "Do not use splice(2) to transfer data")
-        ("single", "Run single-threaded");
-
-    // FIXME: Find a better way to limit the try clause to just
-    // opt_parser.parse() (cf. https://github.com/jarro2783/cxxopts/issues/146)
-    auto options = parse_wrapper(opt_parser, argc, argv);
-
-    if (options.count("help")) {
-        print_usage(argv[0]);
-        // Strip everything before the option list from the
-        // default help string.
-        auto help = opt_parser.help();
-        std::cout << std::endl << "options:"
-                  << help.substr(help.find("\n\n") + 1, string::npos);
-        exit(0);
-
-    } else if (argc != 3) {
-        std::cout << argv[0] << ": invalid number of arguments\n";
-        print_usage(argv[0]);
-        exit(2);
-    }
-
-    fs.debug = options.count("debug") != 0;
-    fs.nosplice = options.count("nosplice") != 0;
-    char* resolved_path = realpath(argv[1], NULL);
-    if (resolved_path == NULL)
-        warn("WARNING: realpath() failed with");
-    fs.source = std::string {resolved_path};
-    free(resolved_path);
-
-    return options;
-}
-
-
-static void maximize_fd_limit() {
-    struct rlimit lim {};
-    auto res = getrlimit(RLIMIT_NOFILE, &lim);
-    if (res != 0) {
-        warn("WARNING: getrlimit() failed with");
-        return;
-    }
-    lim.rlim_cur = lim.rlim_max;
-    res = setrlimit(RLIMIT_NOFILE, &lim);
-    if (res != 0)
-        warn("WARNING: setrlimit() failed with");
-}
-
-
-int main(int argc, char *argv[]) {
-
-    // Parse command line options
-    auto options {parse_options(argc, argv)};
-
-    // We need an fd for every dentry in our the filesystem that the
-    // kernel knows about. This is way more than most processes need,
-    // so try to get rid of any resource softlimit.
-    maximize_fd_limit();
-
-    // Initialize filesystem root
-    fs.root.fd = -1;
-    fs.root.nlookup = 9999;
-    fs.timeout = options.count("nocache") ? 0 : 86400.0;
-
-    struct stat stat;
-    auto ret = lstat(fs.source.c_str(), &stat);
-    if (ret == -1)
-        err(1, "ERROR: failed to stat source (\"%s\")", fs.source.c_str());
-    if (!S_ISDIR(stat.st_mode))
-        errx(1, "ERROR: source is not a directory");
-    fs.src_dev = stat.st_dev;
-
-    fs.root.fd = open(fs.source.c_str(), O_PATH);
-    if (fs.root.fd == -1)
-        err(1, "ERROR: open(\"%s\", O_PATH)", fs.source.c_str());
-
-    // Initialize fuse
-    fuse_args args = FUSE_ARGS_INIT(0, nullptr);
-    if (fuse_opt_add_arg(&args, argv[0]) ||
-        fuse_opt_add_arg(&args, "-o") ||
-        fuse_opt_add_arg(&args, "default_permissions,fsname=hpps") ||
-        (options.count("debug-fuse") && fuse_opt_add_arg(&args, "-odebug")))
-        errx(3, "ERROR: Out of memory");
-
-    fuse_lowlevel_ops sfs_oper {};
-    assign_operations(sfs_oper);
-    auto se = fuse_session_new(&args, &sfs_oper, sizeof(sfs_oper), &fs);
-    if (se == nullptr)
-        goto err_out1;
-
-    if (fuse_set_signal_handlers(se) != 0)
-        goto err_out2;
-
-    // Don't apply umask, use modes exactly as specified
-    umask(0);
-
-    // Mount and run main loop
-    struct fuse_loop_config loop_config;
-    loop_config.clone_fd = 0;
-    loop_config.max_idle_threads = 10;
-    if (fuse_session_mount(se, argv[2]) != 0)
-        goto err_out3;
-    if (options.count("single"))
-        ret = fuse_session_loop(se);
-    else
-        ret = fuse_session_loop_mt(se, &loop_config);
-
-    fuse_session_unmount(se);
-
-err_out3:
-    fuse_remove_signal_handlers(se);
-err_out2:
-    fuse_session_destroy(se);
-err_out1:
-    fuse_opt_free_args(&args);
-
-    return ret ? 1 : 0;
-}
-
diff --git a/example/passthrough_ll.c b/example/passthrough_ll.c
deleted file mode 100644
index 8fdf887..0000000
--- a/example/passthrough_ll.c
+++ /dev/null
@@ -1,1290 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This file system mirrors the existing file system hierarchy of the
- * system, starting at the root file system. This is implemented by
- * just "passing through" all requests to the corresponding user-space
- * libc functions. In contrast to passthrough.c and passthrough_fh.c,
- * this implementation uses the low-level API. Its performance should
- * be the least bad among the three, but many operations are not
- * implemented. In particular, it is not possible to remove files (or
- * directories) because the code necessary to defer actual removal
- * until the file is not opened anymore would make the example much
- * more complicated.
- *
- * When writeback caching is enabled (-o writeback mount option), it
- * is only possible to write to files for which the mounting user has
- * read permissions. This is because the writeback cache requires the
- * kernel to be able to issue read requests for all files (which the
- * passthrough filesystem cannot satisfy if it can't read the file in
- * the underlying filesystem).
- *
- * Compile with:
- *
- *     gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll
- *
- * ## Source code ##
- * \include passthrough_ll.c
- */
-
-#define _GNU_SOURCE
-#define FUSE_USE_VERSION 34
-
-#include "config.h"
-
-#include <fuse_lowlevel.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include <string.h>
-#include <limits.h>
-#include <dirent.h>
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <sys/file.h>
-#include <sys/xattr.h>
-
-#include "passthrough_helpers.h"
-
-/* We are re-using pointers to our `struct lo_inode` and `struct
-   lo_dirp` elements as inodes. This means that we must be able to
-   store uintptr_t values in a fuse_ino_t variable. The following
-   incantation checks this condition at compile time. */
-#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
-_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
-	       "fuse_ino_t too small to hold uintptr_t values!");
-#else
-struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
-	{ unsigned _uintptr_to_must_hold_fuse_ino_t:
-			((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
-#endif
-
-struct lo_inode {
-	struct lo_inode *next; /* protected by lo->mutex */
-	struct lo_inode *prev; /* protected by lo->mutex */
-	int fd;
-	ino_t ino;
-	dev_t dev;
-	uint64_t refcount; /* protected by lo->mutex */
-};
-
-enum {
-	CACHE_NEVER,
-	CACHE_NORMAL,
-	CACHE_ALWAYS,
-};
-
-struct lo_data {
-	pthread_mutex_t mutex;
-	int debug;
-	int writeback;
-	int flock;
-	int xattr;
-	const char *source;
-	double timeout;
-	int cache;
-	int timeout_set;
-	struct lo_inode root; /* protected by lo->mutex */
-};
-
-static const struct fuse_opt lo_opts[] = {
-	{ "writeback",
-	  offsetof(struct lo_data, writeback), 1 },
-	{ "no_writeback",
-	  offsetof(struct lo_data, writeback), 0 },
-	{ "source=%s",
-	  offsetof(struct lo_data, source), 0 },
-	{ "flock",
-	  offsetof(struct lo_data, flock), 1 },
-	{ "no_flock",
-	  offsetof(struct lo_data, flock), 0 },
-	{ "xattr",
-	  offsetof(struct lo_data, xattr), 1 },
-	{ "no_xattr",
-	  offsetof(struct lo_data, xattr), 0 },
-	{ "timeout=%lf",
-	  offsetof(struct lo_data, timeout), 0 },
-	{ "timeout=",
-	  offsetof(struct lo_data, timeout_set), 1 },
-	{ "cache=never",
-	  offsetof(struct lo_data, cache), CACHE_NEVER },
-	{ "cache=auto",
-	  offsetof(struct lo_data, cache), CACHE_NORMAL },
-	{ "cache=always",
-	  offsetof(struct lo_data, cache), CACHE_ALWAYS },
-
-	FUSE_OPT_END
-};
-
-static struct lo_data *lo_data(fuse_req_t req)
-{
-	return (struct lo_data *) fuse_req_userdata(req);
-}
-
-static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
-{
-	if (ino == FUSE_ROOT_ID)
-		return &lo_data(req)->root;
-	else
-		return (struct lo_inode *) (uintptr_t) ino;
-}
-
-static int lo_fd(fuse_req_t req, fuse_ino_t ino)
-{
-	return lo_inode(req, ino)->fd;
-}
-
-static bool lo_debug(fuse_req_t req)
-{
-	return lo_data(req)->debug != 0;
-}
-
-static void lo_init(void *userdata,
-		    struct fuse_conn_info *conn)
-{
-	struct lo_data *lo = (struct lo_data*) userdata;
-
-	if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
-		conn->want |= FUSE_CAP_EXPORT_SUPPORT;
-
-	if (lo->writeback &&
-	    conn->capable & FUSE_CAP_WRITEBACK_CACHE) {
-		if (lo->debug)
-			fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n");
-		conn->want |= FUSE_CAP_WRITEBACK_CACHE;
-	}
-	if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
-		if (lo->debug)
-			fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n");
-		conn->want |= FUSE_CAP_FLOCK_LOCKS;
-	}
-}
-
-static void lo_destroy(void *userdata)
-{
-	struct lo_data *lo = (struct lo_data*) userdata;
-
-	while (lo->root.next != &lo->root) {
-		struct lo_inode* next = lo->root.next;
-		lo->root.next = next->next;
-		free(next);
-	}
-}
-
-static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
-			     struct fuse_file_info *fi)
-{
-	int res;
-	struct stat buf;
-	struct lo_data *lo = lo_data(req);
-
-	(void) fi;
-
-	res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
-	if (res == -1)
-		return (void) fuse_reply_err(req, errno);
-
-	fuse_reply_attr(req, &buf, lo->timeout);
-}
-
-static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
-		       int valid, struct fuse_file_info *fi)
-{
-	int saverr;
-	char procname[64];
-	struct lo_inode *inode = lo_inode(req, ino);
-	int ifd = inode->fd;
-	int res;
-
-	if (valid & FUSE_SET_ATTR_MODE) {
-		if (fi) {
-			res = fchmod(fi->fh, attr->st_mode);
-		} else {
-			sprintf(procname, "/proc/self/fd/%i", ifd);
-			res = chmod(procname, attr->st_mode);
-		}
-		if (res == -1)
-			goto out_err;
-	}
-	if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
-		uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
-			attr->st_uid : (uid_t) -1;
-		gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
-			attr->st_gid : (gid_t) -1;
-
-		res = fchownat(ifd, "", uid, gid,
-			       AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
-		if (res == -1)
-			goto out_err;
-	}
-	if (valid & FUSE_SET_ATTR_SIZE) {
-		if (fi) {
-			res = ftruncate(fi->fh, attr->st_size);
-		} else {
-			sprintf(procname, "/proc/self/fd/%i", ifd);
-			res = truncate(procname, attr->st_size);
-		}
-		if (res == -1)
-			goto out_err;
-	}
-	if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
-		struct timespec tv[2];
-
-		tv[0].tv_sec = 0;
-		tv[1].tv_sec = 0;
-		tv[0].tv_nsec = UTIME_OMIT;
-		tv[1].tv_nsec = UTIME_OMIT;
-
-		if (valid & FUSE_SET_ATTR_ATIME_NOW)
-			tv[0].tv_nsec = UTIME_NOW;
-		else if (valid & FUSE_SET_ATTR_ATIME)
-			tv[0] = attr->st_atim;
-
-		if (valid & FUSE_SET_ATTR_MTIME_NOW)
-			tv[1].tv_nsec = UTIME_NOW;
-		else if (valid & FUSE_SET_ATTR_MTIME)
-			tv[1] = attr->st_mtim;
-
-		if (fi)
-			res = futimens(fi->fh, tv);
-		else {
-			sprintf(procname, "/proc/self/fd/%i", ifd);
-			res = utimensat(AT_FDCWD, procname, tv, 0);
-		}
-		if (res == -1)
-			goto out_err;
-	}
-
-	return lo_getattr(req, ino, fi);
-
-out_err:
-	saverr = errno;
-	fuse_reply_err(req, saverr);
-}
-
-static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
-{
-	struct lo_inode *p;
-	struct lo_inode *ret = NULL;
-
-	pthread_mutex_lock(&lo->mutex);
-	for (p = lo->root.next; p != &lo->root; p = p->next) {
-		if (p->ino == st->st_ino && p->dev == st->st_dev) {
-			assert(p->refcount > 0);
-			ret = p;
-			ret->refcount++;
-			break;
-		}
-	}
-	pthread_mutex_unlock(&lo->mutex);
-	return ret;
-}
-
-static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
-			 struct fuse_entry_param *e)
-{
-	int newfd;
-	int res;
-	int saverr;
-	struct lo_data *lo = lo_data(req);
-	struct lo_inode *inode;
-
-	memset(e, 0, sizeof(*e));
-	e->attr_timeout = lo->timeout;
-	e->entry_timeout = lo->timeout;
-
-	newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
-	if (newfd == -1)
-		goto out_err;
-
-	res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
-	if (res == -1)
-		goto out_err;
-
-	inode = lo_find(lo_data(req), &e->attr);
-	if (inode) {
-		close(newfd);
-		newfd = -1;
-	} else {
-		struct lo_inode *prev, *next;
-
-		saverr = ENOMEM;
-		inode = calloc(1, sizeof(struct lo_inode));
-		if (!inode)
-			goto out_err;
-
-		inode->refcount = 1;
-		inode->fd = newfd;
-		inode->ino = e->attr.st_ino;
-		inode->dev = e->attr.st_dev;
-
-		pthread_mutex_lock(&lo->mutex);
-		prev = &lo->root;
-		next = prev->next;
-		next->prev = inode;
-		inode->next = next;
-		inode->prev = prev;
-		prev->next = inode;
-		pthread_mutex_unlock(&lo->mutex);
-	}
-	e->ino = (uintptr_t) inode;
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "  %lli/%s -> %lli\n",
-			(unsigned long long) parent, name, (unsigned long long) e->ino);
-
-	return 0;
-
-out_err:
-	saverr = errno;
-	if (newfd != -1)
-		close(newfd);
-	return saverr;
-}
-
-static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
-{
-	struct fuse_entry_param e;
-	int err;
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
-			parent, name);
-
-	err = lo_do_lookup(req, parent, name, &e);
-	if (err)
-		fuse_reply_err(req, err);
-	else
-		fuse_reply_entry(req, &e);
-}
-
-static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
-			     const char *name, mode_t mode, dev_t rdev,
-			     const char *link)
-{
-	int res;
-	int saverr;
-	struct lo_inode *dir = lo_inode(req, parent);
-	struct fuse_entry_param e;
-
-	res = mknod_wrapper(dir->fd, name, link, mode, rdev);
-
-	saverr = errno;
-	if (res == -1)
-		goto out;
-
-	saverr = lo_do_lookup(req, parent, name, &e);
-	if (saverr)
-		goto out;
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "  %lli/%s -> %lli\n",
-			(unsigned long long) parent, name, (unsigned long long) e.ino);
-
-	fuse_reply_entry(req, &e);
-	return;
-
-out:
-	fuse_reply_err(req, saverr);
-}
-
-static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
-		     const char *name, mode_t mode, dev_t rdev)
-{
-	lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
-}
-
-static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
-		     mode_t mode)
-{
-	lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
-}
-
-static void lo_symlink(fuse_req_t req, const char *link,
-		       fuse_ino_t parent, const char *name)
-{
-	lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
-}
-
-static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
-		    const char *name)
-{
-	int res;
-	struct lo_data *lo = lo_data(req);
-	struct lo_inode *inode = lo_inode(req, ino);
-	struct fuse_entry_param e;
-	char procname[64];
-	int saverr;
-
-	memset(&e, 0, sizeof(struct fuse_entry_param));
-	e.attr_timeout = lo->timeout;
-	e.entry_timeout = lo->timeout;
-
-	sprintf(procname, "/proc/self/fd/%i", inode->fd);
-	res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
-		     AT_SYMLINK_FOLLOW);
-	if (res == -1)
-		goto out_err;
-
-	res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
-	if (res == -1)
-		goto out_err;
-
-	pthread_mutex_lock(&lo->mutex);
-	inode->refcount++;
-	pthread_mutex_unlock(&lo->mutex);
-	e.ino = (uintptr_t) inode;
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "  %lli/%s -> %lli\n",
-			(unsigned long long) parent, name,
-			(unsigned long long) e.ino);
-
-	fuse_reply_entry(req, &e);
-	return;
-
-out_err:
-	saverr = errno;
-	fuse_reply_err(req, saverr);
-}
-
-static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
-{
-	int res;
-
-	res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
-
-	fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
-		      fuse_ino_t newparent, const char *newname,
-		      unsigned int flags)
-{
-	int res;
-
-	if (flags) {
-		fuse_reply_err(req, EINVAL);
-		return;
-	}
-
-	res = renameat(lo_fd(req, parent), name,
-			lo_fd(req, newparent), newname);
-
-	fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
-{
-	int res;
-
-	res = unlinkat(lo_fd(req, parent), name, 0);
-
-	fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
-{
-	if (!inode)
-		return;
-
-	pthread_mutex_lock(&lo->mutex);
-	assert(inode->refcount >= n);
-	inode->refcount -= n;
-	if (!inode->refcount) {
-		struct lo_inode *prev, *next;
-
-		prev = inode->prev;
-		next = inode->next;
-		next->prev = prev;
-		prev->next = next;
-
-		pthread_mutex_unlock(&lo->mutex);
-		close(inode->fd);
-		free(inode);
-
-	} else {
-		pthread_mutex_unlock(&lo->mutex);
-	}
-}
-
-static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
-{
-	struct lo_data *lo = lo_data(req);
-	struct lo_inode *inode = lo_inode(req, ino);
-
-	if (lo_debug(req)) {
-		fuse_log(FUSE_LOG_DEBUG, "  forget %lli %lli -%lli\n",
-			(unsigned long long) ino,
-			(unsigned long long) inode->refcount,
-			(unsigned long long) nlookup);
-	}
-
-	unref_inode(lo, inode, nlookup);
-}
-
-static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
-{
-	lo_forget_one(req, ino, nlookup);
-	fuse_reply_none(req);
-}
-
-static void lo_forget_multi(fuse_req_t req, size_t count,
-				struct fuse_forget_data *forgets)
-{
-	int i;
-
-	for (i = 0; i < count; i++)
-		lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
-	fuse_reply_none(req);
-}
-
-static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
-{
-	char buf[PATH_MAX + 1];
-	int res;
-
-	res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
-	if (res == -1)
-		return (void) fuse_reply_err(req, errno);
-
-	if (res == sizeof(buf))
-		return (void) fuse_reply_err(req, ENAMETOOLONG);
-
-	buf[res] = '\0';
-
-	fuse_reply_readlink(req, buf);
-}
-
-struct lo_dirp {
-	DIR *dp;
-	struct dirent *entry;
-	off_t offset;
-};
-
-static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
-{
-	return (struct lo_dirp *) (uintptr_t) fi->fh;
-}
-
-static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
-{
-	int error = ENOMEM;
-	struct lo_data *lo = lo_data(req);
-	struct lo_dirp *d;
-	int fd;
-
-	d = calloc(1, sizeof(struct lo_dirp));
-	if (d == NULL)
-		goto out_err;
-
-	fd = openat(lo_fd(req, ino), ".", O_RDONLY);
-	if (fd == -1)
-		goto out_errno;
-
-	d->dp = fdopendir(fd);
-	if (d->dp == NULL)
-		goto out_errno;
-
-	d->offset = 0;
-	d->entry = NULL;
-
-	fi->fh = (uintptr_t) d;
-	if (lo->cache == CACHE_ALWAYS)
-		fi->cache_readdir = 1;
-	fuse_reply_open(req, fi);
-	return;
-
-out_errno:
-	error = errno;
-out_err:
-	if (d) {
-		if (fd != -1)
-			close(fd);
-		free(d);
-	}
-	fuse_reply_err(req, error);
-}
-
-static int is_dot_or_dotdot(const char *name)
-{
-	return name[0] == '.' && (name[1] == '\0' ||
-				  (name[1] == '.' && name[2] == '\0'));
-}
-
-static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
-			  off_t offset, struct fuse_file_info *fi, int plus)
-{
-	struct lo_dirp *d = lo_dirp(fi);
-	char *buf;
-	char *p;
-	size_t rem = size;
-	int err;
-
-	(void) ino;
-
-	buf = calloc(1, size);
-	if (!buf) {
-		err = ENOMEM;
-		goto error;
-	}
-	p = buf;
-
-	if (offset != d->offset) {
-		seekdir(d->dp, offset);
-		d->entry = NULL;
-		d->offset = offset;
-	}
-	while (1) {
-		size_t entsize;
-		off_t nextoff;
-		const char *name;
-
-		if (!d->entry) {
-			errno = 0;
-			d->entry = readdir(d->dp);
-			if (!d->entry) {
-				if (errno) {  // Error
-					err = errno;
-					goto error;
-				} else {  // End of stream
-					break; 
-				}
-			}
-		}
-		nextoff = d->entry->d_off;
-		name = d->entry->d_name;
-		fuse_ino_t entry_ino = 0;
-		if (plus) {
-			struct fuse_entry_param e;
-			if (is_dot_or_dotdot(name)) {
-				e = (struct fuse_entry_param) {
-					.attr.st_ino = d->entry->d_ino,
-					.attr.st_mode = d->entry->d_type << 12,
-				};
-			} else {
-				err = lo_do_lookup(req, ino, name, &e);
-				if (err)
-					goto error;
-				entry_ino = e.ino;
-			}
-
-			entsize = fuse_add_direntry_plus(req, p, rem, name,
-							 &e, nextoff);
-		} else {
-			struct stat st = {
-				.st_ino = d->entry->d_ino,
-				.st_mode = d->entry->d_type << 12,
-			};
-			entsize = fuse_add_direntry(req, p, rem, name,
-						    &st, nextoff);
-		}
-		if (entsize > rem) {
-			if (entry_ino != 0) 
-				lo_forget_one(req, entry_ino, 1);
-			break;
-		}
-		
-		p += entsize;
-		rem -= entsize;
-
-		d->entry = NULL;
-		d->offset = nextoff;
-	}
-
-    err = 0;
-error:
-    // If there's an error, we can only signal it if we haven't stored
-    // any entries yet - otherwise we'd end up with wrong lookup
-    // counts for the entries that are already in the buffer. So we
-    // return what we've collected until that point.
-    if (err && rem == size)
-	    fuse_reply_err(req, err);
-    else
-	    fuse_reply_buf(req, buf, size - rem);
-    free(buf);
-}
-
-static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
-		       off_t offset, struct fuse_file_info *fi)
-{
-	lo_do_readdir(req, ino, size, offset, fi, 0);
-}
-
-static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
-			   off_t offset, struct fuse_file_info *fi)
-{
-	lo_do_readdir(req, ino, size, offset, fi, 1);
-}
-
-static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
-{
-	struct lo_dirp *d = lo_dirp(fi);
-	(void) ino;
-	closedir(d->dp);
-	free(d);
-	fuse_reply_err(req, 0);
-}
-
-static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
-		      mode_t mode, struct fuse_file_info *fi)
-{
-	int fd;
-	struct lo_data *lo = lo_data(req);
-	struct fuse_entry_param e;
-	int err;
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n",
-			parent, name);
-
-	fd = openat(lo_fd(req, parent), name,
-		    (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
-	if (fd == -1)
-		return (void) fuse_reply_err(req, errno);
-
-	fi->fh = fd;
-	if (lo->cache == CACHE_NEVER)
-		fi->direct_io = 1;
-	else if (lo->cache == CACHE_ALWAYS)
-		fi->keep_cache = 1;
-
-	err = lo_do_lookup(req, parent, name, &e);
-	if (err)
-		fuse_reply_err(req, err);
-	else
-		fuse_reply_create(req, &e, fi);
-}
-
-static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
-			struct fuse_file_info *fi)
-{
-	int res;
-	int fd = dirfd(lo_dirp(fi)->dp);
-	(void) ino;
-	if (datasync)
-		res = fdatasync(fd);
-	else
-		res = fsync(fd);
-	fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
-{
-	int fd;
-	char buf[64];
-	struct lo_data *lo = lo_data(req);
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
-			ino, fi->flags);
-
-	/* With writeback cache, kernel may send read requests even
-	   when userspace opened write-only */
-	if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) {
-		fi->flags &= ~O_ACCMODE;
-		fi->flags |= O_RDWR;
-	}
-
-	/* With writeback cache, O_APPEND is handled by the kernel.
-	   This breaks atomicity (since the file may change in the
-	   underlying filesystem, so that the kernel's idea of the
-	   end of the file isn't accurate anymore). In this example,
-	   we just accept that. A more rigorous filesystem may want
-	   to return an error here */
-	if (lo->writeback && (fi->flags & O_APPEND))
-		fi->flags &= ~O_APPEND;
-
-	sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
-	fd = open(buf, fi->flags & ~O_NOFOLLOW);
-	if (fd == -1)
-		return (void) fuse_reply_err(req, errno);
-
-	fi->fh = fd;
-	if (lo->cache == CACHE_NEVER)
-		fi->direct_io = 1;
-	else if (lo->cache == CACHE_ALWAYS)
-		fi->keep_cache = 1;
-	fuse_reply_open(req, fi);
-}
-
-static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
-{
-	(void) ino;
-
-	close(fi->fh);
-	fuse_reply_err(req, 0);
-}
-
-static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
-{
-	int res;
-	(void) ino;
-	res = close(dup(fi->fh));
-	fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
-		     struct fuse_file_info *fi)
-{
-	int res;
-	(void) ino;
-	if (datasync)
-		res = fdatasync(fi->fh);
-	else
-		res = fsync(fi->fh);
-	fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
-		    off_t offset, struct fuse_file_info *fi)
-{
-	struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, "
-			"off=%lu)\n", ino, size, (unsigned long) offset);
-
-	buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
-	buf.buf[0].fd = fi->fh;
-	buf.buf[0].pos = offset;
-
-	fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
-}
-
-static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
-			 struct fuse_bufvec *in_buf, off_t off,
-			 struct fuse_file_info *fi)
-{
-	(void) ino;
-	ssize_t res;
-	struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
-
-	out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
-	out_buf.buf[0].fd = fi->fh;
-	out_buf.buf[0].pos = off;
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n",
-			ino, out_buf.buf[0].size, (unsigned long) off);
-
-	res = fuse_buf_copy(&out_buf, in_buf, 0);
-	if(res < 0)
-		fuse_reply_err(req, -res);
-	else
-		fuse_reply_write(req, (size_t) res);
-}
-
-static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
-{
-	int res;
-	struct statvfs stbuf;
-
-	res = fstatvfs(lo_fd(req, ino), &stbuf);
-	if (res == -1)
-		fuse_reply_err(req, errno);
-	else
-		fuse_reply_statfs(req, &stbuf);
-}
-
-static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
-			 off_t offset, off_t length, struct fuse_file_info *fi)
-{
-	int err = EOPNOTSUPP;
-	(void) ino;
-
-#ifdef HAVE_FALLOCATE
-	err = fallocate(fi->fh, mode, offset, length);
-	if (err < 0)
-		err = errno;
-
-#elif defined(HAVE_POSIX_FALLOCATE)
-	if (mode) {
-		fuse_reply_err(req, EOPNOTSUPP);
-		return;
-	}
-
-	err = posix_fallocate(fi->fh, offset, length);
-#endif
-
-	fuse_reply_err(req, err);
-}
-
-static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
-		     int op)
-{
-	int res;
-	(void) ino;
-
-	res = flock(fi->fh, op);
-
-	fuse_reply_err(req, res == -1 ? errno : 0);
-}
-
-static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
-			size_t size)
-{
-	char *value = NULL;
-	char procname[64];
-	struct lo_inode *inode = lo_inode(req, ino);
-	ssize_t ret;
-	int saverr;
-
-	saverr = ENOSYS;
-	if (!lo_data(req)->xattr)
-		goto out;
-
-	if (lo_debug(req)) {
-		fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n",
-			ino, name, size);
-	}
-
-	sprintf(procname, "/proc/self/fd/%i", inode->fd);
-
-	if (size) {
-		value = malloc(size);
-		if (!value)
-			goto out_err;
-
-		ret = getxattr(procname, name, value, size);
-		if (ret == -1)
-			goto out_err;
-		saverr = 0;
-		if (ret == 0)
-			goto out;
-
-		fuse_reply_buf(req, value, ret);
-	} else {
-		ret = getxattr(procname, name, NULL, 0);
-		if (ret == -1)
-			goto out_err;
-
-		fuse_reply_xattr(req, ret);
-	}
-out_free:
-	free(value);
-	return;
-
-out_err:
-	saverr = errno;
-out:
-	fuse_reply_err(req, saverr);
-	goto out_free;
-}
-
-static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
-{
-	char *value = NULL;
-	char procname[64];
-	struct lo_inode *inode = lo_inode(req, ino);
-	ssize_t ret;
-	int saverr;
-
-	saverr = ENOSYS;
-	if (!lo_data(req)->xattr)
-		goto out;
-
-	if (lo_debug(req)) {
-		fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n",
-			ino, size);
-	}
-
-	sprintf(procname, "/proc/self/fd/%i", inode->fd);
-
-	if (size) {
-		value = malloc(size);
-		if (!value)
-			goto out_err;
-
-		ret = listxattr(procname, value, size);
-		if (ret == -1)
-			goto out_err;
-		saverr = 0;
-		if (ret == 0)
-			goto out;
-
-		fuse_reply_buf(req, value, ret);
-	} else {
-		ret = listxattr(procname, NULL, 0);
-		if (ret == -1)
-			goto out_err;
-
-		fuse_reply_xattr(req, ret);
-	}
-out_free:
-	free(value);
-	return;
-
-out_err:
-	saverr = errno;
-out:
-	fuse_reply_err(req, saverr);
-	goto out_free;
-}
-
-static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
-			const char *value, size_t size, int flags)
-{
-	char procname[64];
-	struct lo_inode *inode = lo_inode(req, ino);
-	ssize_t ret;
-	int saverr;
-
-	saverr = ENOSYS;
-	if (!lo_data(req)->xattr)
-		goto out;
-
-	if (lo_debug(req)) {
-		fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n",
-			ino, name, value, size);
-	}
-
-	sprintf(procname, "/proc/self/fd/%i", inode->fd);
-
-	ret = setxattr(procname, name, value, size, flags);
-	saverr = ret == -1 ? errno : 0;
-
-out:
-	fuse_reply_err(req, saverr);
-}
-
-static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
-{
-	char procname[64];
-	struct lo_inode *inode = lo_inode(req, ino);
-	ssize_t ret;
-	int saverr;
-
-	saverr = ENOSYS;
-	if (!lo_data(req)->xattr)
-		goto out;
-
-	if (lo_debug(req)) {
-		fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n",
-			ino, name);
-	}
-
-	sprintf(procname, "/proc/self/fd/%i", inode->fd);
-
-	ret = removexattr(procname, name);
-	saverr = ret == -1 ? errno : 0;
-
-out:
-	fuse_reply_err(req, saverr);
-}
-
-#ifdef HAVE_COPY_FILE_RANGE
-static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
-			       struct fuse_file_info *fi_in,
-			       fuse_ino_t ino_out, off_t off_out,
-			       struct fuse_file_info *fi_out, size_t len,
-			       int flags)
-{
-	ssize_t res;
-
-	if (lo_debug(req))
-		fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
-				"off=%lu, ino=%" PRIu64 "/fd=%lu, "
-				"off=%lu, size=%zd, flags=0x%x)\n",
-			ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out,
-			len, flags);
-
-	res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
-			      flags);
-	if (res < 0)
-		fuse_reply_err(req, errno);
-	else
-		fuse_reply_write(req, res);
-}
-#endif
-
-static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
-		     struct fuse_file_info *fi)
-{
-	off_t res;
-
-	(void)ino;
-	res = lseek(fi->fh, off, whence);
-	if (res != -1)
-		fuse_reply_lseek(req, res);
-	else
-		fuse_reply_err(req, errno);
-}
-
-static const struct fuse_lowlevel_ops lo_oper = {
-	.init		= lo_init,
-	.destroy	= lo_destroy,
-	.lookup		= lo_lookup,
-	.mkdir		= lo_mkdir,
-	.mknod		= lo_mknod,
-	.symlink	= lo_symlink,
-	.link		= lo_link,
-	.unlink		= lo_unlink,
-	.rmdir		= lo_rmdir,
-	.rename		= lo_rename,
-	.forget		= lo_forget,
-	.forget_multi	= lo_forget_multi,
-	.getattr	= lo_getattr,
-	.setattr	= lo_setattr,
-	.readlink	= lo_readlink,
-	.opendir	= lo_opendir,
-	.readdir	= lo_readdir,
-	.readdirplus	= lo_readdirplus,
-	.releasedir	= lo_releasedir,
-	.fsyncdir	= lo_fsyncdir,
-	.create		= lo_create,
-	.open		= lo_open,
-	.release	= lo_release,
-	.flush		= lo_flush,
-	.fsync		= lo_fsync,
-	.read		= lo_read,
-	.write_buf      = lo_write_buf,
-	.statfs		= lo_statfs,
-	.fallocate	= lo_fallocate,
-	.flock		= lo_flock,
-	.getxattr	= lo_getxattr,
-	.listxattr	= lo_listxattr,
-	.setxattr	= lo_setxattr,
-	.removexattr	= lo_removexattr,
-#ifdef HAVE_COPY_FILE_RANGE
-	.copy_file_range = lo_copy_file_range,
-#endif
-	.lseek		= lo_lseek,
-};
-
-int main(int argc, char *argv[])
-{
-	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-	struct fuse_session *se;
-	struct fuse_cmdline_opts opts;
-	struct fuse_loop_config config;
-	struct lo_data lo = { .debug = 0,
-	                      .writeback = 0 };
-	int ret = -1;
-
-	/* Don't mask creation mode, kernel already did that */
-	umask(0);
-
-	pthread_mutex_init(&lo.mutex, NULL);
-	lo.root.next = lo.root.prev = &lo.root;
-	lo.root.fd = -1;
-	lo.cache = CACHE_NORMAL;
-
-	if (fuse_parse_cmdline(&args, &opts) != 0)
-		return 1;
-	if (opts.show_help) {
-		printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
-		fuse_cmdline_help();
-		fuse_lowlevel_help();
-		ret = 0;
-		goto err_out1;
-	} else if (opts.show_version) {
-		printf("FUSE library version %s\n", fuse_pkgversion());
-		fuse_lowlevel_version();
-		ret = 0;
-		goto err_out1;
-	}
-
-	if(opts.mountpoint == NULL) {
-		printf("usage: %s [options] <mountpoint>\n", argv[0]);
-		printf("       %s --help\n", argv[0]);
-		ret = 1;
-		goto err_out1;
-	}
-
-	if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
-		return 1;
-
-	lo.debug = opts.debug;
-	lo.root.refcount = 2;
-	if (lo.source) {
-		struct stat stat;
-		int res;
-
-		res = lstat(lo.source, &stat);
-		if (res == -1) {
-			fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n",
-				 lo.source);
-			exit(1);
-		}
-		if (!S_ISDIR(stat.st_mode)) {
-			fuse_log(FUSE_LOG_ERR, "source is not a directory\n");
-			exit(1);
-		}
-
-	} else {
-		lo.source = "/";
-	}
-	if (!lo.timeout_set) {
-		switch (lo.cache) {
-		case CACHE_NEVER:
-			lo.timeout = 0.0;
-			break;
-
-		case CACHE_NORMAL:
-			lo.timeout = 1.0;
-			break;
-
-		case CACHE_ALWAYS:
-			lo.timeout = 86400.0;
-			break;
-		}
-	} else if (lo.timeout < 0) {
-		fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n",
-			 lo.timeout);
-		exit(1);
-	}
-
-	lo.root.fd = open(lo.source, O_PATH);
-	if (lo.root.fd == -1) {
-		fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n",
-			 lo.source);
-		exit(1);
-	}
-
-	se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
-	if (se == NULL)
-	    goto err_out1;
-
-	if (fuse_set_signal_handlers(se) != 0)
-	    goto err_out2;
-
-	if (fuse_session_mount(se, opts.mountpoint) != 0)
-	    goto err_out3;
-
-	fuse_daemonize(opts.foreground);
-
-	/* Block until ctrl+c or fusermount -u */
-	if (opts.singlethread)
-		ret = fuse_session_loop(se);
-	else {
-		config.clone_fd = opts.clone_fd;
-		config.max_idle_threads = opts.max_idle_threads;
-		ret = fuse_session_loop_mt(se, &config);
-	}
-
-	fuse_session_unmount(se);
-err_out3:
-	fuse_remove_signal_handlers(se);
-err_out2:
-	fuse_session_destroy(se);
-err_out1:
-	free(opts.mountpoint);
-	fuse_opt_free_args(&args);
-
-	if (lo.root.fd >= 0)
-		close(lo.root.fd);
-
-	return ret ? 1 : 0;
-}
diff --git a/example/poll.c b/example/poll.c
deleted file mode 100644
index 521e6aa..0000000
--- a/example/poll.c
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
-  FUSE fsel: FUSE select example
-  Copyright (C) 2008       SUSE Linux Products GmbH
-  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This example illustrates how to write a FUSE file system that
- * supports polling for changes that don't come through the kernel. It
- * can be tested with the poll_client.c program.
- *
- * Compile with:
- *
- *     gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll
- *
- * ## Source code ##
- * \include poll.c
- */
-
-#define FUSE_USE_VERSION 31
-
-#include <config.h>
-
-#include <fuse.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <time.h>
-#include <pthread.h>
-#include <poll.h>
-
-/*
- * fsel_open_mask is used to limit the number of opens to 1 per file.
- * This is to use file index (0-F) as fh as poll support requires
- * unique fh per open file.  Lifting this would require proper open
- * file management.
- */
-static unsigned fsel_open_mask;
-static const char fsel_hex_map[] = "0123456789ABCDEF";
-static struct fuse *fsel_fuse;	/* needed for poll notification */
-
-#define FSEL_CNT_MAX	10	/* each file can store upto 10 chars */
-#define FSEL_FILES	16
-
-static pthread_mutex_t fsel_mutex;	/* protects notify_mask and cnt array */
-static unsigned fsel_poll_notify_mask;	/* poll notification scheduled? */
-static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
-static unsigned fsel_cnt[FSEL_FILES];	/* nbytes stored in each file */
-
-static int fsel_path_index(const char *path)
-{
-	char ch = path[1];
-
-	if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
-		return -1;
-	return ch <= '9' ? ch - '0' : ch - 'A' + 10;
-}
-
-static int fsel_getattr(const char *path, struct stat *stbuf,
-			struct fuse_file_info *fi)
-{
-	(void) fi;
-	int idx;
-
-	memset(stbuf, 0, sizeof(struct stat));
-
-	if (strcmp(path, "/") == 0) {
-		stbuf->st_mode = S_IFDIR | 0555;
-		stbuf->st_nlink = 2;
-		return 0;
-	}
-
-	idx = fsel_path_index(path);
-	if (idx < 0)
-		return -ENOENT;
-
-	stbuf->st_mode = S_IFREG | 0444;
-	stbuf->st_nlink = 1;
-	stbuf->st_size = fsel_cnt[idx];
-	return 0;
-}
-
-static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-			off_t offset, struct fuse_file_info *fi,
-			enum fuse_readdir_flags flags)
-{
-	char name[2] = { };
-	int i;
-
-	(void) offset;
-	(void) fi;
-	(void) flags;
-
-	if (strcmp(path, "/") != 0)
-		return -ENOENT;
-
-	for (i = 0; i < FSEL_FILES; i++) {
-		name[0] = fsel_hex_map[i];
-		filler(buf, name, NULL, 0, 0);
-	}
-
-	return 0;
-}
-
-static int fsel_open(const char *path, struct fuse_file_info *fi)
-{
-	int idx = fsel_path_index(path);
-
-	if (idx < 0)
-		return -ENOENT;
-	if ((fi->flags & O_ACCMODE) != O_RDONLY)
-		return -EACCES;
-	if (fsel_open_mask & (1 << idx))
-		return -EBUSY;
-	fsel_open_mask |= (1 << idx);
-
-	/*
-	 * fsel files are nonseekable somewhat pipe-like files which
-	 * gets filled up periodically by producer thread and consumed
-	 * on read.  Tell FUSE as such.
-	 */
-	fi->fh = idx;
-	fi->direct_io = 1;
-	fi->nonseekable = 1;
-
-	return 0;
-}
-
-static int fsel_release(const char *path, struct fuse_file_info *fi)
-{
-	int idx = fi->fh;
-
-	(void) path;
-
-	fsel_open_mask &= ~(1 << idx);
-	return 0;
-}
-
-static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
-		     struct fuse_file_info *fi)
-{
-	int idx = fi->fh;
-
-	(void) path;
-	(void) offset;
-
-	pthread_mutex_lock(&fsel_mutex);
-	if (fsel_cnt[idx] < size)
-		size = fsel_cnt[idx];
-	printf("READ   %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
-	fsel_cnt[idx] -= size;
-	pthread_mutex_unlock(&fsel_mutex);
-
-	memset(buf, fsel_hex_map[idx], size);
-	return size;
-}
-
-static int fsel_poll(const char *path, struct fuse_file_info *fi,
-		     struct fuse_pollhandle *ph, unsigned *reventsp)
-{
-	static unsigned polled_zero;
-	int idx = fi->fh;
-
-	(void) path;
-
-	/*
-	 * Poll notification requires pointer to struct fuse which
-	 * can't be obtained when using fuse_main().  As notification
-	 * happens only after poll is called, fill it here from
-	 * fuse_context.
-	 */
-	if (!fsel_fuse) {
-		struct fuse_context *cxt = fuse_get_context();
-		if (cxt)
-			fsel_fuse = cxt->fuse;
-	}
-
-	pthread_mutex_lock(&fsel_mutex);
-
-	if (ph != NULL) {
-		struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
-
-		if (oldph)
-			fuse_pollhandle_destroy(oldph);
-
-		fsel_poll_notify_mask |= (1 << idx);
-		fsel_poll_handle[idx] = ph;
-	}
-
-	if (fsel_cnt[idx]) {
-		*reventsp |= POLLIN;
-		printf("POLL   %X cnt=%u polled_zero=%u\n",
-		       idx, fsel_cnt[idx], polled_zero);
-		polled_zero = 0;
-	} else
-		polled_zero++;
-
-	pthread_mutex_unlock(&fsel_mutex);
-	return 0;
-}
-
-static const struct fuse_operations fsel_oper = {
-	.getattr	= fsel_getattr,
-	.readdir	= fsel_readdir,
-	.open		= fsel_open,
-	.release	= fsel_release,
-	.read		= fsel_read,
-	.poll		= fsel_poll,
-};
-
-static void *fsel_producer(void *data)
-{
-	const struct timespec interval = { 0, 250000000 };
-	unsigned idx = 0, nr = 1;
-
-	(void) data;
-
-	while (1) {
-		int i, t;
-
-		pthread_mutex_lock(&fsel_mutex);
-
-		/*
-		 * This is the main producer loop which is executed
-		 * ever 500ms.  On each iteration, it fills one byte
-		 * to 1, 2 or 4 files and sends poll notification if
-		 * requested.
-		 */
-		for (i = 0, t = idx; i < nr;
-		     i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
-			if (fsel_cnt[t] == FSEL_CNT_MAX)
-				continue;
-
-			fsel_cnt[t]++;
-			if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
-				struct fuse_pollhandle *ph;
-
-				printf("NOTIFY %X\n", t);
-				ph = fsel_poll_handle[t];
-				fuse_notify_poll(ph);
-				fuse_pollhandle_destroy(ph);
-				fsel_poll_notify_mask &= ~(1 << t);
-				fsel_poll_handle[t] = NULL;
-			}
-		}
-
-		idx = (idx + 1) % FSEL_FILES;
-		if (idx == 0)
-			nr = (nr * 2) % 7;	/* cycle through 1, 2 and 4 */
-
-		pthread_mutex_unlock(&fsel_mutex);
-
-		nanosleep(&interval, NULL);
-	}
-
-	return NULL;
-}
-
-int main(int argc, char *argv[])
-{
-	pthread_t producer;
-	pthread_attr_t attr;
-	int ret;
-
-	errno = pthread_mutex_init(&fsel_mutex, NULL);
-	if (errno) {
-		perror("pthread_mutex_init");
-		return 1;
-	}
-
-	errno = pthread_attr_init(&attr);
-	if (errno) {
-		perror("pthread_attr_init");
-		return 1;
-	}
-
-	errno = pthread_create(&producer, &attr, fsel_producer, NULL);
-	if (errno) {
-		perror("pthread_create");
-		return 1;
-	}
-
-	ret = fuse_main(argc, argv, &fsel_oper, NULL);
-
-	pthread_cancel(producer);
-	pthread_join(producer, NULL);
-
-	return ret;
-}
diff --git a/example/poll_client.c b/example/poll_client.c
deleted file mode 100644
index 222452a..0000000
--- a/example/poll_client.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-  FUSE fselclient: FUSE select example client
-  Copyright (C) 2008       SUSE Linux Products GmbH
-  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * This program tests the poll.c example file systsem.
- *
- * Compile with:
- *
- *      gcc -Wall poll_client.c -o poll_client
- *
- * ## Source code ##
- * \include poll_client.c
- */
-
-#include <config.h>
-
-#include <sys/select.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#define FSEL_FILES	16
-
-int main(void)
-{
-	static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
-	int fds[FSEL_FILES];
-	int i, nfds, tries;
-
-	for (i = 0; i < FSEL_FILES; i++) {
-		char name[] = { hex_map[i], '\0' };
-		fds[i] = open(name, O_RDONLY);
-		if (fds[i] < 0) {
-			perror("open");
-			return 1;
-		}
-	}
-	nfds = fds[FSEL_FILES - 1] + 1;
-
-	for(tries=0; tries < 16; tries++) {
-		static char buf[4096];
-		fd_set rfds;
-		int rc;
-
-		FD_ZERO(&rfds);
-		for (i = 0; i < FSEL_FILES; i++)
-			FD_SET(fds[i], &rfds);
-
-		rc = select(nfds, &rfds, NULL, NULL, NULL);
-
-		if (rc < 0) {
-			perror("select");
-			return 1;
-		}
-
-		for (i = 0; i < FSEL_FILES; i++) {
-			if (!FD_ISSET(fds[i], &rfds)) {
-				printf("_:   ");
-				continue;
-			}
-			printf("%X:", i);
-			rc = read(fds[i], buf, sizeof(buf));
-			if (rc < 0) {
-				perror("read");
-				return 1;
-			}
-			printf("%02d ", rc);
-		}
-		printf("\n");
-	}
-	return 0;
-}
diff --git a/example/printcap.c b/example/printcap.c
deleted file mode 100644
index 472a597..0000000
--- a/example/printcap.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2017 Nikolaus Rath <Nikolaus@rath.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-/** @file
- *
- * minimal example filesystem that prints out all capabilities
- * supported by the kernel and then exits.
- *
- * Compile with:
- *
- *     gcc -Wall printcap.c `pkg-config fuse3 --cflags --libs` -o printcap
- *
- * ## Source code ##
- * \include printcap.c
- */
-
-#define FUSE_USE_VERSION 31
-
-#include <config.h>
-
-#include <fuse_lowlevel.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-
-struct fuse_session *se;
-
-static void pc_init(void *userdata,
-		    struct fuse_conn_info *conn)
-{
-	(void) userdata;
-	
-	printf("Protocol version: %d.%d\n", conn->proto_major,
-	       conn->proto_minor);
-	printf("Capabilities:\n");
-	if(conn->capable & FUSE_CAP_WRITEBACK_CACHE)
-		printf("\tFUSE_CAP_WRITEBACK_CACHE\n");
-	if(conn->capable & FUSE_CAP_ASYNC_READ)
-			printf("\tFUSE_CAP_ASYNC_READ\n");
-	if(conn->capable & FUSE_CAP_POSIX_LOCKS)
-			printf("\tFUSE_CAP_POSIX_LOCKS\n");
-	if(conn->capable & FUSE_CAP_ATOMIC_O_TRUNC)
-			printf("\tFUSE_CAP_ATOMIC_O_TRUNC\n");
-	if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
-			printf("\tFUSE_CAP_EXPORT_SUPPORT\n");
-	if(conn->capable & FUSE_CAP_DONT_MASK)
-			printf("\tFUSE_CAP_DONT_MASK\n");
-	if(conn->capable & FUSE_CAP_SPLICE_MOVE)
-			printf("\tFUSE_CAP_SPLICE_MOVE\n");
-	if(conn->capable & FUSE_CAP_SPLICE_READ)
-			printf("\tFUSE_CAP_SPLICE_READ\n");
-	if(conn->capable & FUSE_CAP_SPLICE_WRITE)
-			printf("\tFUSE_CAP_SPLICE_WRITE\n");
-	if(conn->capable & FUSE_CAP_FLOCK_LOCKS)
-			printf("\tFUSE_CAP_FLOCK_LOCKS\n");
-	if(conn->capable & FUSE_CAP_IOCTL_DIR)
-			printf("\tFUSE_CAP_IOCTL_DIR\n");
-	if(conn->capable & FUSE_CAP_AUTO_INVAL_DATA)
-			printf("\tFUSE_CAP_AUTO_INVAL_DATA\n");
-	if(conn->capable & FUSE_CAP_READDIRPLUS)
-			printf("\tFUSE_CAP_READDIRPLUS\n");
-	if(conn->capable & FUSE_CAP_READDIRPLUS_AUTO)
-			printf("\tFUSE_CAP_READDIRPLUS_AUTO\n");
-	if(conn->capable & FUSE_CAP_ASYNC_DIO)
-			printf("\tFUSE_CAP_ASYNC_DIO\n");
-	if(conn->capable & FUSE_CAP_WRITEBACK_CACHE)
-			printf("\tFUSE_CAP_WRITEBACK_CACHE\n");
-	if(conn->capable & FUSE_CAP_NO_OPEN_SUPPORT)
-			printf("\tFUSE_CAP_NO_OPEN_SUPPORT\n");
-	if(conn->capable & FUSE_CAP_PARALLEL_DIROPS)
-			printf("\tFUSE_CAP_PARALLEL_DIROPS\n");
-	if(conn->capable & FUSE_CAP_POSIX_ACL)
-			printf("\tFUSE_CAP_POSIX_ACL\n");
-	if(conn->capable & FUSE_CAP_CACHE_SYMLINKS)
-			printf("\tFUSE_CAP_CACHE_SYMLINKS\n");
-	if(conn->capable & FUSE_CAP_NO_OPENDIR_SUPPORT)
-			printf("\tFUSE_CAP_NO_OPENDIR_SUPPORT\n");
-	if(conn->capable & FUSE_CAP_EXPLICIT_INVAL_DATA)
-			printf("\tFUSE_CAP_EXPLICIT_INVAL_DATA\n");
-	fuse_session_exit(se);
-}
-
-
-static const struct fuse_lowlevel_ops pc_oper = {
-	.init		= pc_init,
-};
-
-int main(int argc, char **argv)
-{
-	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-	char *mountpoint;
-	int ret = -1;
-
-	mountpoint = strdup("/tmp/fuse_printcap_XXXXXX");
-	if(mkdtemp(mountpoint) == NULL) {
-		perror("mkdtemp");
-		return 1;
-	}
-	
-	printf("FUSE library version %s\n", fuse_pkgversion());
-	fuse_lowlevel_version();
-
-	se = fuse_session_new(&args, &pc_oper,
-			      sizeof(pc_oper), NULL);
-	if (se == NULL)
-	    goto err_out1;
-
-	if (fuse_set_signal_handlers(se) != 0)
-	    goto err_out2;
-
-	if (fuse_session_mount(se, mountpoint) != 0)
-	    goto err_out3;
-
-	ret = fuse_session_loop(se);
-
-	fuse_session_unmount(se);
-err_out3:
-	fuse_remove_signal_handlers(se);
-err_out2:
-	fuse_session_destroy(se);
-err_out1:
-	rmdir(mountpoint);
-	free(mountpoint);
-	fuse_opt_free_args(&args);
-
-	return ret ? 1 : 0;
-}
diff --git a/include/fuse_common.h b/include/fuse_common.h
index ea4bdb0..3f836d7 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -92,6 +92,11 @@
 	 * same file handle. */
 	uint64_t fh;
 
+	/** Passthrough file handle id.  May be filled in by filesystem in
+	 * create and open.  It is used to create a passthrough connection
+	 * between FUSE file and lower file system file. */
+	uint32_t passthrough_fh;
+
 	/** Lock owner id.  Available in locking operations and flush */
 	uint64_t lock_owner;
 
@@ -395,6 +400,18 @@
 #define FUSE_CAP_EXPLICIT_INVAL_DATA    (1 << 25)
 
 /**
+ * Indicates support for passthrough mode access for read/write operations.
+ *
+ * If this flag is set in the `capable` field of the `fuse_conn_info`
+ * structure, then the FUSE kernel module supports redirecting read/write
+ * operations to the lower file system instead of letting them to be handled
+ * by the FUSE daemon.
+ *
+ * This feature is disabled by default.
+ */
+#define FUSE_CAP_PASSTHROUGH            (1 << 31)
+
+/**
  * Ioctl flags
  *
  * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 018a00a..70278fa 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -301,6 +301,7 @@
 #define FUSE_CACHE_SYMLINKS	(1 << 23)
 #define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
 #define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
+#define FUSE_PASSTHROUGH	(1 << 31)
 
 /**
  * CUSE INIT request/reply flags
@@ -423,6 +424,9 @@
 	FUSE_LSEEK		= 46,
 	FUSE_COPY_FILE_RANGE	= 47,
 
+	/* Android specific operations */
+	FUSE_CANONICAL_PATH     = 2016,
+
 	/* CUSE specific operations */
 	CUSE_INIT		= 4096
 };
@@ -453,6 +457,17 @@
 	struct fuse_attr attr;
 };
 
+#define FUSE_ACTION_KEEP        0
+#define FUSE_ACTION_REMOVE      1
+#define FUSE_ACTION_REPLACE     2
+
+struct fuse_entry_bpf_out {
+        uint64_t        backing_action;
+        uint64_t        backing_fd;
+        uint64_t        bpf_action;
+        uint64_t        bpf_fd;
+};
+
 struct fuse_forget_in {
 	uint64_t	nlookup;
 };
@@ -544,7 +559,7 @@
 struct fuse_open_out {
 	uint64_t	fh;
 	uint32_t	open_flags;
-	uint32_t	padding;
+	uint32_t	passthrough_fh;
 };
 
 struct fuse_release_in {
@@ -571,6 +586,13 @@
 	uint32_t	padding;
 };
 
+struct fuse_passthrough_out_v0 {
+	uint32_t	fd;
+	/* For future implementation */
+	uint32_t	len;
+	void *		vec;
+};
+
 #define FUSE_COMPAT_WRITE_IN_SIZE 24
 
 struct fuse_write_in {
@@ -822,7 +844,10 @@
 };
 
 /* Device ioctls: */
-#define FUSE_DEV_IOC_CLONE	_IOR(229, 0, uint32_t)
+#define FUSE_DEV_IOC_CLONE	      _IOR(229, 0, uint32_t)
+#define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V0 _IOW(229, 1, struct fuse_passthrough_out_v0)
+#define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V1 _IOW(229, 127, struct fuse_passthrough_out_v0)
+#define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V2 _IOW(229, 126, uint32_t)
 
 struct fuse_lseek_in {
 	uint64_t	fh;
@@ -845,4 +870,51 @@
 	uint64_t	flags;
 };
 
+/** Export fuse_args only for bpf */
+#ifdef __KERNEL__
+struct fuse_mount;
+
+/** One input argument of a request */
+struct fuse_in_arg {
+  unsigned size;
+  const void *value;
+};
+
+/** One output argument of a request */
+struct fuse_arg {
+  unsigned size;
+  void *value;
+};
+
+struct fuse_args {
+  uint64_t nodeid;
+  uint32_t opcode;
+  unsigned short in_numargs;
+  unsigned short out_numargs;
+  int force:1;
+  int noreply:1;
+  int nocreds:1;
+  int in_pages:1;
+  int out_pages:1;
+  int out_argvar:1;
+  int page_zeroing:1;
+  int page_replace:1;
+  int may_block:1;
+  struct fuse_in_arg in_args[5];
+  struct fuse_arg out_args[3];
+  void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
+
+  /* Path used for completing d_canonical_path */
+  struct path *canonical_path;
+};
+#endif
+
+#define FUSE_BPF_USER_FILTER    1
+#define FUSE_BPF_BACKING        2
+#define FUSE_BPF_POST_FILTER    4
+
+#define FUSE_OPCODE_FILTER      0x0ffff
+#define FUSE_PREFILTER          0x10000
+#define FUSE_POSTFILTER         0x20000
+
 #endif /* _LINUX_FUSE_H */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index d73e9fa..c591f71 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -98,6 +98,10 @@
 	    that come through the kernel, this should be set to a very
 	    large value. */
 	double entry_timeout;
+        uint64_t        backing_action;
+        uint64_t        backing_fd;
+        uint64_t        bpf_action;
+        uint64_t        bpf_fd;
 };
 
 /**
@@ -317,6 +321,18 @@
 	 */
 	void (*readlink) (fuse_req_t req, fuse_ino_t ino);
 
+        /**
+	 * Return canonical path for inotify
+	 *
+	 * Valid replies:
+	 *   fuse_reply_canonical_path
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 */
+	void (*canonical_path) (fuse_req_t req, fuse_ino_t ino);
+
 	/**
 	 * Create file node
 	 *
@@ -1343,6 +1359,20 @@
  */
 int fuse_reply_readlink(fuse_req_t req, const char *link);
 
+int fuse_passthrough_enable(fuse_req_t req, unsigned int fd);
+
+/**
+ * Reply with the canonical path for inotify
+ *
+ * Possible requests:
+ *   canonical_path
+ *
+ * @param req request handle
+ * @param path to canonicalize
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_canonical_path(fuse_req_t req, const char *path);
+
 /**
  * Reply with open parameters
  *
diff --git a/lib/android_config.h b/lib/android_config.h
new file mode 100644
index 0000000..b05b316
--- /dev/null
+++ b/lib/android_config.h
@@ -0,0 +1,2 @@
+#define pthread_setcancelstate(x, y)
+#define pthread_cancel(c)
diff --git a/lib/config.h b/lib/config.h
new file mode 100644
index 0000000..2e1624d
--- /dev/null
+++ b/lib/config.h
@@ -0,0 +1,42 @@
+/*
+ * Autogenerated by the Meson build system.
+ * Do not edit, your changes will be lost.
+ */
+
+#pragma once
+
+#define HAVE_COPY_FILE_RANGE
+
+#define HAVE_FALLOCATE
+
+#define HAVE_FDATASYNC
+
+#define HAVE_FORK
+
+#define HAVE_FSTATAT
+
+#define HAVE_ICONV
+
+#define HAVE_OPENAT
+
+#define HAVE_PIPE2
+
+#define HAVE_POSIX_FALLOCATE
+
+#define HAVE_READLINKAT
+
+#define HAVE_SETXATTR
+
+#define HAVE_SPLICE
+
+#define HAVE_STRUCT_STAT_ST_ATIM
+
+#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
+
+#define HAVE_UTIMENSAT
+
+#define HAVE_VMSPLICE
+
+#define PACKAGE_VERSION "3.6.1"
+
+#include "android_config.h"
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index d227688..aee22b4 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -27,6 +27,7 @@
 #include <errno.h>
 #include <assert.h>
 #include <sys/file.h>
+#include <sys/ioctl.h>
 
 #ifndef F_LINUX_SPECIFIC_BASE
 #define F_LINUX_SPECIFIC_BASE       1024
@@ -387,6 +388,7 @@
 		      const struct fuse_file_info *f)
 {
 	arg->fh = f->fh;
+	arg->passthrough_fh = f->passthrough_fh;
 	if (f->direct_io)
 		arg->open_flags |= FOPEN_DIRECT_IO;
 	if (f->keep_cache)
@@ -397,20 +399,45 @@
 		arg->open_flags |= FOPEN_NONSEEKABLE;
 }
 
-int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
-{
-	struct fuse_entry_out arg;
-	size_t size = req->se->conn.proto_minor < 9 ?
-		FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param* e) {
+    struct {
+        struct fuse_entry_out arg;
+        struct fuse_entry_bpf_out bpf_arg;
+    } __attribute__((packed)) arg_ext = {0};
 
-	/* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
-	   negative entry */
-	if (!e->ino && req->se->conn.proto_minor < 4)
-		return fuse_reply_err(req, ENOENT);
+    struct fuse_entry_out arg;
+    struct fuse_entry_bpf_out bpf_arg;
+    size_t size;
+    int extended_args = e->bpf_action || bpf_arg.bpf_fd || e->backing_action || e->backing_fd;
 
-	memset(&arg, 0, sizeof(arg));
-	fill_entry(&arg, e);
-	return send_reply_ok(req, &arg, size);
+    if (extended_args) {
+        size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg_ext);
+    } else {
+        size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
+    }
+
+    /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
+       negative entry */
+    if (!e->ino && req->se->conn.proto_minor < 4) return fuse_reply_err(req, ENOENT);
+
+    memset(&arg, 0, sizeof(arg));
+
+    if (extended_args) {
+        memset(&bpf_arg, 0, sizeof(bpf_arg));
+
+        bpf_arg.bpf_action = e->bpf_action;
+        bpf_arg.bpf_fd = e->bpf_fd;
+        bpf_arg.backing_action = e->backing_action;
+        bpf_arg.backing_fd = e->backing_fd;
+
+        arg_ext.arg = arg;
+        arg_ext.bpf_arg = bpf_arg;
+
+        return send_reply_ok(req, &arg_ext, size);
+    } else {
+        fill_entry(&arg, e);
+        return send_reply_ok(req, &arg, size);
+    }
 }
 
 int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
@@ -449,6 +476,84 @@
 	return send_reply_ok(req, linkname, strlen(linkname));
 }
 
+int fuse_reply_canonical_path(fuse_req_t req, const char *path)
+{
+        // The kernel expects a buffer containing the null terminator for this op
+        // So we add the null terminator size to strlen
+	return send_reply_ok(req, path, strlen(path) + 1);
+}
+
+enum {
+	FUSE_PASSTHROUGH_API_UNAVAILABLE,
+	FUSE_PASSTHROUGH_API_V0,
+	FUSE_PASSTHROUGH_API_V1,
+	FUSE_PASSTHROUGH_API_V2,
+	FUSE_PASSTHROUGH_API_STABLE,
+};
+
+/*
+ * Requests the FUSE passthrough feature to be enabled on a specific file
+ * through the passed fd.
+ * This function returns an identifier that must be used as passthrough_fh
+ * when the open/create_open request reply is sent back to /dev/fuse.
+ * As for the current FUSE passthrough implementation, passthrough_fh values
+ * are only valid if > 0, so in case the FUSE passthrough open ioctl returns
+ * a value <= 0, this must be considered an error and is returned as-is by
+ * this function.
+ */
+int fuse_passthrough_enable(fuse_req_t req, unsigned int fd) {
+	static sig_atomic_t passthrough_version = FUSE_PASSTHROUGH_API_STABLE;
+	int ret = 0; /* values <= 0 represent errors in FUSE passthrough */
+
+	/*
+	 * The interface of FUSE passthrough is still unstable in the kernel,
+	 * so the following solution is to search for the most updated API
+	 * version and, if not found, fall back to an older one.
+	 * This happens when ioctl() returns -1 and errno is set to ENOTTY,
+	 * an error code that corresponds to the lack of a specific ioctl.
+	 */
+	switch (passthrough_version) {
+	case FUSE_PASSTHROUGH_API_STABLE:
+		/* There is not a stable API yet */
+		passthrough_version = FUSE_PASSTHROUGH_API_V2;
+	case FUSE_PASSTHROUGH_API_V2: {
+		ret = ioctl(req->se->fd, FUSE_DEV_IOC_PASSTHROUGH_OPEN_V2, &fd);
+		if (ret == -1 && errno == ENOTTY)
+			passthrough_version = FUSE_PASSTHROUGH_API_V1;
+		else
+			break;
+	}
+	case FUSE_PASSTHROUGH_API_V1: {
+		struct fuse_passthrough_out_v0 out = {};
+		out.fd = fd;
+
+		ret = ioctl(req->se->fd, FUSE_DEV_IOC_PASSTHROUGH_OPEN_V1, &out);
+		if (ret == -1 && errno == ENOTTY)
+			passthrough_version = FUSE_PASSTHROUGH_API_V0;
+		else
+			break;
+	}
+	case FUSE_PASSTHROUGH_API_V0: {
+		struct fuse_passthrough_out_v0 out = {};
+		out.fd = fd;
+
+		ret = ioctl(req->se->fd, FUSE_DEV_IOC_PASSTHROUGH_OPEN_V0, &out);
+		if (ret == -1 && errno == ENOTTY)
+			passthrough_version = FUSE_PASSTHROUGH_API_UNAVAILABLE;
+		else
+			break;
+	}
+	default:
+		fuse_log(FUSE_LOG_ERR, "fuse: passthrough_enable no valid API\n");
+		return -ENOTTY;
+	}
+
+	if (ret <= 0)
+		fuse_log(FUSE_LOG_ERR, "fuse: passthrough_enable: %s\n", strerror(errno));
+
+	return ret;
+}
+
 int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
 {
 	struct fuse_open_out arg;
@@ -1199,6 +1304,16 @@
 		fuse_reply_err(req, ENOSYS);
 }
 
+static void do_canonical_path(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	(void) inarg;
+
+	if (req->se->op.canonical_path)
+		req->se->op.canonical_path(req, nodeid);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
 static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
 	struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
@@ -1977,6 +2092,8 @@
 				bufsize = max_bufsize;
 			}
 		}
+		if (arg->flags & FUSE_PASSTHROUGH)
+			se->conn.capable |= FUSE_PASSTHROUGH;
 	} else {
 		se->conn.max_readahead = 0;
 	}
@@ -2089,6 +2206,8 @@
 		outarg.flags |= FUSE_WRITEBACK_CACHE;
 	if (se->conn.want & FUSE_CAP_POSIX_ACL)
 		outarg.flags |= FUSE_POSIX_ACL;
+	if (se->conn.want & FUSE_CAP_PASSTHROUGH)
+		outarg.flags |= FUSE_PASSTHROUGH;
 	if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS)
 		outarg.flags |= FUSE_CACHE_SYMLINKS;
 	if (se->conn.want & FUSE_CAP_EXPLICIT_INVAL_DATA)
@@ -2464,6 +2583,7 @@
 	[FUSE_GETATTR]	   = { do_getattr,     "GETATTR"     },
 	[FUSE_SETATTR]	   = { do_setattr,     "SETATTR"     },
 	[FUSE_READLINK]	   = { do_readlink,    "READLINK"    },
+        [FUSE_CANONICAL_PATH] = { do_canonical_path, "CANONICAL_PATH" },
 	[FUSE_SYMLINK]	   = { do_symlink,     "SYMLINK"     },
 	[FUSE_MKNOD]	   = { do_mknod,       "MKNOD"	     },
 	[FUSE_MKDIR]	   = { do_mkdir,       "MKDIR"	     },
@@ -2517,6 +2637,22 @@
 		return fuse_ll_ops[opcode].name;
 }
 
+static const char *opfiltername(int filter)
+{
+	switch (filter) {
+	case 0:
+		return "NONE";
+	case FUSE_PREFILTER:
+		return "FUSE_PREFILTER";
+	case FUSE_POSTFILTER:
+		return "FUSE_POSTFILTER";
+	case FUSE_PREFILTER | FUSE_POSTFILTER:
+		return "FUSE_PREFILTER | FUSE_POSTFILTER";
+	default:
+		return "???";
+	}
+}
+
 static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
 				  struct fuse_bufvec *src)
 {
@@ -2551,6 +2687,7 @@
 	void *mbuf = NULL;
 	int err;
 	int res;
+	int opcode_filter;
 
 	if (buf->flags & FUSE_BUF_IS_FD) {
 		if (buf->size < tmpbuf.buf[0].size)
@@ -2572,11 +2709,16 @@
 		in = buf->mem;
 	}
 
+	/* Cleanup opcode most significant bits used by FUSE BPF */
+	opcode_filter = in->opcode & ~FUSE_OPCODE_FILTER;
+	in->opcode &= FUSE_OPCODE_FILTER;
+
 	if (se->debug) {
 		fuse_log(FUSE_LOG_DEBUG,
-			"unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
+			"unique: %llu, opcode: %s (%i), opcode filter: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
 			(unsigned long long) in->unique,
 			opname((enum fuse_opcode) in->opcode), in->opcode,
+			opfiltername((enum fuse_opcode) opcode_filter), opcode_filter,
 			(unsigned long long) in->nodeid, buf->size, in->pid);
 	}
 
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index a06f768..35964f3 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -82,7 +82,6 @@
 		fuse_fs_unlink;
 		fuse_fs_utimens;
 		fuse_fs_write;
-		fuse_register_module;
 		fuse_reply_iov;
 		fuse_version;
 		fuse_pkgversion;
@@ -159,14 +158,15 @@
 FUSE_3.4 {
 	global:
 		fuse_fs_copy_file_range;
-		fuse_reply_copy_file_range;
 } FUSE_3.3;
 
 FUSE_3.7 {
 	global:
 		fuse_set_log_func;
 		fuse_log;
-} FUSE_3.4;
+		fuse_passthrough_enable;
+		fuse_reply_canonical_path;
+} FUSE_3.3;
 
 # Local Variables:
 # indent-tabs-mode: t
diff --git a/make_release_tarball.sh b/make_release_tarball.sh
deleted file mode 100755
index a2e777b..0000000
--- a/make_release_tarball.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/sh
-#
-# Create tarball from Git tag, removing and adding
-# some files.
-#
-
-set -e
-
-if [ -z "$1" ]; then
-    TAG="$(git tag --list 'fuse-3*' --sort=-taggerdate | head -1)"
-else
-    TAG="$1"
-fi
-
-echo "Creating release tarball for ${TAG}..."
-
-git checkout -q "${TAG}"
-doxygen doc/Doxyfile
-
-mkdir "${TAG}"
-
-git archive --format=tar "${TAG}" | tar -x "--directory=${TAG}"
-find "${TAG}" -name .gitignore -delete
-rm "${TAG}/make_release_tarball.sh" \
-   "${TAG}/.travis.yml" \
-   "${TAG}/.cirrus.yml"
-cp -a doc/html "${TAG}/doc/"
-tar -cJf "${TAG}.tar.xz" "${TAG}/"
-gpg --armor --detach-sign "${TAG}.tar.xz"
-
-PREV_TAG="$(git tag --list 'fuse-3*' --sort=-taggerdate --merged "${TAG}^"| head -1)"
-echo "Contributors from ${PREV_TAG} to ${TAG}:"
-git log --pretty="format:%an <%aE>" "${PREV_TAG}..${TAG}" | sort -u
-
diff --git a/meson_options.txt b/meson_options.txt
deleted file mode 100644
index 43304d1..0000000
--- a/meson_options.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-option('disable-mtab', type : 'boolean', value : false,
-       description: 'Disable and ignore usage of /etc/mtab')
-
-option('udevrulesdir', type : 'string', value : '',
-       description: 'Where to install udev rules (if empty, query pkg-config(1))')
-
-option('utils', type : 'boolean', value : true,
-       description: 'Whether or not to build and install helper programs')
-
-option('examples', type : 'boolean', value : true,
-       description: 'Whether or not to build example programs')
-
-option('useroot', type : 'boolean', value : true,
-       description: 'Set owner and setuid bits on installed files')
-
-option('tests', type : 'boolean', value : true,
-       description: 'Compile the test files')
-
diff --git a/test/.gitignore b/test/.gitignore
deleted file mode 100644
index e51938a..0000000
--- a/test/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-test
-test_write_cache
-__pycache__/
diff --git a/test/conftest.py b/test/conftest.py
deleted file mode 100644
index 6e46d5d..0000000
--- a/test/conftest.py
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import pytest
-import time
-import re
-import os
-import threading
-
-
-# If a test fails, wait a moment before retrieving the captured
-# stdout/stderr. When using a server process, this makes sure that we capture
-# any potential output of the server that comes *after* a test has failed. For
-# example, if a request handler raises an exception, the server first signals an
-# error to FUSE (causing the test to fail), and then logs the exception. Without
-# the extra delay, the exception will go into nowhere.
-@pytest.mark.hookwrapper
-def pytest_pyfunc_call(pyfuncitem):
-    outcome = yield
-    failed = outcome.excinfo is not None
-    if failed:
-        time.sleep(1)
-
-
-class OutputChecker:
-    '''Check output data for suspicious patterns.
-
-    Everything written to check_output.fd will be scanned for suspicious
-    messages and then written to sys.stdout.
-    '''
-
-    def __init__(self):
-        (fd_r, fd_w) = os.pipe()
-        self.fd = fd_w
-        self._false_positives = []
-        self._buf = bytearray()
-        self._thread = threading.Thread(target=self._loop, daemon=True, args=(fd_r,))
-        self._thread.start()
-
-    def register_output(self, pattern, count=1, flags=re.MULTILINE):
-        '''Register *pattern* as false positive for output checking
-
-        This prevents the test from failing because the output otherwise
-        appears suspicious.
-        '''
-
-        self._false_positives.append((pattern, flags, count))
-
-    def _loop(self, ifd):
-        BUFSIZE = 128*1024
-        ofd = sys.stdout.fileno()
-        while True:
-            buf = os.read(ifd, BUFSIZE)
-            if not buf:
-                break
-            os.write(ofd, buf)
-            self._buf += buf
-
-    def _check(self):
-        os.close(self.fd)
-        self._thread.join()
-
-        buf = self._buf.decode('utf8', errors='replace')
-
-        # Strip out false positives
-        for (pattern, flags, count) in self._false_positives:
-            cp = re.compile(pattern, flags)
-            (buf, cnt) = cp.subn('', buf, count=count)
-
-        patterns = [ r'\b{}\b'.format(x) for x in
-                     ('exception', 'error', 'warning', 'fatal', 'traceback',
-                        'fault', 'crash(?:ed)?', 'abort(?:ed)',
-                        'uninitiali[zs]ed') ]
-        patterns += ['^==[0-9]+== ']
-
-        for pattern in patterns:
-            cp = re.compile(pattern, re.IGNORECASE | re.MULTILINE)
-            hit = cp.search(buf)
-            if hit:
-                raise AssertionError('Suspicious output to stderr (matched "%s")'
-                                     % hit.group(0))
-
-@pytest.fixture()
-def output_checker(request):
-    checker = OutputChecker()
-    yield checker
-    checker._check()
-
-
-# Make test outcome available to fixtures
-# (from https://github.com/pytest-dev/pytest/issues/230)
-@pytest.hookimpl(hookwrapper=True, tryfirst=True)
-def pytest_runtest_makereport(item, call):
-    outcome = yield
-    rep = outcome.get_result()
-    setattr(item, "rep_" + rep.when, rep)
-    return rep
diff --git a/test/lsan_suppress.txt b/test/lsan_suppress.txt
deleted file mode 100644
index 4352b3a..0000000
--- a/test/lsan_suppress.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Suppression file for address sanitizer.
-
-# There are some leaks in command line option parsing. They should be
-# fixed at some point, but are harmless since the consume just a small,
-# constant amount of memory and do not grow.
-leak:fuse_opt_parse
-
-
-# Leaks in fusermount3 are harmless as well (it's a short-lived
-# process) - but patches are welcome!
-leak:fusermount.c
diff --git a/test/meson.build b/test/meson.build
deleted file mode 100644
index 12d3c41..0000000
--- a/test/meson.build
+++ /dev/null
@@ -1,33 +0,0 @@
-# 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)
-td += executable('readdir_inode', 'readdir_inode.c',
-                 include_directories: include_dirs,
-                 install: false)
-
-test_scripts = [ 'conftest.py', 'pytest.ini', 'test_examples.py',
-                 'util.py', 'test_ctests.py' ]
-td += custom_target('test_scripts', input: test_scripts,
-                      output: test_scripts, build_by_default: true,
-                      command: ['cp', '-fPp',
-                                '@INPUT@', meson.current_build_dir() ])
-
-# Provide something helpful when running 'ninja test'
-
-if meson.is_subproject()
-	test('libfuse is a subproject, skipping tests', executable('wrong_command',
-                      'wrong_command.c', install: false,
-                       c_args: [ '-DMESON_IS_SUBPROJECT' ]))
-else
-	test('wrong_command', executable('wrong_command', 'wrong_command.c',
-                      install: false))
-endif
diff --git a/test/pytest.ini b/test/pytest.ini
deleted file mode 100644
index 036a9f4..0000000
--- a/test/pytest.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[pytest]
-addopts = --verbose --assert=rewrite --tb=native -x -r a
-markers = 
-    uses_fuse: Indicates that FUSE is supported.
diff --git a/test/readdir_inode.c b/test/readdir_inode.c
deleted file mode 100644
index 99f95ff..0000000
--- a/test/readdir_inode.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Prints each directory entry, its inode and d_type as returned by 'readdir'.
- * Skips '.' and '..' because readdir is not required to return them and
- * some of our examples don't. However if they are returned, their d_type
- * should be valid.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <errno.h>
-
-int main(int argc, char* argv[])
-{
-    DIR* dirp;
-    struct dirent* dent;
-
-    if (argc != 2) {
-        fprintf(stderr, "Usage: readdir_inode dir\n");
-        return 1;
-    }
-
-    dirp = opendir(argv[1]);
-    if (dirp == NULL) {
-        perror("failed to open directory");
-        return 2;
-    }
-
-    errno = 0;
-    dent = readdir(dirp);
-    while (dent != NULL) {
-        if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
-            printf("%llu %d %s\n", (unsigned long long)dent->d_ino,
-			(int)dent->d_type, dent->d_name);
-            if ((long long)dent->d_ino < 0)
-               fprintf(stderr,"%s : bad d_ino %llu\n",
-                        dent->d_name, (unsigned long long)dent->d_ino);
-            if ((dent->d_type < 1) || (dent->d_type > 15))
-               fprintf(stderr,"%s : bad d_type %d\n",
-                        dent->d_name, (int)dent->d_type);
-        } else {
-            if (dent->d_type != DT_DIR)
-               fprintf(stderr,"%s : bad d_type %d\n",
-                        dent->d_name, (int)dent->d_type);
-        }
-        dent = readdir(dirp);
-    }
-    if (errno != 0) {
-        perror("failed to read directory entry");
-        return 3;
-    }
-
-    closedir(dirp);
-
-    return 0;
-}
diff --git a/test/stracedecode.c b/test/stracedecode.c
deleted file mode 100644
index 940438a..0000000
--- a/test/stracedecode.c
+++ /dev/null
@@ -1,198 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include "fuse_kernel.h"
-
-static struct {
-	const char *name;
-} fuse_ll_ops[] = {
-	[FUSE_LOOKUP]	   = { "LOOKUP"	     },
-	[FUSE_FORGET]	   = { "FORGET"	     },
-	[FUSE_GETATTR]	   = { "GETATTR"     },
-	[FUSE_SETATTR]	   = { "SETATTR"     },
-	[FUSE_READLINK]	   = { "READLINK"    },
-	[FUSE_SYMLINK]	   = { "SYMLINK"     },
-	[FUSE_MKNOD]	   = { "MKNOD"	     },
-	[FUSE_MKDIR]	   = { "MKDIR"	     },
-	[FUSE_UNLINK]	   = { "UNLINK"	     },
-	[FUSE_RMDIR]	   = { "RMDIR"	     },
-	[FUSE_RENAME]	   = { "RENAME"	     },
-	[FUSE_LINK]	   = { "LINK"	     },
-	[FUSE_OPEN]	   = { "OPEN"	     },
-	[FUSE_READ]	   = { "READ"	     },
-	[FUSE_WRITE]	   = { "WRITE"	     },
-	[FUSE_STATFS]	   = { "STATFS"	     },
-	[FUSE_RELEASE]	   = { "RELEASE"     },
-	[FUSE_FSYNC]	   = { "FSYNC"	     },
-	[FUSE_SETXATTR]	   = { "SETXATTR"    },
-	[FUSE_GETXATTR]	   = { "GETXATTR"    },
-	[FUSE_LISTXATTR]   = { "LISTXATTR"   },
-	[FUSE_REMOVEXATTR] = { "REMOVEXATTR" },
-	[FUSE_FLUSH]	   = { "FLUSH"	     },
-	[FUSE_INIT]	   = { "INIT"	     },
-	[FUSE_OPENDIR]	   = { "OPENDIR"     },
-	[FUSE_READDIR]	   = { "READDIR"     },
-	[FUSE_RELEASEDIR]  = { "RELEASEDIR"  },
-	[FUSE_FSYNCDIR]	   = { "FSYNCDIR"    },
-	[FUSE_GETLK]	   = { "GETLK"	     },
-	[FUSE_SETLK]	   = { "SETLK"	     },
-	[FUSE_SETLKW]	   = { "SETLKW"	     },
-	[FUSE_ACCESS]	   = { "ACCESS"	     },
-	[FUSE_CREATE]	   = { "CREATE"	     },
-	[FUSE_INTERRUPT]   = { "INTERRUPT"   },
-	[FUSE_BMAP]	   = { "BMAP"	     },
-	[FUSE_DESTROY]	   = { "DESTROY"     },
-	[FUSE_READDIRPLUS] = { "READDIRPLUS" },
-};
-
-#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
-
-static const char *opname(enum fuse_opcode opcode)
-{
-	if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
-		return "???";
-	else
-		return fuse_ll_ops[opcode].name;
-}
-
-
-static void process_buf(int dir, char *buf, int len)
-{
-	static unsigned long long prevuniq = -1;
-	static int prevopcode;
-
-	if (!dir) {
-		struct fuse_in_header *in = (struct fuse_in_header *) buf;
-		buf += sizeof(struct fuse_in_header);
-
-		printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n",
-		       (unsigned long long) in->unique,
-		       opname((enum fuse_opcode) in->opcode), in->opcode,
-		       (unsigned long) in->nodeid, in->len, len);
-
-		switch (in->opcode) {
-		case FUSE_READ: {
-			struct fuse_read_in *arg = (struct fuse_read_in *) buf;
-			printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n",
-			       arg->fh, arg->offset, arg->size, arg->read_flags,
-			       arg->lock_owner, arg->flags);
-			break;
-		}
-		case FUSE_WRITE: {
-			struct fuse_write_in *arg = (struct fuse_write_in *) buf;
-			printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n",
-			       arg->fh, arg->offset, arg->size, arg->write_flags,
-			       arg->lock_owner, arg->flags);
-			break;
-		}
-		}
-		prevuniq = in->unique;
-		prevopcode = in->opcode;
-	} else {
-		struct fuse_out_header *out = (struct fuse_out_header *) buf;
-		buf += sizeof(struct fuse_out_header);
-
-		printf("   unique: %llu, error: %i (%s), len: %i, outsize: %i\n",
-		       (unsigned long long) out->unique, out->error,
-		       strerror(-out->error), out->len, len);
-
-		if (out->unique == prevuniq) {
-			switch (prevopcode) {
-			case FUSE_GETATTR: {
-				struct fuse_attr_out *arg = (struct fuse_attr_out *) buf;
-				printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n",
-				       arg->attr_valid, arg->attr_valid_nsec,
-				       arg->attr.ino, arg->attr.size, arg->attr.blocks);
-				break;
-			}
-			case FUSE_LOOKUP: {
-				struct fuse_entry_out *arg = (struct fuse_entry_out *) buf;
-				printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n",
-				       arg->nodeid, arg->attr_valid, arg->attr_valid_nsec,
-				       arg->attr.ino, arg->attr.size, arg->attr.blocks);
-				break;
-			}
-			}
-		}
-	}
-
-}
-
-int main(void)
-{
-	FILE *in = stdin;
-	while (1) {
-		int dir;
-		int res;
-		char buf[1048576];
-		unsigned len = 0;
-
-		memset(buf, 0, sizeof(buf));
-		while (1) {
-			char str[32];
-
-			res = fscanf(in, "%30s", str);
-			if (res != 1 && feof(in))
-				return 0;
-
-			if (res == 0)
-				continue;
-
-			if (strncmp(str, "read(", 5) == 0) {
-				dir = 0;
-				break;
-			} else if (strncmp(str, "writev(", 7) == 0) {
-				dir = 1;
-				break;
-			}
-		}
-
-		while (1) {
-			int c = getc(in);
-			if (c == '"') {
-				while (1) {
-					int val;
-
-					c = getc(in);
-					if (c == EOF) {
-						fprintf(stderr, "eof in string\n");
-						break;
-					}
-					if (c == '\n') {
-						fprintf(stderr, "eol in string\n");
-						break;
-					}
-					if (c == '"')
-						break;
-					if (c != '\\') {
-						val = c;
-					} else {
-						c = getc(in);
-						switch (c) {
-						case 'n': val = '\n'; break;
-						case 'r': val = '\r'; break;
-						case 't': val = '\t'; break;
-						case '"': val = '"'; break;
-						case '\\': val = '\\'; break;
-						case 'x':
-							res = scanf("%x", &val);
-							if (res != 1) {
-								fprintf(stderr, "parse error\n");
-								continue;
-							}
-							break;
-						default:
-							fprintf(stderr, "unknown sequence: '\\%c'\n", c);
-							continue;
-						}
-					}
-					buf[len++] = val;
-				}
-			}
-			if (c == '\n')
-				break;
-		}
-		process_buf(dir, buf, len);
-		memset(buf, 0, len);
-		len = 0;
-	}
-}
diff --git a/test/test_ctests.py b/test/test_ctests.py
deleted file mode 100644
index 4290f20..0000000
--- a/test/test_ctests.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env python3
-
-if __name__ == '__main__':
-    import pytest
-    import sys
-    sys.exit(pytest.main([__file__] + sys.argv[1:]))
-
-import subprocess
-import pytest
-import platform
-import sys
-from distutils.version import LooseVersion
-from util import (wait_for_mount, umount, cleanup, base_cmdline,
-                  safe_sleep, basename, fuse_test_marker, fuse_caps,
-                  fuse_proto)
-from os.path import join as pjoin
-import os.path
-
-pytestmark = fuse_test_marker()
-
-@pytest.mark.skipif('FUSE_CAP_WRITEBACK_CACHE' not in fuse_caps,
-                    reason='not supported by running kernel')
-@pytest.mark.parametrize("writeback", (False, True))
-def test_write_cache(tmpdir, writeback, output_checker):
-    if writeback and LooseVersion(platform.release()) < '3.14':
-        pytest.skip('Requires kernel 3.14 or newer')
-    # This test hangs under Valgrind when running close(fd)
-    # test_write_cache.c:test_fs(). Most likely this is because of an internal
-    # deadlock in valgrind, it probably assumes that until close() returns,
-    # control does not come to the program.
-    mnt_dir = str(tmpdir)
-    cmdline = [ pjoin(basename, 'test', 'test_write_cache'),
-                mnt_dir ]
-    if writeback:
-        cmdline.append('-owriteback_cache')
-    subprocess.check_call(cmdline, stdout=output_checker.fd, stderr=output_checker.fd)
-
-
-names = [ 'notify_inval_inode', 'invalidate_path' ]
-if fuse_proto >= (7,15):
-    names.append('notify_store_retrieve')
-@pytest.mark.skipif(fuse_proto < (7,12),
-                    reason='not supported by running kernel')
-@pytest.mark.parametrize("name", names)
-@pytest.mark.parametrize("notify", (True, False))
-def test_notify1(tmpdir, name, notify, output_checker):
-    mnt_dir = str(tmpdir)
-    cmdline = base_cmdline + \
-              [ pjoin(basename, 'example', name),
-                '-f', '--update-interval=1', mnt_dir ]
-    if not notify:
-        cmdline.append('--no-notify')
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-    try:
-        wait_for_mount(mount_process, mnt_dir)
-        filename = pjoin(mnt_dir, 'current_time')
-        with open(filename, 'r') as fh:
-            read1 = fh.read()
-        safe_sleep(2)
-        with open(filename, 'r') as fh:
-            read2 = fh.read()
-        if notify:
-            assert read1 != read2
-        else:
-            assert read1 == read2
-    except:
-        cleanup(mount_process, mnt_dir)
-        raise
-    else:
-        umount(mount_process, mnt_dir)
-
-@pytest.mark.skipif(fuse_proto < (7,12),
-                    reason='not supported by running kernel')
-@pytest.mark.parametrize("notify", (True, False))
-def test_notify_file_size(tmpdir, notify, output_checker):
-    mnt_dir = str(tmpdir)
-    cmdline = base_cmdline + \
-              [ pjoin(basename, 'example', 'invalidate_path'),
-                '-f', '--update-interval=1', mnt_dir ]
-    if not notify:
-        cmdline.append('--no-notify')
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-    try:
-        wait_for_mount(mount_process, mnt_dir)
-        filename = pjoin(mnt_dir, 'growing')
-        size = os.path.getsize(filename)
-        safe_sleep(2)
-        new_size = os.path.getsize(filename)
-        if notify:
-            assert new_size > size
-        else:
-            assert new_size == size
-    except:
-        cleanup(mount_process, mnt_dir)
-        raise
-    else:
-        umount(mount_process, mnt_dir)
diff --git a/test/test_examples.py b/test/test_examples.py
deleted file mode 100755
index 97eebfc..0000000
--- a/test/test_examples.py
+++ /dev/null
@@ -1,761 +0,0 @@
-#!/usr/bin/env python3
-
-if __name__ == '__main__':
-    import pytest
-    import sys
-    sys.exit(pytest.main([__file__] + sys.argv[1:]))
-
-import subprocess
-import os
-import sys
-import py
-import pytest
-import stat
-import shutil
-import filecmp
-import tempfile
-import time
-import errno
-import sys
-import platform
-from distutils.version import LooseVersion
-from tempfile import NamedTemporaryFile
-from contextlib import contextmanager
-from util import (wait_for_mount, umount, cleanup, base_cmdline,
-                  safe_sleep, basename, fuse_test_marker, test_printcap,
-                  fuse_proto, powerset)
-from os.path import join as pjoin
-
-pytestmark = fuse_test_marker()
-
-TEST_FILE = __file__
-
-with open(TEST_FILE, 'rb') as fh:
-    TEST_DATA = fh.read()
-
-def name_generator(__ctr=[0]):
-    __ctr[0] += 1
-    return 'testfile_%d' % __ctr[0]
-
-options = []
-if sys.platform == 'linux':
-    options.append('clone_fd')
-
-def invoke_directly(mnt_dir, name, options):
-    cmdline = base_cmdline + [ pjoin(basename, 'example', name),
-                               '-f', mnt_dir, '-o', ','.join(options) ]
-    if name == 'hello_ll':
-        # supports single-threading only
-        cmdline.append('-s')
-
-    return cmdline
-
-def invoke_mount_fuse(mnt_dir, name, options):
-    return base_cmdline + [ pjoin(basename, 'util', 'mount.fuse3'),
-                            name, mnt_dir, '-o', ','.join(options) ]
-
-def invoke_mount_fuse_drop_privileges(mnt_dir, name, options):
-    if os.getuid() != 0:
-        pytest.skip('drop_privileges requires root, skipping.')
-
-    return invoke_mount_fuse(mnt_dir, name, options + ('drop_privileges',))
-
-class raii_tmpdir:
-    def __init__(self):
-        self.d = tempfile.mkdtemp()
-
-    def __str__(self):
-        return str(self.d)
-
-    def mkdir(self, path):
-        return py.path.local(str(self.d)).mkdir(path)
-
-@pytest.fixture
-def short_tmpdir():
-    return raii_tmpdir()
-
-def readdir_inode(dir):
-    cmd = base_cmdline + [ pjoin(basename, 'test', 'readdir_inode'), dir ]
-    with subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                          universal_newlines=True) as proc:
-        lines = proc.communicate()[0].splitlines()
-    lines.sort()
-    return lines
-
-
-@pytest.mark.parametrize("cmdline_builder", (invoke_directly, invoke_mount_fuse,
-                                             invoke_mount_fuse_drop_privileges))
-@pytest.mark.parametrize("options", powerset(options))
-@pytest.mark.parametrize("name", ('hello', 'hello_ll'))
-def test_hello(tmpdir, name, options, cmdline_builder, output_checker):
-    mnt_dir = str(tmpdir)
-    mount_process = subprocess.Popen(
-        cmdline_builder(mnt_dir, name, options),
-        stdout=output_checker.fd, stderr=output_checker.fd)
-    try:
-        wait_for_mount(mount_process, mnt_dir)
-        assert os.listdir(mnt_dir) == [ 'hello' ]
-        filename = pjoin(mnt_dir, 'hello')
-        with open(filename, 'r') as fh:
-            assert fh.read() == 'Hello World!\n'
-        with pytest.raises(IOError) as exc_info:
-            open(filename, 'r+')
-        assert exc_info.value.errno == errno.EACCES
-        with pytest.raises(IOError) as exc_info:
-            open(filename + 'does-not-exist', 'r+')
-        assert exc_info.value.errno == errno.ENOENT
-    except:
-        cleanup(mount_process, mnt_dir)
-        raise
-    else:
-        umount(mount_process, mnt_dir)
-
-@pytest.mark.parametrize("writeback", (False, True))
-@pytest.mark.parametrize("name", ('passthrough', 'passthrough_plus',
-                           'passthrough_fh', 'passthrough_ll'))
-@pytest.mark.parametrize("debug", (False, True))
-def test_passthrough(short_tmpdir, name, debug, output_checker, writeback):
-    # Avoid false positives from libfuse debug messages
-    if debug:
-        output_checker.register_output(r'^   unique: [0-9]+, error: -[0-9]+ .+$',
-                                       count=0)
-
-    # test_syscalls prints "No error" under FreeBSD
-    output_checker.register_output(r"^ \d\d \[[^\]]+ message: 'No error: 0'\]",
-                                   count=0)
-
-    mnt_dir = str(short_tmpdir.mkdir('mnt'))
-    src_dir = str(short_tmpdir.mkdir('src'))
-
-    if name == 'passthrough_plus':
-        cmdline = base_cmdline + \
-                  [ pjoin(basename, 'example', 'passthrough'),
-                    '--plus', '-f', mnt_dir ]
-    else:
-        cmdline = base_cmdline + \
-                  [ pjoin(basename, 'example', name),
-                    '-f', mnt_dir ]
-    if debug:
-        cmdline.append('-d')
-
-    if writeback:
-        if name != 'passthrough_ll':
-            pytest.skip('example does not support writeback caching')
-        cmdline.append('-o')
-        cmdline.append('writeback')
-        
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-    try:
-        wait_for_mount(mount_process, mnt_dir)
-        work_dir = mnt_dir + src_dir
-
-        tst_statvfs(work_dir)
-        tst_readdir(src_dir, work_dir)
-        tst_readdir_big(src_dir, work_dir)
-        tst_open_read(src_dir, work_dir)
-        tst_open_write(src_dir, work_dir)
-        tst_create(work_dir)
-        tst_passthrough(src_dir, work_dir)
-        tst_append(src_dir, work_dir)
-        tst_seek(src_dir, work_dir)
-        tst_mkdir(work_dir)
-        tst_rmdir(work_dir, src_dir)
-        tst_unlink(work_dir, src_dir)
-        tst_symlink(work_dir)
-        if os.getuid() == 0:
-            tst_chown(work_dir)
-
-        # Underlying fs may not have full nanosecond resolution
-        tst_utimens(work_dir, ns_tol=1000)
-
-        tst_link(work_dir)
-        tst_truncate_path(work_dir)
-        tst_truncate_fd(work_dir)
-        tst_open_unlink(work_dir)
-
-        syscall_test_cmd = [ os.path.join(basename, 'test', 'test_syscalls'),
-                             work_dir, ':' + src_dir ]
-        if writeback:
-            # When writeback caching is enabled, kernel has to open files for
-            # reading even when userspace opens with O_WDONLY. This fails if the
-            # filesystem process doesn't have special permission.
-            syscall_test_cmd.append('-53')
-        subprocess.check_call(syscall_test_cmd)
-    except:
-        cleanup(mount_process, mnt_dir)
-        raise
-    else:
-        umount(mount_process, mnt_dir)
-
-@pytest.mark.parametrize("cache", (False, True))
-def test_passthrough_hp(short_tmpdir, cache, output_checker):
-    mnt_dir = str(short_tmpdir.mkdir('mnt'))
-    src_dir = str(short_tmpdir.mkdir('src'))
-
-    cmdline = base_cmdline + \
-              [ pjoin(basename, 'example', 'passthrough_hp'),
-                src_dir, mnt_dir ]
-
-    if not cache:
-        cmdline.append('--nocache')
-        
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-    try:
-        wait_for_mount(mount_process, mnt_dir)
-
-        tst_statvfs(mnt_dir)
-        tst_readdir(src_dir, mnt_dir)
-        tst_readdir_big(src_dir, mnt_dir)
-        tst_open_read(src_dir, mnt_dir)
-        tst_open_write(src_dir, mnt_dir)
-        tst_create(mnt_dir)
-        if not cache:
-            tst_passthrough(src_dir, mnt_dir)
-        tst_append(src_dir, mnt_dir)
-        tst_seek(src_dir, mnt_dir)
-        tst_mkdir(mnt_dir)
-        if cache:
-            # if cache is enabled, no operations should go through
-            # src_dir as the cache will become stale.
-            tst_rmdir(mnt_dir)
-            tst_unlink(mnt_dir)
-        else:
-            tst_rmdir(mnt_dir, src_dir)
-            tst_unlink(mnt_dir, src_dir)
-        tst_symlink(mnt_dir)
-        if os.getuid() == 0:
-            tst_chown(mnt_dir)
-
-        # Underlying fs may not have full nanosecond resolution
-        tst_utimens(mnt_dir, ns_tol=1000)
-
-        tst_link(mnt_dir)
-        tst_truncate_path(mnt_dir)
-        tst_truncate_fd(mnt_dir)
-        tst_open_unlink(mnt_dir)
-
-        # test_syscalls assumes that changes in source directory
-        # will be reflected immediately in mountpoint, so we
-        # can't use it.
-        if not cache:
-            syscall_test_cmd = [ os.path.join(basename, 'test', 'test_syscalls'),
-                             mnt_dir, ':' + src_dir ]
-            # unlinked testfiles check fails without kernel fix
-            # "fuse: fix illegal access to inode with reused nodeid"
-            # so opt-in for this test from kernel 5.14
-            if LooseVersion(platform.release()) >= '5.14':
-                syscall_test_cmd.append('-u')
-            subprocess.check_call(syscall_test_cmd)
-    except:
-        cleanup(mount_process, mnt_dir)
-        raise
-    else:
-        umount(mount_process, mnt_dir)
-
-        
-@pytest.mark.skipif(fuse_proto < (7,11),
-                    reason='not supported by running kernel')
-def test_ioctl(tmpdir, output_checker):
-    progname = pjoin(basename, 'example', 'ioctl')
-    if not os.path.exists(progname):
-        pytest.skip('%s not built' % os.path.basename(progname))
-    
-    mnt_dir = str(tmpdir)
-    testfile = pjoin(mnt_dir, 'fioc')
-    cmdline = base_cmdline + [progname, '-f', mnt_dir ]
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-    try:
-        wait_for_mount(mount_process, mnt_dir)
-
-        cmdline = base_cmdline + \
-                  [ pjoin(basename, 'example', 'ioctl_client'),
-                    testfile ]
-        assert subprocess.check_output(cmdline) == b'0\n'
-        with open(testfile, 'wb') as fh:
-            fh.write(b'foobar')
-        assert subprocess.check_output(cmdline) == b'6\n'
-        subprocess.check_call(cmdline + [ '3' ])
-        with open(testfile, 'rb') as fh:
-            assert fh.read()== b'foo'
-    except:
-        cleanup(mount_process, mnt_dir)
-        raise
-    else:
-        umount(mount_process, mnt_dir)
-
-def test_poll(tmpdir, output_checker):
-    mnt_dir = str(tmpdir)
-    cmdline = base_cmdline + [pjoin(basename, 'example', 'poll'),
-               '-f', mnt_dir ]
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-    try:
-        wait_for_mount(mount_process, mnt_dir)
-        cmdline = base_cmdline + \
-                  [ pjoin(basename, 'example', 'poll_client') ]
-        subprocess.check_call(cmdline, cwd=mnt_dir)
-    except:
-        cleanup(mount_process, mnt_dir)
-        raise
-    else:
-        umount(mount_process, mnt_dir)
-
-def test_null(tmpdir, output_checker):
-    progname = pjoin(basename, 'example', 'null')
-    if not os.path.exists(progname):
-        pytest.skip('%s not built' % os.path.basename(progname))
-    
-    mnt_file = str(tmpdir) + '/file'
-    with open(mnt_file, 'w') as fh:
-        fh.write('dummy')
-    cmdline = base_cmdline + [ progname, '-f', mnt_file ]
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-    def test_fn(name):
-        return os.stat(name).st_size > 4000
-    try:
-        wait_for_mount(mount_process, mnt_file, test_fn)
-        with open(mnt_file, 'rb') as fh:
-            assert fh.read(382) == b'\0' * 382
-        with open(mnt_file, 'wb') as fh:
-            fh.write(b'whatever')
-    except:
-        cleanup(mount_process, mnt_file)
-        raise
-    else:
-        umount(mount_process, mnt_file)
-
-
-@pytest.mark.skipif(fuse_proto < (7,12),
-                    reason='not supported by running kernel')
-@pytest.mark.parametrize("notify", (True, False))
-def test_notify_inval_entry(tmpdir, notify, output_checker):
-    mnt_dir = str(tmpdir)
-    cmdline = base_cmdline + \
-              [ pjoin(basename, 'example', 'notify_inval_entry'),
-                '-f', '--update-interval=1',
-                '--timeout=5', mnt_dir ]
-    if not notify:
-        cmdline.append('--no-notify')
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-    try:
-        wait_for_mount(mount_process, mnt_dir)
-        fname = pjoin(mnt_dir, os.listdir(mnt_dir)[0])
-        try:
-            os.stat(fname)
-        except FileNotFoundError:
-            # We may have hit a race condition and issued
-            # readdir just before the name changed
-            fname = pjoin(mnt_dir, os.listdir(mnt_dir)[0])
-            os.stat(fname)
-
-        safe_sleep(2)
-        if not notify:
-            os.stat(fname)
-            safe_sleep(5)
-        with pytest.raises(FileNotFoundError):
-            os.stat(fname)
-    except:
-        cleanup(mount_process, mnt_dir)
-        raise
-    else:
-        umount(mount_process, mnt_dir)
-
-@pytest.mark.skipif(os.getuid() != 0,
-                    reason='needs to run as root')
-def test_cuse(output_checker):
-
-    # Valgrind warns about unknown ioctls, that's ok
-    output_checker.register_output(r'^==([0-9]+).+unhandled ioctl.+\n'
-                                   r'==\1== \s{3}.+\n'
-                                   r'==\1== \s{3}.+$', count=0)
-
-    devname = 'cuse-test-%d' % os.getpid()
-    devpath = '/dev/%s' % devname
-    cmdline = base_cmdline + \
-              [ pjoin(basename, 'example', 'cuse'),
-                '-f', '--name=%s' % devname ]
-    mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
-                                     stderr=output_checker.fd)
-
-    cmdline = base_cmdline + \
-              [ pjoin(basename, 'example', 'cuse_client'),
-                devpath ]
-    try:
-        wait_for_mount(mount_process, devpath,
-                       test_fn=os.path.exists)
-        assert subprocess.check_output(cmdline + ['s']) == b'0\n'
-        data = b'some test data'
-        off = 5
-        proc = subprocess.Popen(cmdline + [ 'w', str(len(data)), str(off) ],
-                                stdin=subprocess.PIPE)
-        proc.stdin.write(data)
-        proc.stdin.close()
-        assert proc.wait(timeout=10) == 0
-        size = str(off + len(data)).encode() + b'\n'
-        assert subprocess.check_output(cmdline + ['s']) == size
-        out = subprocess.check_output(
-            cmdline + [ 'r', str(off + len(data) + 2), '0' ])
-        assert out == (b'\0' * off) + data
-    finally:
-        mount_process.terminate()
-
-@contextmanager
-def os_open(name, flags):
-    fd = os.open(name, flags)
-    try:
-        yield fd
-    finally:
-        os.close(fd)
-
-def os_create(name):
-    os.close(os.open(name, os.O_CREAT | os.O_RDWR))
-
-def tst_unlink(mnt_dir, src_dir=None):
-    name = name_generator()
-    fullname = mnt_dir + "/" + name
-    srcname = fullname
-    if src_dir is not None:
-        srcname = pjoin(src_dir, name)
-    with open(srcname, 'wb') as fh:
-        fh.write(b'hello')
-    assert name in os.listdir(mnt_dir)
-    os.unlink(fullname)
-    with pytest.raises(OSError) as exc_info:
-        os.stat(fullname)
-    assert exc_info.value.errno == errno.ENOENT
-    assert name not in os.listdir(mnt_dir)
-
-def tst_mkdir(mnt_dir):
-    dirname = name_generator()
-    fullname = mnt_dir + "/" + dirname
-    os.mkdir(fullname)
-    fstat = os.stat(fullname)
-    assert stat.S_ISDIR(fstat.st_mode)
-    assert os.listdir(fullname) ==  []
-    # Some filesystem (e.g. BTRFS) don't track st_nlink for directories
-    assert fstat.st_nlink in (1,2)
-    assert dirname in os.listdir(mnt_dir)
-
-def tst_rmdir(mnt_dir, src_dir=None):
-    name = name_generator()
-    fullname = mnt_dir + "/" + name
-    srcname = fullname
-    if src_dir is not None:
-        srcname = pjoin(src_dir, name)
-    os.mkdir(srcname)
-    assert name in os.listdir(mnt_dir)
-    os.rmdir(fullname)
-    with pytest.raises(OSError) as exc_info:
-        os.stat(fullname)
-    assert exc_info.value.errno == errno.ENOENT
-    assert name not in os.listdir(mnt_dir)
-
-def tst_symlink(mnt_dir):
-    linkname = name_generator()
-    fullname = mnt_dir + "/" + linkname
-    os.symlink("/imaginary/dest", fullname)
-    fstat = os.lstat(fullname)
-    assert stat.S_ISLNK(fstat.st_mode)
-    assert os.readlink(fullname) == "/imaginary/dest"
-    assert fstat.st_nlink == 1
-    assert linkname in os.listdir(mnt_dir)
-
-def tst_create(mnt_dir):
-    name = name_generator()
-    fullname = pjoin(mnt_dir, name)
-    with pytest.raises(OSError) as exc_info:
-        os.stat(fullname)
-    assert exc_info.value.errno == errno.ENOENT
-    assert name not in os.listdir(mnt_dir)
-
-    fd = os.open(fullname, os.O_CREAT | os.O_RDWR)
-    os.close(fd)
-
-    assert name in os.listdir(mnt_dir)
-    fstat = os.lstat(fullname)
-    assert stat.S_ISREG(fstat.st_mode)
-    assert fstat.st_nlink == 1
-    assert fstat.st_size == 0
-
-def tst_chown(mnt_dir):
-    filename = pjoin(mnt_dir, name_generator())
-    os.mkdir(filename)
-    fstat = os.lstat(filename)
-    uid = fstat.st_uid
-    gid = fstat.st_gid
-
-    uid_new = uid + 1
-    os.chown(filename, uid_new, -1)
-    fstat = os.lstat(filename)
-    assert fstat.st_uid == uid_new
-    assert fstat.st_gid == gid
-
-    gid_new = gid + 1
-    os.chown(filename, -1, gid_new)
-    fstat = os.lstat(filename)
-    assert fstat.st_uid == uid_new
-    assert fstat.st_gid == gid_new
-
-def tst_open_read(src_dir, mnt_dir):
-    name = name_generator()
-    with open(pjoin(src_dir, name), 'wb') as fh_out, \
-         open(TEST_FILE, 'rb') as fh_in:
-        shutil.copyfileobj(fh_in, fh_out)
-
-    assert filecmp.cmp(pjoin(mnt_dir, name), TEST_FILE, False)
-
-def tst_open_write(src_dir, mnt_dir):
-    name = name_generator()
-    os_create(pjoin(src_dir, name))
-    fullname = pjoin(mnt_dir, name)
-    with open(fullname, 'wb') as fh_out, \
-         open(TEST_FILE, 'rb') as fh_in:
-        shutil.copyfileobj(fh_in, fh_out)
-
-    assert filecmp.cmp(fullname, TEST_FILE, False)
-
-def tst_append(src_dir, mnt_dir):
-    name = name_generator()
-    os_create(pjoin(src_dir, name))
-    fullname = pjoin(mnt_dir, name)
-    with os_open(fullname, os.O_WRONLY) as fd:
-        os.write(fd, b'foo\n')
-    with os_open(fullname, os.O_WRONLY|os.O_APPEND) as fd:
-        os.write(fd, b'bar\n')
-
-    with open(fullname, 'rb') as fh:
-        assert fh.read() == b'foo\nbar\n'
-
-def tst_seek(src_dir, mnt_dir):
-    name = name_generator()
-    os_create(pjoin(src_dir, name))
-    fullname = pjoin(mnt_dir, name)
-    with os_open(fullname, os.O_WRONLY) as fd:
-        os.lseek(fd, 1, os.SEEK_SET)
-        os.write(fd, b'foobar\n')
-    with os_open(fullname, os.O_WRONLY) as fd:
-        os.lseek(fd, 4, os.SEEK_SET)
-        os.write(fd, b'com')
-        
-    with open(fullname, 'rb') as fh:
-        assert fh.read() == b'\0foocom\n'
-        
-def tst_open_unlink(mnt_dir):
-    name = pjoin(mnt_dir, name_generator())
-    data1 = b'foo'
-    data2 = b'bar'
-    fullname = pjoin(mnt_dir, name)
-    with open(fullname, 'wb+', buffering=0) as fh:
-        fh.write(data1)
-        os.unlink(fullname)
-        with pytest.raises(OSError) as exc_info:
-            os.stat(fullname)
-        assert exc_info.value.errno == errno.ENOENT
-        assert name not in os.listdir(mnt_dir)
-        fh.write(data2)
-        fh.seek(0)
-        assert fh.read() == data1+data2
-
-def tst_statvfs(mnt_dir):
-    os.statvfs(mnt_dir)
-
-def tst_link(mnt_dir):
-    name1 = pjoin(mnt_dir, name_generator())
-    name2 = pjoin(mnt_dir, name_generator())
-    shutil.copyfile(TEST_FILE, name1)
-    assert filecmp.cmp(name1, TEST_FILE, False)
-
-    fstat1 = os.lstat(name1)
-    assert fstat1.st_nlink == 1
-
-    os.link(name1, name2)
-
-    fstat1 = os.lstat(name1)
-    fstat2 = os.lstat(name2)
-    assert fstat1 == fstat2
-    assert fstat1.st_nlink == 2
-    assert os.path.basename(name2) in os.listdir(mnt_dir)
-    assert filecmp.cmp(name1, name2, False)
-
-    # Since RELEASE requests are asynchronous, it is possible that
-    # libfuse still considers the file to be open at this point
-    # and (since -o hard_remove is not used) renames it instead of
-    # deleting it. In that case, the following lstat() call will
-    # still report an st_nlink value of 2 (cf. issue #157).
-    os.unlink(name2)
-
-    assert os.path.basename(name2) not in os.listdir(mnt_dir)
-    with pytest.raises(FileNotFoundError):
-        os.lstat(name2)
-
-    # See above, we may have to wait until RELEASE has been
-    # received before the st_nlink value is correct.
-    maxwait = time.time() + 2
-    fstat1 = os.lstat(name1)
-    while fstat1.st_nlink == 2 and time.time() < maxwait:
-        fstat1 = os.lstat(name1)
-        time.sleep(0.1)
-    assert fstat1.st_nlink == 1
-
-    os.unlink(name1)
-
-def tst_readdir(src_dir, mnt_dir):
-    newdir = name_generator()
-
-    src_newdir = pjoin(src_dir, newdir)
-    mnt_newdir = pjoin(mnt_dir, newdir)
-    file_ = src_newdir + "/" + name_generator()
-    subdir = src_newdir + "/" + name_generator()
-    subfile = subdir + "/" + name_generator()
-
-    os.mkdir(src_newdir)
-    shutil.copyfile(TEST_FILE, file_)
-    os.mkdir(subdir)
-    shutil.copyfile(TEST_FILE, subfile)
-
-    listdir_is = os.listdir(mnt_newdir)
-    listdir_is.sort()
-    listdir_should = [ os.path.basename(file_), os.path.basename(subdir) ]
-    listdir_should.sort()
-    assert listdir_is == listdir_should
-
-    inodes_is = readdir_inode(mnt_newdir)
-    inodes_should = readdir_inode(src_newdir)
-    assert inodes_is == inodes_should
-
-    os.unlink(file_)
-    os.unlink(subfile)
-    os.rmdir(subdir)
-    os.rmdir(src_newdir)
-
-def tst_readdir_big(src_dir, mnt_dir):
-
-    # Add enough entries so that readdir needs to be called
-    # multiple times.
-    fnames = []
-    for i in range(500):
-        fname  = ('A rather long filename to make sure that we '
-                  'fill up the buffer - ' * 3) + str(i)
-        with open(pjoin(src_dir, fname), 'w') as fh:
-            fh.write('File %d' % i)
-        fnames.append(fname)
-
-    listdir_is = sorted(os.listdir(mnt_dir))
-    listdir_should = sorted(os.listdir(src_dir))
-    assert listdir_is == listdir_should
-
-    inodes_is = readdir_inode(mnt_dir)
-    inodes_should = readdir_inode(src_dir)
-    assert inodes_is == inodes_should
-
-    for fname in fnames:
-        stat_src = os.stat(pjoin(src_dir, fname))
-        stat_mnt = os.stat(pjoin(mnt_dir, fname))
-        assert stat_src.st_ino == stat_mnt.st_ino
-        assert stat_src.st_mtime == stat_mnt.st_mtime
-        assert stat_src.st_ctime == stat_mnt.st_ctime
-        assert stat_src.st_size == stat_mnt.st_size
-        os.unlink(pjoin(src_dir, fname))
-
-def tst_truncate_path(mnt_dir):
-    assert len(TEST_DATA) > 1024
-
-    filename = pjoin(mnt_dir, name_generator())
-    with open(filename, 'wb') as fh:
-        fh.write(TEST_DATA)
-
-    fstat = os.stat(filename)
-    size = fstat.st_size
-    assert size == len(TEST_DATA)
-
-    # Add zeros at the end
-    os.truncate(filename, size + 1024)
-    assert os.stat(filename).st_size == size + 1024
-    with open(filename, 'rb') as fh:
-        assert fh.read(size) == TEST_DATA
-        assert fh.read(1025) == b'\0' * 1024
-
-    # Truncate data
-    os.truncate(filename, size - 1024)
-    assert os.stat(filename).st_size == size - 1024
-    with open(filename, 'rb') as fh:
-        assert fh.read(size) == TEST_DATA[:size-1024]
-
-    os.unlink(filename)
-
-def tst_truncate_fd(mnt_dir):
-    assert len(TEST_DATA) > 1024
-    with NamedTemporaryFile('w+b', 0, dir=mnt_dir) as fh:
-        fd = fh.fileno()
-        fh.write(TEST_DATA)
-        fstat = os.fstat(fd)
-        size = fstat.st_size
-        assert size == len(TEST_DATA)
-
-        # Add zeros at the end
-        os.ftruncate(fd, size + 1024)
-        assert os.fstat(fd).st_size == size + 1024
-        fh.seek(0)
-        assert fh.read(size) == TEST_DATA
-        assert fh.read(1025) == b'\0' * 1024
-
-        # Truncate data
-        os.ftruncate(fd, size - 1024)
-        assert os.fstat(fd).st_size == size - 1024
-        fh.seek(0)
-        assert fh.read(size) == TEST_DATA[:size-1024]
-
-def tst_utimens(mnt_dir, ns_tol=0):
-    filename = pjoin(mnt_dir, name_generator())
-    os.mkdir(filename)
-    fstat = os.lstat(filename)
-
-    atime = fstat.st_atime + 42.28
-    mtime = fstat.st_mtime - 42.23
-    if sys.version_info < (3,3):
-        os.utime(filename, (atime, mtime))
-    else:
-        atime_ns = fstat.st_atime_ns + int(42.28*1e9)
-        mtime_ns = fstat.st_mtime_ns - int(42.23*1e9)
-        os.utime(filename, None, ns=(atime_ns, mtime_ns))
-
-    fstat = os.lstat(filename)
-
-    assert abs(fstat.st_atime - atime) < 1
-    assert abs(fstat.st_mtime - mtime) < 1
-    if sys.version_info >= (3,3):
-        assert abs(fstat.st_atime_ns - atime_ns) <= ns_tol
-        assert abs(fstat.st_mtime_ns - mtime_ns) <= ns_tol
-
-def tst_passthrough(src_dir, mnt_dir):
-    name = name_generator()
-    src_name = pjoin(src_dir, name)
-    mnt_name = pjoin(src_dir, name)
-    assert name not in os.listdir(src_dir)
-    assert name not in os.listdir(mnt_dir)
-    with open(src_name, 'w') as fh:
-        fh.write('Hello, world')
-    assert name in os.listdir(src_dir)
-    assert name in os.listdir(mnt_dir)
-    assert os.stat(src_name) == os.stat(mnt_name)
-
-    name = name_generator()
-    src_name = pjoin(src_dir, name)
-    mnt_name = pjoin(src_dir, name)
-    assert name not in os.listdir(src_dir)
-    assert name not in os.listdir(mnt_dir)
-    with open(mnt_name, 'w') as fh:
-        fh.write('Hello, world')
-    assert name in os.listdir(src_dir)
-    assert name in os.listdir(mnt_dir)
-    assert os.stat(src_name) == os.stat(mnt_name)
-
-# avoid warning about unused import
-test_printcap
-
-    
diff --git a/test/test_setattr.c b/test/test_setattr.c
deleted file mode 100644
index fa7c8ba..0000000
--- a/test/test_setattr.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-
-#define FUSE_USE_VERSION 30
-
-#include <config.h>
-#include <fuse_lowlevel.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <pthread.h>
-
-#ifndef __linux__
-#include <limits.h>
-#else
-#include <linux/limits.h>
-#endif
-
-#define FILE_INO 2
-#define FILE_NAME "truncate_me"
-
-static int got_fh;
-static mode_t file_mode = S_IFREG | 0644;
-
-static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
-    stbuf->st_ino = ino;
-    if (ino == FUSE_ROOT_ID) {
-        stbuf->st_mode = S_IFDIR | 0755;
-        stbuf->st_nlink = 1;
-    }
-
-    else if (ino == FILE_INO) {
-        stbuf->st_mode = file_mode;
-        stbuf->st_nlink = 1;
-        stbuf->st_size = 0;
-    }
-
-    else
-        return -1;
-
-    return 0;
-}
-
-static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
-                       const char *name) {
-    struct fuse_entry_param e;
-    memset(&e, 0, sizeof(e));
-
-    if (parent != FUSE_ROOT_ID)
-        goto err_out;
-    else if (strcmp(name, FILE_NAME) == 0)
-        e.ino = FILE_INO;
-    else
-        goto err_out;
-
-    if (tfs_stat(e.ino, &e.attr) != 0)
-        goto err_out;
-    fuse_reply_entry(req, &e);
-    return;
-
-err_out:
-    fuse_reply_err(req, ENOENT);
-}
-
-static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
-                        struct fuse_file_info *fi) {
-    struct stat stbuf;
-
-    (void) fi;
-
-    memset(&stbuf, 0, sizeof(stbuf));
-    if (tfs_stat(ino, &stbuf) != 0)
-        fuse_reply_err(req, ENOENT);
-    else
-        fuse_reply_attr(req, &stbuf, 5);
-}
-
-static void tfs_open(fuse_req_t req, fuse_ino_t ino,
-                     struct fuse_file_info *fi) {
-    if (ino == FUSE_ROOT_ID)
-        fuse_reply_err(req, EISDIR);
-    else {
-        assert(ino == FILE_INO);
-        fi->fh = FILE_INO;
-        fuse_reply_open(req, fi);
-    }
-}
-
-static void tfs_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
-                         int to_set, struct fuse_file_info *fi) {
-    if(ino != FILE_INO ||
-       !(to_set & FUSE_SET_ATTR_MODE)) {
-        fuse_reply_err(req, EINVAL);
-        return;
-    }
-
-    if(fi == NULL)
-        fprintf(stderr, "setattr with fi == NULL\n");
-    else if (fi->fh != FILE_INO)
-        fprintf(stderr, "setattr with wrong fi->fh\n");
-    else {
-        fprintf(stderr, "setattr ok\n");
-        got_fh = 1;
-        file_mode = attr->st_mode;
-    }
-
-    tfs_getattr(req, ino, fi);
-}
-
-static struct fuse_lowlevel_ops tfs_oper = {
-    .lookup	= tfs_lookup,
-    .getattr	= tfs_getattr,
-    .open	= tfs_open,
-    .setattr	= tfs_setattr,
-};
-
-static void* run_fs(void *data) {
-    struct fuse_session *se = (struct fuse_session*) data;
-    assert(fuse_session_loop(se) == 0);
-    return NULL;
-}
-
-static void test_fs(char *mountpoint) {
-    char fname[PATH_MAX];
-    int fd;
-
-    assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME,
-                     mountpoint) > 0);
-    fd = open(fname, O_WRONLY);
-    if (fd == -1) {
-        perror(fname);
-        assert(0);
-    }
-
-    assert(fchmod(fd, 0600) == 0);
-    close(fd);
-}
-
-int main(int argc, char *argv[]) {
-    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-    struct fuse_session *se;
-    struct fuse_cmdline_opts fuse_opts;
-    pthread_t fs_thread;
-
-    assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
-#ifndef __FreeBSD__    
-    assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
-#endif
-    se = fuse_session_new(&args, &tfs_oper,
-                          sizeof(tfs_oper), NULL);
-    assert (se != NULL);
-    assert(fuse_set_signal_handlers(se) == 0);
-    assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
-
-    /* Start file-system thread */
-    assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
-
-    /* Do test */
-    test_fs(fuse_opts.mountpoint);
-
-    /* Stop file system */
-    assert(pthread_cancel(fs_thread) == 0);
-
-    fuse_session_unmount(se);
-    assert(got_fh == 1);
-    fuse_remove_signal_handlers(se);
-    fuse_session_destroy(se);
-
-    printf("Test completed successfully.\n");
-    return 0;
-}
-
-
-/**
- * Local Variables:
- * mode: c
- * indent-tabs-mode: nil
- * c-basic-offset: 4
- * End:
- */
diff --git a/test/test_syscalls.c b/test/test_syscalls.c
deleted file mode 100644
index 160a2ac..0000000
--- a/test/test_syscalls.c
+++ /dev/null
@@ -1,2097 +0,0 @@
-#define _GNU_SOURCE
-#include "config.h"
-
-#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/socket.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-
-#ifndef ALLPERMS
-# define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */
-#endif
-
-
-static const char *basepath;
-static const char *basepath_r;
-static char testfile[1024];
-static char testfile2[1024];
-static char testdir[1024];
-static char testdir2[1024];
-static char testsock[1024];
-static char subfile[1280];
-
-static char testfile_r[1024];
-static char testfile2_r[1024];
-static char testdir_r[1024];
-static char testdir2_r[1024];
-static char subfile_r[1280];
-
-static char testname[256];
-static char testdata[] = "abcdefghijklmnopqrstuvwxyz";
-static char testdata2[] = "1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./";
-static const char *testdir_files[] = { "f1", "f2", NULL};
-static long seekdir_offsets[4];
-static char zerodata[4096];
-static int testdatalen = sizeof(testdata) - 1;
-static int testdata2len = sizeof(testdata2) - 1;
-static unsigned int testnum = 0;
-static unsigned int select_test = 0;
-static unsigned int skip_test = 0;
-static unsigned int unlinked_test = 0;
-
-#define MAX_ENTRIES 1024
-#define MAX_TESTS 100
-
-static struct test {
-	int fd;
-	struct stat stat;
-} tests[MAX_TESTS];
-
-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 int is_dot_or_dotdot(const char *name) {
-    return name[0] == '.' &&
-           (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
-}
-
-static void success(void)
-{
-	fprintf(stderr, "%s OK\n", testname);
-}
-
-#define this_test (&tests[testnum-1])
-#define next_test (&tests[testnum])
-
-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, "]");
-	// Use dedicated testfile per test
-	sprintf(testfile, "%s/testfile.%d", basepath, testnum);
-	sprintf(testfile_r, "%s/testfile.%d", basepath_r, testnum);
-	if (testnum > MAX_TESTS) {
-		fprintf(stderr, "%s - too many tests\n", testname);
-		exit(1);
-	}
-	this_test->fd = -1;
-}
-
-#define start_test(msg, args...) { \
-	testnum++; \
-	if ((select_test && testnum != select_test) || \
-	    (testnum == skip_test)) { \
-		return 0; \
-	} \
-	__start_test(msg, ##args);		\
-}
-
-#define PERROR(msg) test_perror(__FUNCTION__, msg)
-#define ERROR(msg, args...) test_error(__FUNCTION__, msg, ##args)
-
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-
-static int st_check_size(struct stat *st, int len)
-{
-	if (st->st_size != len) {
-		ERROR("length %u instead of %u", (int) st->st_size,
-		      (int) len);
-		return -1;
-	}
-	return 0;
-}
-
-static int check_size(const char *path, int len)
-{
-	struct stat stbuf;
-	int res = stat(path, &stbuf);
-	if (res == -1) {
-		PERROR("stat");
-		return -1;
-	}
-	return st_check_size(&stbuf, len);
-}
-
-static int check_testfile_size(const char *path, int len)
-{
-	this_test->stat.st_size = len;
-	return check_size(path, len);
-}
-
-static int st_check_type(struct stat *st, mode_t type)
-{
-	if ((st->st_mode & S_IFMT) != type) {
-		ERROR("type 0%o instead of 0%o", st->st_mode & S_IFMT, type);
-		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;
-	}
-	return st_check_type(&stbuf, type);
-}
-
-static int st_check_mode(struct stat *st, mode_t mode)
-{
-	if ((st->st_mode & ALLPERMS) != mode) {
-		ERROR("mode 0%o instead of 0%o", st->st_mode & ALLPERMS,
-		      mode);
-		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;
-	}
-	return st_check_mode(&stbuf, mode);
-}
-
-static int check_testfile_mode(const char *path, mode_t mode)
-{
-	this_test->stat.st_mode &= ~ALLPERMS;
-	this_test->stat.st_mode |= mode;
-	return check_mode(path, mode);
-}
-
-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 st_check_nlink(struct stat *st, nlink_t nlink)
-{
-	if (st->st_nlink != nlink) {
-		ERROR("nlink %li instead of %li", (long) st->st_nlink,
-		      (long) nlink);
-		return -1;
-	}
-	return 0;
-}
-
-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;
-	}
-	return st_check_nlink(&stbuf, nlink);
-}
-
-static int fcheck_stat(int fd, int flags, struct stat *st)
-{
-	struct stat stbuf;
-	int res = fstat(fd, &stbuf);
-	if (res == -1) {
-		if (flags & O_PATH) {
-			// With O_PATH fd, the server does not have to keep
-			// the inode alive so FUSE inode may be stale or bad
-			if (errno == ESTALE || errno == EIO || errno == ENOENT)
-				return 0;
-		}
-		PERROR("fstat");
-		return -1;
-	}
-
-	int err = 0;
-	err += st_check_type(&stbuf, st->st_mode & S_IFMT);
-	err += st_check_mode(&stbuf, st->st_mode & ALLPERMS);
-	err += st_check_size(&stbuf, st->st_size);
-	err += st_check_nlink(&stbuf, st->st_nlink);
-
-	return err;
-}
-
-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];
-	}
-	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;
-		}
-		if (is_dot_or_dotdot(de->d_name))
-			continue;
-		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 create_path_fd(const char *path, const char *data, int len)
-{
-	int path_fd;
-	int res;
-
-	res = create_file(path, data, len);
-	if (res == -1)
-		return -1;
-
-	path_fd = open(path, O_PATH);
-	if (path_fd == -1)
-		PERROR("open(O_PATH)");
-
-	return path_fd;
-}
-
-// Can be called once per test
-static int create_testfile(const char *path, const char *data, int len)
-{
-	struct test *t = this_test;
-	struct stat *st = &t->stat;
-	int res, fd;
-
-	if (t->fd > 0) {
-		ERROR("testfile already created");
-		return -1;
-	}
-
-	fd = create_path_fd(path, data, len);
-	if (fd == -1)
-		return -1;
-
-	t->fd = fd;
-
-	res = fstat(fd, st);
-	if (res == -1) {
-		PERROR("fstat");
-		return -1;
-	}
-
-	return 0;
-}
-
-static int check_unlinked_testfile(int fd)
-{
-	struct stat *st = &this_test->stat;
-
-	st->st_nlink = 0;
-	return fcheck_stat(fd, O_PATH, st);
-}
-
-// Check recorded testfiles after all tests completed
-static int check_unlinked_testfiles(void)
-{
-	int fd;
-	int res, err = 0;
-	int num = testnum;
-
-	if (!unlinked_test)
-		return 0;
-
-	testnum = 0;
-	while (testnum < num) {
-		fd = next_test->fd;
-		start_test("check_unlinked_testfile");
-		if (fd == -1)
-			continue;
-
-		err += check_unlinked_testfile(fd);
-		res = close(fd);
-		if (res == -1) {
-			PERROR("close(test_fd)");
-			err--;
-		}
-	}
-
-	if (err) {
-		fprintf(stderr, "%i unlinked testfile checks failed\n", -err);
-		return 1;
-	}
-
-	return err;
-}
-
-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[1280];
-		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[1280];
-		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_testfile(testfile, data, datalen);
-	if (res == -1)
-		return -1;
-
-	res = truncate(testfile, len);
-	if (res == -1) {
-		PERROR("truncate");
-		return -1;
-	}
-	res = check_testfile_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_testfile(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_testfile_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_testfile_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_seekdir(void)
-{
-	int i;
-	int res;
-	DIR *dp;
-	struct dirent *de;
-
-	start_test("seekdir");
-	res = create_dir(testdir, testdir_files);
-	if (res == -1)
-		return res;
-
-	dp = opendir(testdir);
-	if (dp == NULL) {
-		PERROR("opendir");
-		return -1;
-	}
-
-	/* Remember dir offsets */
-	for (i = 0; i < ARRAY_SIZE(seekdir_offsets); i++) {
-		seekdir_offsets[i] = telldir(dp);
-		errno = 0;
-		de = readdir(dp);
-		if (de == NULL) {
-			if (errno) {
-				PERROR("readdir");
-				goto fail;
-			}
-			break;
-		}
-	}
-
-	/* Walk until the end of directory */
-	while (de)
-		de = readdir(dp);
-
-	/* Start from the last valid dir offset and seek backwards */
-	for (i--; i >= 0; i--) {
-		seekdir(dp, seekdir_offsets[i]);
-		de = readdir(dp);
-		if (de == NULL) {
-			ERROR("Unexpected end of directory after seekdir()");
-			goto fail;
-		}
-	}
-
-	closedir(dp);
-	res = cleanup_dir(testdir, testdir_files, 0);
-	if (!res)
-		success();
-	return res;
-fail:
-	closedir(dp);
-	cleanup_dir(testdir, testdir_files, 1);
-	return -1;
-}
-
-#ifdef HAVE_COPY_FILE_RANGE
-static int test_copy_file_range(void)
-{
-	const char *data = testdata;
-	int datalen = testdatalen;
-	int err = 0;
-	int res;
-	int fd_in, fd_out;
-	off_t pos_in = 0, pos_out = 0;
-
-	start_test("copy_file_range");
-	unlink(testfile);
-	fd_in = open(testfile, O_CREAT | O_RDWR, 0644);
-	if (fd_in == -1) {
-		PERROR("creat");
-		return -1;
-	}
-	res = write(fd_in, data, datalen);
-	if (res == -1) {
-		PERROR("write");
-		close(fd_in);
-		return -1;
-	}
-	if (res != datalen) {
-		ERROR("write is short: %u instead of %u", res, datalen);
-		close(fd_in);
-		return -1;
-	}
-
-	unlink(testfile2);
-	fd_out = creat(testfile2, 0644);
-	if (fd_out == -1) {
-		PERROR("creat");
-		close(fd_in);
-		return -1;
-	}
-	res = copy_file_range(fd_in, &pos_in, fd_out, &pos_out, datalen, 0);
-	if (res == -1) {
-		PERROR("copy_file_range");
-		close(fd_in);
-		close(fd_out);
-		return -1;
-	}
-	if (res != datalen) {
-		ERROR("copy is short: %u instead of %u", res, datalen);
-		close(fd_in);
-		close(fd_out);
-		return -1;
-	}
-
-	res = close(fd_in);
-	if (res == -1) {
-		PERROR("close");
-		close(fd_out);
-		return -1;
-	}
-	res = close(fd_out);
-	if (res == -1) {
-		PERROR("close");
-		return -1;
-	}
-
-	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;
-	if (err)
-		return -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;
-}
-#else
-static int test_copy_file_range(void)
-{
-	return 0;
-}
-#endif
-
-static int test_utime(void)
-{
-	struct utimbuf utm;
-	time_t atime = 987631200;
-	time_t mtime = 123116400;
-	int res;
-
-	start_test("utime");
-	res = create_testfile(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) {
-		close(fd);
-		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;
-	}
-	struct stat st = {
-		.st_mode = S_IFREG | 0644,
-		.st_size = datalen,
-	};
-	err = fcheck_stat(fd, O_RDWR, &st);
-	err += fcheck_data(fd, data, 0, datalen);
-	res = close(fd);
-	if (res == -1) {
-		PERROR("close");
-		err--;
-	}
-	if (err)
-		return -1;
-
-	success();
-	return 0;
-}
-
-#ifndef __FreeBSD__
-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;
-}
-#endif
-
-#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 & S_IRUSR))
-		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 & S_IRUSR) {
-				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_testfile(testfile, data, datalen);
-	if (res == -1)
-		return -1;
-
-	res = chmod(testfile, mode);
-	if (res == -1) {
-		PERROR("chmod");
-		return -1;
-	}
-
-	res = check_testfile_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);
-	}
-
-	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;
-
-	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_testfile(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;
-
-	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_link(void)
-{
-	const char *data = testdata;
-	int datalen = testdatalen;
-	int err = 0;
-	int res;
-
-	start_test("link");
-	res = create_testfile(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_testfile(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_testfile(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_rename_dir_loop(void)
-{
-#define PATH(p)		(snprintf(path, sizeof path, "%s/%s", testdir, p), path)
-#define PATH2(p)	(snprintf(path2, sizeof path2, "%s/%s", testdir, p), path2)
-
-	char path[1280], path2[1280];
-	int err = 0;
-	int res;
-
-	start_test("rename dir loop");
-
-	res = create_dir(testdir, testdir_files);
-	if (res == -1)
-		return -1;
-
-	res = mkdir(PATH("a"), 0755);
-	if (res == -1) {
-		PERROR("mkdir");
-		goto fail;
-	}
-
-	res = rename(PATH("a"), PATH2("a"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	errno = 0;
-	res = rename(PATH("a"), PATH2("a/b"));
-	if (res == 0 || errno != EINVAL) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = mkdir(PATH("a/b"), 0755);
-	if (res == -1) {
-		PERROR("mkdir");
-		goto fail;
-	}
-
-	res = mkdir(PATH("a/b/c"), 0755);
-	if (res == -1) {
-		PERROR("mkdir");
-		goto fail;
-	}
-
-	errno = 0;
-	res = rename(PATH("a"), PATH2("a/b/c"));
-	if (res == 0 || errno != EINVAL) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	errno = 0;
-	res = rename(PATH("a"), PATH2("a/b/c/a"));
-	if (res == 0 || errno != EINVAL) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	errno = 0;
-	res = rename(PATH("a/b/c"), PATH2("a"));
-	if (res == 0 || errno != ENOTEMPTY) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = open(PATH("a/foo"), O_CREAT, 0644);
-	if (res == -1) {
-		PERROR("open");
-		goto fail;
-	}
-	close(res);
-
-	res = rename(PATH("a/foo"), PATH2("a/bar"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = rename(PATH("a/bar"), PATH2("a/foo"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = rename(PATH("a/foo"), PATH2("a/b/bar"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = rename(PATH("a/b/bar"), PATH2("a/foo"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = rename(PATH("a/foo"), PATH2("a/b/c/bar"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = rename(PATH("a/b/c/bar"), PATH2("a/foo"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = open(PATH("a/bar"), O_CREAT, 0644);
-	if (res == -1) {
-		PERROR("open");
-		goto fail;
-	}
-	close(res);
-
-	res = rename(PATH("a/foo"), PATH2("a/bar"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	unlink(PATH("a/bar"));
-
-	res = rename(PATH("a/b"), PATH2("a/d"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = rename(PATH("a/d"), PATH2("a/b"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = mkdir(PATH("a/d"), 0755);
-	if (res == -1) {
-		PERROR("mkdir");
-		goto fail;
-	}
-
-	res = rename(PATH("a/b"), PATH2("a/d"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = rename(PATH("a/d"), PATH2("a/b"));
-	if (res == -1) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	res = mkdir(PATH("a/d"), 0755);
-	if (res == -1) {
-		PERROR("mkdir");
-		goto fail;
-	}
-
-	res = mkdir(PATH("a/d/e"), 0755);
-	if (res == -1) {
-		PERROR("mkdir");
-		goto fail;
-	}
-
-	errno = 0;
-	res = rename(PATH("a/b"), PATH2("a/d"));
-	if (res == 0 || (errno != ENOTEMPTY && errno != EEXIST)) {
-		PERROR("rename");
-		goto fail;
-	}
-
-	rmdir(PATH("a/d/e"));
-	rmdir(PATH("a/d"));
-
- 	rmdir(PATH("a/b/c"));
-	rmdir(PATH("a/b"));
-	rmdir(PATH("a"));
-
-	err += cleanup_dir(testdir, testdir_files, 0);
-	res = rmdir(testdir);
-	if (res == -1) {
-		PERROR("rmdir");
-		goto fail;
-	}
-	res = check_nonexist(testdir);
-	if (res == -1)
-		return -1;
-	if (err)
-		return -1;
-
-	success();
-	return 0;
-
-fail:
-	unlink(PATH("a/bar"));
-
-	rmdir(PATH("a/d/e"));
-	rmdir(PATH("a/d"));
- 
- 	rmdir(PATH("a/b/c"));
-	rmdir(PATH("a/b"));
-	rmdir(PATH("a"));
-
-	cleanup_dir(testdir, testdir_files, 1);
-	rmdir(testdir);
-
-	return -1;
-
-#undef PATH2
-#undef PATH
-}
-
-#ifndef __FreeBSD__
-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;
-}
-#endif
-
-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);
-	/* Some file systems (like btrfs) don't track link
-	   count for directories */
-	//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;
-}
-
-static int test_socket(void)
-{
-	struct sockaddr_un su;
-	int fd;
-	int res;
-	int err = 0;
-
-	start_test("socket");
-	if (strlen(testsock) + 1 > sizeof(su.sun_path)) {
-		fprintf(stderr, "Need to shorten mount point by %zu chars\n",
-			strlen(testsock) + 1 - sizeof(su.sun_path));
-		return -1;
-	}
-	unlink(testsock);
-	fd = socket(AF_UNIX, SOCK_STREAM, 0);
-	if (fd < 0) {
-		PERROR("socket");
-		return -1;
-	}
-	su.sun_family = AF_UNIX;
-	strncpy(su.sun_path, testsock, sizeof(su.sun_path) - 1);
-	su.sun_path[sizeof(su.sun_path) - 1] = '\0';
-	res = bind(fd, (struct sockaddr*)&su, sizeof(su));
-	if (res == -1) {
-		PERROR("bind");
-		return -1;
-	}
-
-	res = check_type(testsock, S_IFSOCK);
-	if (res == -1) {
-		close(fd);
-		return -1;
-	}
-	err += check_nlink(testsock, 1);
-	close(fd);
-	res = unlink(testsock);
-	if (res == -1) {
-		PERROR("unlink");
-		return -1;
-	}
-	res = check_nonexist(testsock);
-	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[])
-{
-	int err = 0;
-	int a;
-	int is_root;
-
-	umask(0);
-	if (argc < 2 || argc > 4) {
-		fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#] [-u]\n", argv[0]);
-		return 1;
-	}
-	basepath = argv[1];
-	basepath_r = basepath;
-	for (a = 2; a < argc; a++) {
-		char *endptr;
-		char *arg = argv[a];
-		if (arg[0] == ':') {
-			basepath_r = arg + 1;
-		} else {
-			if (arg[0] == '-') {
-				arg++;
-				if (arg[0] == 'u') {
-					unlinked_test = 1;
-					endptr = arg + 1;
-				} else {
-					skip_test = strtoul(arg, &endptr, 10);
-				}
-			} else {
-				select_test = strtoul(arg, &endptr, 10);
-			}
-			if (arg[0] == '\0' || *endptr != '\0') {
-				fprintf(stderr, "invalid option: '%s'\n", argv[a]);
-				return 1;
-			}
-		}
-	}
-	assert(strlen(basepath) < 512);
-	assert(strlen(basepath_r) < 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(testsock, "%s/testsock", basepath);
-
-	sprintf(testfile_r, "%s/testfile", basepath_r);
-	sprintf(testfile2_r, "%s/testfile2", basepath_r);
-	sprintf(testdir_r, "%s/testdir", basepath_r);
-	sprintf(testdir2_r, "%s/testdir2", basepath_r);
-	sprintf(subfile_r, "%s/subfile", testdir2_r);
-
-	is_root = (geteuid() == 0);
-
-	err += test_create();
-	err += test_create_unlink();
-	err += test_symlink();
-	err += test_link();
-	err += test_link2();
-#ifndef __FreeBSD__	
-	err += test_mknod();
-	err += test_mkfifo();
-#endif
-	err += test_mkdir();
-	err += test_rename_file();
-	err += test_rename_dir();
-	err += test_rename_dir_loop();
-	err += test_seekdir();
-	err += test_socket();
-	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);
-	err += test_copy_file_range();
-
-	unlink(testfile2);
-	unlink(testsock);
-	rmdir(testdir);
-	rmdir(testdir2);
-
-	if (err) {
-		fprintf(stderr, "%i tests failed\n", -err);
-		return 1;
-	}
-
-	return check_unlinked_testfiles();
-}
diff --git a/test/test_write_cache.c b/test/test_write_cache.c
deleted file mode 100644
index 88344dc..0000000
--- a/test/test_write_cache.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-
-#define FUSE_USE_VERSION 30
-
-#include <config.h>
-#include <fuse_lowlevel.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <pthread.h>
-
-#ifndef __linux__
-#include <limits.h>
-#else
-#include <linux/limits.h>
-#endif
-
-#define FILE_INO 2
-#define FILE_NAME "write_me"
-
-/* Command line parsing */
-struct options {
-    int writeback;
-    int data_size;
-} options = {
-    .writeback = 0,
-    .data_size = 4096,
-};
-
-#define OPTION(t, p)                           \
-    { t, offsetof(struct options, p), 1 }
-static const struct fuse_opt option_spec[] = {
-    OPTION("writeback_cache", writeback),
-    OPTION("--data-size=%d", data_size),
-    FUSE_OPT_END
-};
-static int got_write;
-
-static void tfs_init (void *userdata, struct fuse_conn_info *conn)
-{
-    (void) userdata;
-
-    if(options.writeback) {
-        assert(conn->capable & FUSE_CAP_WRITEBACK_CACHE);
-        conn->want |= FUSE_CAP_WRITEBACK_CACHE;
-    }
-}
-
-static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
-    stbuf->st_ino = ino;
-    if (ino == FUSE_ROOT_ID) {
-        stbuf->st_mode = S_IFDIR | 0755;
-        stbuf->st_nlink = 1;
-    }
-
-    else if (ino == FILE_INO) {
-        stbuf->st_mode = S_IFREG | 0222;
-        stbuf->st_nlink = 1;
-        stbuf->st_size = 0;
-    }
-
-    else
-        return -1;
-
-    return 0;
-}
-
-static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
-                       const char *name) {
-    struct fuse_entry_param e;
-    memset(&e, 0, sizeof(e));
-
-    if (parent != FUSE_ROOT_ID)
-        goto err_out;
-    else if (strcmp(name, FILE_NAME) == 0)
-        e.ino = FILE_INO;
-    else
-        goto err_out;
-
-    if (tfs_stat(e.ino, &e.attr) != 0)
-        goto err_out;
-    fuse_reply_entry(req, &e);
-    return;
-
-err_out:
-    fuse_reply_err(req, ENOENT);
-}
-
-static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
-                        struct fuse_file_info *fi) {
-    struct stat stbuf;
-
-    (void) fi;
-
-    memset(&stbuf, 0, sizeof(stbuf));
-    if (tfs_stat(ino, &stbuf) != 0)
-        fuse_reply_err(req, ENOENT);
-    else
-        fuse_reply_attr(req, &stbuf, 5);
-}
-
-static void tfs_open(fuse_req_t req, fuse_ino_t ino,
-                     struct fuse_file_info *fi) {
-    if (ino == FUSE_ROOT_ID)
-        fuse_reply_err(req, EISDIR);
-    else {
-        assert(ino == FILE_INO);
-        fuse_reply_open(req, fi);
-    }
-}
-
-static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
-                      size_t size, off_t off, struct fuse_file_info *fi) {
-    (void) fi; (void) buf; (void) off;
-    size_t expected;
-
-    assert(ino == FILE_INO);
-    expected = options.data_size;
-    if(options.writeback)
-        expected *= 2;
-
-    if(size != expected)
-        fprintf(stderr, "ERROR: Expected %zd bytes, got %zd\n!",
-                expected, size);
-    else
-        got_write = 1;
-    fuse_reply_write(req, size);
-}
-
-static struct fuse_lowlevel_ops tfs_oper = {
-    .init       = tfs_init,
-    .lookup	= tfs_lookup,
-    .getattr	= tfs_getattr,
-    .open	= tfs_open,
-    .write	= tfs_write,
-};
-
-static void* run_fs(void *data) {
-    struct fuse_session *se = (struct fuse_session*) data;
-    assert(fuse_session_loop(se) == 0);
-    return NULL;
-}
-
-static void test_fs(char *mountpoint) {
-    char fname[PATH_MAX];
-    char *buf;
-    size_t dsize = options.data_size;
-    int fd;
-
-    buf = malloc(dsize);
-    assert(buf != NULL);
-    assert((fd = open("/dev/urandom", O_RDONLY)) != -1);
-    assert(read(fd, buf, dsize) == dsize);
-    close(fd);
-
-    assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME,
-                     mountpoint) > 0);
-    fd = open(fname, O_WRONLY);
-    if (fd == -1) {
-        perror(fname);
-        assert(0);
-    }
-
-    assert(write(fd, buf, dsize) == dsize);
-    assert(write(fd, buf, dsize) == dsize);
-    free(buf);
-    close(fd);
-}
-
-int main(int argc, char *argv[]) {
-    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
-    struct fuse_session *se;
-    struct fuse_cmdline_opts fuse_opts;
-    pthread_t fs_thread;
-
-    assert(fuse_opt_parse(&args, &options, option_spec, NULL) == 0);
-    assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
-#ifndef __FreeBSD__    
-    assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
-#endif
-    se = fuse_session_new(&args, &tfs_oper,
-                          sizeof(tfs_oper), NULL);
-    fuse_opt_free_args(&args);
-    assert (se != NULL);
-    assert(fuse_set_signal_handlers(se) == 0);
-    assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
-
-    /* Start file-system thread */
-    assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
-
-    /* Write test data */
-    test_fs(fuse_opts.mountpoint);
-    free(fuse_opts.mountpoint);
-
-    /* Stop file system */
-    fuse_session_exit(se);
-    fuse_session_unmount(se);
-    assert(pthread_join(fs_thread, NULL) == 0);
-
-    assert(got_write == 1);
-    fuse_remove_signal_handlers(se);
-    fuse_session_destroy(se);
-
-    printf("Test completed successfully.\n");
-    return 0;
-}
-
-
-/**
- * Local Variables:
- * mode: c
- * indent-tabs-mode: nil
- * c-basic-offset: 4
- * End:
- */
diff --git a/test/travis-build.sh b/test/travis-build.sh
deleted file mode 100755
index 267b6e7..0000000
--- a/test/travis-build.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/bash
-
-set -e
-
-TEST_CMD="python3 -m pytest --maxfail=99 test/"
-
-# Make sure binaries can be accessed when invoked by root.
-umask 0022
-
-# There are tests that run as root but without CAP_DAC_OVERRIDE. To allow these
-# to launch built binaries, the directory tree must be accessible to the root
-# user. Since the source directory isn't necessarily accessible to root, we
-# build and run tests in a temporary directory that we can set up to be world
-# readable/executable.
-SOURCE_DIR="$(readlink -f .)"
-TEST_DIR="$(mktemp -dt libfuse-build-XXXXXX)"
-chmod 0755 "${TEST_DIR}"
-cd "${TEST_DIR}"
-echo "Running in ${TEST_DIR}"
-
-cp -v "${SOURCE_DIR}/test/lsan_suppress.txt" .
-export LSAN_OPTIONS="suppressions=$(pwd)/lsan_suppress.txt"
-export ASAN_OPTIONS="detect_leaks=1"
-export CC
-
-# Standard build
-for CC in gcc gcc-7 gcc-10 clang; do
-    echo "=== Building with ${CC} ==="
-    mkdir build-${CC}; cd build-${CC}
-    if [ "${CC}" == "clang" ]; then
-        export CXX="clang++"
-    fi
-    if [ ${CC} == 'gcc-7' ]; then
-        build_opts='-D b_lundef=false'
-    else
-        build_opts=''
-    fi
-    if [ ${CC} == 'gcc-10' ]; then
-        build_opts='-Dc_args=-flto=auto'
-    else
-        build_opts=''
-    fi
-    meson -D werror=true ${build_opts} "${SOURCE_DIR}" || (cat meson-logs/meson-log.txt; false)
-    ninja
-
-    sudo chown root:root util/fusermount3
-    sudo chmod 4755 util/fusermount3
-    TEST_WITH_VALGRIND=true ${TEST_CMD}
-    cd ..
-done
-(cd build-$CC; sudo ninja install)
-
-# Sanitized build
-CC=clang
-CXX=clang++
-for san in undefined address; do
-    echo "=== Building with clang and ${san} sanitizer ==="
-    mkdir build-${san}; cd build-${san}
-    # b_lundef=false is required to work around clang
-    # bug, cf. https://groups.google.com/forum/#!topic/mesonbuild/tgEdAXIIdC4
-    meson -D b_sanitize=${san} -D b_lundef=false -D werror=true "${SOURCE_DIR}" \
-           || (cat meson-logs/meson-log.txt; false)
-    ninja
-
-    # Test as root and regular user
-    sudo ${TEST_CMD}
-    sudo chown root:root util/fusermount3
-    sudo chmod 4755 util/fusermount3
-    # Cleanup temporary files (since they're now owned by root)
-    sudo rm -rf test/.pytest_cache/ test/__pycache__
-
-    ${TEST_CMD}
-    cd ..
-done
-
-# Documentation.
-(cd "${SOURCE_DIR}"; doxygen doc/Doxyfile)
-
-# Clean up.
-rm -rf "${TEST_DIR}"
diff --git a/test/travis-install.sh b/test/travis-install.sh
deleted file mode 100755
index 65360b4..0000000
--- a/test/travis-install.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-set -e
-
-sudo python3 -m pip install --upgrade pip
-# Meson 0.45 requires Python 3.5 or newer
-sudo python3 -m pip install pytest meson==0.44
-valgrind --version
-ninja --version
-meson --version
diff --git a/test/util.py b/test/util.py
deleted file mode 100644
index 8bef265..0000000
--- a/test/util.py
+++ /dev/null
@@ -1,171 +0,0 @@
-#!/usr/bin/env python3
-import subprocess
-import pytest
-import os
-import stat
-import time
-from os.path import join as pjoin
-import sys
-import re
-import itertools
-
-basename = pjoin(os.path.dirname(__file__), '..')
-
-def test_printcap():
-    cmdline = base_cmdline + [ pjoin(basename, 'example', 'printcap') ]
-    proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
-                            universal_newlines=True)
-    (stdout, _) = proc.communicate(30)
-    assert proc.returncode == 0
-
-    proto = None
-    caps = set()
-    for line in stdout.split('\n'):
-        if line.startswith('\t'):
-            caps.add(line.strip())
-            continue
-
-        hit = re.match(r'Protocol version: (\d+)\.(\d+)$', line) 
-        if hit:
-            proto = (int(hit.group(1)), int(hit.group(2)))
-
-    return (proto, caps)
-
-
-def wait_for_mount(mount_process, mnt_dir,
-                   test_fn=os.path.ismount):
-    elapsed = 0
-    while elapsed < 30:
-        if test_fn(mnt_dir):
-            return True
-        if mount_process.poll() is not None:
-            pytest.fail('file system process terminated prematurely')
-        time.sleep(0.1)
-        elapsed += 0.1
-    pytest.fail("mountpoint failed to come up")
-
-def cleanup(mount_process, mnt_dir):
-    # Don't bother trying Valgrind if things already went wrong
-
-    if 'bsd' in sys.platform or 'dragonfly' in sys.platform:
-        cmd = [ 'umount', '-f', mnt_dir ]
-    else:
-        cmd = [pjoin(basename, 'util', 'fusermount3'),
-                         '-z', '-u', mnt_dir]
-    subprocess.call(cmd, stdout=subprocess.DEVNULL,
-                    stderr=subprocess.STDOUT)
-    mount_process.terminate()
-    try:
-        mount_process.wait(1)
-    except subprocess.TimeoutExpired:
-        mount_process.kill()
-
-def umount(mount_process, mnt_dir):
-
-    if 'bsd' in sys.platform or 'dragonfly' in sys.platform:
-        cmdline = [ 'umount', mnt_dir ]
-    else:
-        # fusermount3 will be setuid root, so we can only trace it with
-        # valgrind if we're root
-        if os.getuid() == 0:
-            cmdline = base_cmdline
-        else:
-            cmdline = []
-        cmdline = cmdline + [ pjoin(basename, 'util', 'fusermount3'),
-                              '-z', '-u', mnt_dir ]
-
-    subprocess.check_call(cmdline)
-    assert not os.path.ismount(mnt_dir)
-
-    # Give mount process a little while to terminate. Popen.wait(timeout)
-    # was only added in 3.3...
-    elapsed = 0
-    while elapsed < 30:
-        code = mount_process.poll()
-        if code is not None:
-            if code == 0:
-                return
-            pytest.fail('file system process terminated with code %s' % (code,))
-        time.sleep(0.1)
-        elapsed += 0.1
-    pytest.fail('mount process did not terminate')
-
-
-def safe_sleep(secs):
-    '''Like time.sleep(), but sleep for at least *secs*
-
-    `time.sleep` may sleep less than the given period if a signal is
-    received. This function ensures that we sleep for at least the
-    desired time.
-    '''
-
-    now = time.time()
-    end = now + secs
-    while now < end:
-        time.sleep(end - now)
-        now = time.time()
-
-def fuse_test_marker():
-    '''Return a pytest.marker that indicates FUSE availability
-
-    If system/user/environment does not support FUSE, return
-    a `pytest.mark.skip` object with more details. If FUSE is
-    supported, return `pytest.mark.uses_fuse()`.
-    '''
-
-    skip = lambda x: pytest.mark.skip(reason=x)
-
-    if 'bsd' in sys.platform or 'dragonfly' in sys.platform:
-        return pytest.mark.uses_fuse()
-
-    with subprocess.Popen(['which', 'fusermount3'], stdout=subprocess.PIPE,
-                          universal_newlines=True) as which:
-        fusermount_path = which.communicate()[0].strip()
-
-    if not fusermount_path or which.returncode != 0:
-        return skip("Can't find fusermount executable")
-
-    if not os.path.exists('/dev/fuse'):
-        return skip("FUSE kernel module does not seem to be loaded")
-
-    if os.getuid() == 0:
-        return pytest.mark.uses_fuse()
-
-    mode = os.stat(fusermount_path).st_mode
-    if mode & stat.S_ISUID == 0:
-        return skip('fusermount executable not setuid, and we are not root.')
-
-    try:
-        fd = os.open('/dev/fuse', os.O_RDWR)
-    except OSError as exc:
-        return skip('Unable to open /dev/fuse: %s' % exc.strerror)
-    else:
-        os.close(fd)
-
-    return pytest.mark.uses_fuse()
-
-def powerset(iterable):
-  s = list(iterable)
-  return itertools.chain.from_iterable(
-      itertools.combinations(s, r) for r in range(len(s)+1))
-
-
-# Use valgrind if requested
-if os.environ.get('TEST_WITH_VALGRIND', 'no').lower().strip() \
-   not in ('no', 'false', '0'):
-    base_cmdline = [ 'valgrind', '-q', '--' ]
-else:
-    base_cmdline = []
-
-# Try to use local fusermount3
-os.environ['PATH'] = '%s:%s' % (pjoin(basename, 'util'), os.environ['PATH'])
-# Put example binaries on PATH
-os.environ['PATH'] = '%s:%s' % (pjoin(basename, 'example'), os.environ['PATH'])
-
-try:
-    (fuse_proto, fuse_caps) = test_printcap()
-except:
-    # Rely on test to raise error
-    fuse_proto = (0,0)
-    fuse_caps = set()
-
diff --git a/test/wrong_command.c b/test/wrong_command.c
deleted file mode 100644
index 8b563c9..0000000
--- a/test/wrong_command.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <stdio.h>
-
-int main(void) {
-#ifdef MESON_IS_SUBPROJECT
-	fprintf(stderr, "libfuse tests were skipped because it's a meson subproject.\n"
-			"If you wish to run them try:\n"
-			"'cd <srcdir>/subprojects/libfuse && meson . build && cd build && python3 -m pytest test/' instead");
-	return 77; /* report as a skipped test */
-#else
-	fprintf(stderr, "\x1B[31m\e[1m"
-		"This is not the command you are looking for.\n"
-		"You probably want to run 'python3 -m pytest test/' instead"
-		"\e[0m\n");
-	return 1;
-#endif
-}
diff --git a/tsan_suppressions.txt b/tsan_suppressions.txt
deleted file mode 100644
index 3470d15..0000000
--- a/tsan_suppressions.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Use with
-# TSAN_OPTIONS="suppressions=tsan_suppressions.txt" ./myprogram
-
-# False positive
-race:pthread_setcancelstate
diff --git a/util/.gitignore b/util/.gitignore
deleted file mode 100644
index bfa05af..0000000
--- a/util/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-fusermount3
-mount.fuse3
-mount_util.c
\ No newline at end of file
diff --git a/util/fuse.conf b/util/fuse.conf
deleted file mode 100644
index 3c9b6ac..0000000
--- a/util/fuse.conf
+++ /dev/null
@@ -1,17 +0,0 @@
-# The file /etc/fuse.conf allows for the following parameters:
-#
-# user_allow_other - Using the allow_other mount option works fine as root, in
-# order to have it work as user you need user_allow_other in /etc/fuse.conf as
-# well. (This option allows users to use the allow_other option.) You need
-# allow_other if you want users other than the owner to access a mounted fuse.
-# This option must appear on a line by itself. There is no value, just the
-# presence of the option.
-
-#user_allow_other
-
-
-# mount_max = n - this option sets the maximum number of mounts.
-# Currently (2014) it must be typed exactly as shown
-# (with a single space before and after the equals sign).
-
-#mount_max = 1000
diff --git a/util/fusermount.c b/util/fusermount.c
deleted file mode 100644
index ed62ea9..0000000
--- a/util/fusermount.c
+++ /dev/null
@@ -1,1421 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-/* This program does the mounting and unmounting of FUSE filesystems */
-
-#define _GNU_SOURCE /* for clone */
-#include "config.h"
-#include "mount_util.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <paths.h>
-#include <mntent.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
-#include <sys/fsuid.h>
-#include <sys/socket.h>
-#include <sys/utsname.h>
-#include <sched.h>
-#include <stdbool.h>
-#include <sys/vfs.h>
-
-#define FUSE_COMMFD_ENV		"_FUSE_COMMFD"
-
-#define FUSE_DEV "/dev/fuse"
-
-#ifndef MS_DIRSYNC
-#define MS_DIRSYNC 128
-#endif
-#ifndef MS_REC
-#define MS_REC 16384
-#endif
-#ifndef MS_PRIVATE
-#define MS_PRIVATE (1<<18)
-#endif
-
-#ifndef UMOUNT_DETACH
-#define UMOUNT_DETACH	0x00000002	/* Just detach from the tree */
-#endif
-#ifndef UMOUNT_NOFOLLOW
-#define UMOUNT_NOFOLLOW	0x00000008	/* Don't follow symlink on umount */
-#endif
-#ifndef UMOUNT_UNUSED
-#define UMOUNT_UNUSED	0x80000000	/* Flag guaranteed to be unused */
-#endif
-
-static const char *progname;
-
-static int user_allow_other = 0;
-static int mount_max = 1000;
-
-static int auto_unmount = 0;
-
-static const char *get_user_name(void)
-{
-	struct passwd *pw = getpwuid(getuid());
-	if (pw != NULL && pw->pw_name != NULL)
-		return pw->pw_name;
-	else {
-		fprintf(stderr, "%s: could not determine username\n", progname);
-		return NULL;
-	}
-}
-
-static uid_t oldfsuid;
-static gid_t oldfsgid;
-
-static void drop_privs(void)
-{
-	if (getuid() != 0) {
-		oldfsuid = setfsuid(getuid());
-		oldfsgid = setfsgid(getgid());
-	}
-}
-
-static void restore_privs(void)
-{
-	if (getuid() != 0) {
-		setfsuid(oldfsuid);
-		setfsgid(oldfsgid);
-	}
-}
-
-#ifndef IGNORE_MTAB
-/*
- * Make sure that /etc/mtab is checked and updated atomically
- */
-static int lock_umount(void)
-{
-	const char *mtab_lock = _PATH_MOUNTED ".fuselock";
-	int mtablock;
-	int res;
-	struct stat mtab_stat;
-
-	/* /etc/mtab could be a symlink to /proc/mounts */
-	if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
-		return -1;
-
-	mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
-	if (mtablock == -1) {
-		fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
-			progname, strerror(errno));
-		return -1;
-	}
-	res = lockf(mtablock, F_LOCK, 0);
-	if (res < 0) {
-		fprintf(stderr, "%s: error getting lock: %s\n", progname,
-			strerror(errno));
-		close(mtablock);
-		return -1;
-	}
-
-	return mtablock;
-}
-
-static void unlock_umount(int mtablock)
-{
-	if (mtablock >= 0) {
-		int res;
-
-		res = lockf(mtablock, F_ULOCK, 0);
-		if (res < 0) {
-			fprintf(stderr, "%s: error releasing lock: %s\n",
-				progname, strerror(errno));
-		}
-		close(mtablock);
-	}
-}
-
-static int add_mount(const char *source, const char *mnt, const char *type,
-		     const char *opts)
-{
-	return fuse_mnt_add_mount(progname, source, mnt, type, opts);
-}
-
-static int may_unmount(const char *mnt, int quiet)
-{
-	struct mntent *entp;
-	FILE *fp;
-	const char *user = NULL;
-	char uidstr[32];
-	unsigned uidlen = 0;
-	int found;
-	const char *mtab = _PATH_MOUNTED;
-
-	user = get_user_name();
-	if (user == NULL)
-		return -1;
-
-	fp = setmntent(mtab, "r");
-	if (fp == NULL) {
-		fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
-			strerror(errno));
-		return -1;
-	}
-
-	uidlen = sprintf(uidstr, "%u", getuid());
-
-	found = 0;
-	while ((entp = getmntent(fp)) != NULL) {
-		if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
-		    (strcmp(entp->mnt_type, "fuse") == 0 ||
-		     strcmp(entp->mnt_type, "fuseblk") == 0 ||
-		     strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
-		     strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
-			char *p = strstr(entp->mnt_opts, "user=");
-			if (p &&
-			    (p == entp->mnt_opts || *(p-1) == ',') &&
-			    strcmp(p + 5, user) == 0) {
-				found = 1;
-				break;
-			}
-			/* /etc/mtab is a link pointing to
-			   /proc/mounts: */
-			else if ((p =
-				  strstr(entp->mnt_opts, "user_id=")) &&
-				 (p == entp->mnt_opts ||
-				  *(p-1) == ',') &&
-				 strncmp(p + 8, uidstr, uidlen) == 0 &&
-				 (*(p+8+uidlen) == ',' ||
-				  *(p+8+uidlen) == '\0')) {
-				found = 1;
-				break;
-			}
-		}
-	}
-	endmntent(fp);
-
-	if (!found) {
-		if (!quiet)
-			fprintf(stderr,
-				"%s: entry for %s not found in %s\n",
-				progname, mnt, mtab);
-		return -1;
-	}
-
-	return 0;
-}
-#endif
-
-/*
- * Check whether the file specified in "fusermount3 -u" is really a
- * mountpoint and not a symlink.  This is necessary otherwise the user
- * could move the mountpoint away and replace it with a symlink
- * pointing to an arbitrary mount, thereby tricking fusermount3 into
- * unmounting that (umount(2) will follow symlinks).
- *
- * This is the child process running in a separate mount namespace, so
- * we don't mess with the global namespace and if the process is
- * killed for any reason, mounts are automatically cleaned up.
- *
- * First make sure nothing is propagated back into the parent
- * namespace by marking all mounts "private".
- *
- * Then bind mount parent onto a stable base where the user can't move
- * it around.
- *
- * Finally check /proc/mounts for an entry matching the requested
- * mountpoint.  If it's found then we are OK, and the user can't move
- * it around within the parent directory as rename() will return
- * EBUSY.  Be careful to ignore any mounts that existed before the
- * bind.
- */
-static int check_is_mount_child(void *p)
-{
-	const char **a = p;
-	const char *last = a[0];
-	const char *mnt = a[1];
-	const char *type = a[2];
-	int res;
-	const char *procmounts = "/proc/mounts";
-	int found;
-	FILE *fp;
-	struct mntent *entp;
-	int count;
-
-	res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
-	if (res == -1) {
-		fprintf(stderr, "%s: failed to mark mounts private: %s\n",
-			progname, strerror(errno));
-		return 1;
-	}
-
-	fp = setmntent(procmounts, "r");
-	if (fp == NULL) {
-		fprintf(stderr, "%s: failed to open %s: %s\n", progname,
-			procmounts, strerror(errno));
-		return 1;
-	}
-
-	count = 0;
-	while (getmntent(fp) != NULL)
-		count++;
-	endmntent(fp);
-
-	fp = setmntent(procmounts, "r");
-	if (fp == NULL) {
-		fprintf(stderr, "%s: failed to open %s: %s\n", progname,
-			procmounts, strerror(errno));
-		return 1;
-	}
-
-	res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
-	if (res == -1) {
-		fprintf(stderr, "%s: failed to bind parent to /: %s\n",
-			progname, strerror(errno));
-		return 1;
-	}
-
-	found = 0;
-	while ((entp = getmntent(fp)) != NULL) {
-		if (count > 0) {
-			count--;
-			continue;
-		}
-		if (entp->mnt_dir[0] == '/' &&
-		    strcmp(entp->mnt_dir + 1, last) == 0 &&
-		    (!type || strcmp(entp->mnt_type, type) == 0)) {
-			found = 1;
-			break;
-		}
-	}
-	endmntent(fp);
-
-	if (!found) {
-		fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
-		return 1;
-	}
-
-	return 0;
-}
-
-static pid_t clone_newns(void *a)
-{
-	char buf[131072];
-	char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
-
-#ifdef __ia64__
-	extern int __clone2(int (*fn)(void *),
-			    void *child_stack_base, size_t stack_size,
-			    int flags, void *arg, pid_t *ptid,
-			    void *tls, pid_t *ctid);
-
-	return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
-			CLONE_NEWNS, a, NULL, NULL, NULL);
-#else
-	return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
-#endif
-}
-
-static int check_is_mount(const char *last, const char *mnt, const char *type)
-{
-	pid_t pid, p;
-	int status;
-	const char *a[3] = { last, mnt, type };
-
-	pid = clone_newns((void *) a);
-	if (pid == (pid_t) -1) {
-		fprintf(stderr, "%s: failed to clone namespace: %s\n",
-			progname, strerror(errno));
-		return -1;
-	}
-	p = waitpid(pid, &status, __WCLONE);
-	if (p == (pid_t) -1) {
-		fprintf(stderr, "%s: waitpid failed: %s\n",
-			progname, strerror(errno));
-		return -1;
-	}
-	if (!WIFEXITED(status)) {
-		fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
-			progname, status);
-		return -1;
-	}
-	if (WEXITSTATUS(status) != 0)
-		return -1;
-
-	return 0;
-}
-
-static int chdir_to_parent(char *copy, const char **lastp)
-{
-	char *tmp;
-	const char *parent;
-	char buf[65536];
-	int res;
-
-	tmp = strrchr(copy, '/');
-	if (tmp == NULL || tmp[1] == '\0') {
-		fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
-			progname, copy);
-		return -1;
-	}
-	if (tmp != copy) {
-		*tmp = '\0';
-		parent = copy;
-		*lastp = tmp + 1;
-	} else if (tmp[1] != '\0') {
-		*lastp = tmp + 1;
-		parent = "/";
-	} else {
-		*lastp = ".";
-		parent = "/";
-	}
-
-	res = chdir(parent);
-	if (res == -1) {
-		fprintf(stderr, "%s: failed to chdir to %s: %s\n",
-			progname, parent, strerror(errno));
-		return -1;
-	}
-
-	if (getcwd(buf, sizeof(buf)) == NULL) {
-		fprintf(stderr, "%s: failed to obtain current directory: %s\n",
-			progname, strerror(errno));
-		return -1;
-	}
-	if (strcmp(buf, parent) != 0) {
-		fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
-			parent, buf);
-		return -1;
-
-	}
-
-	return 0;
-}
-
-#ifndef IGNORE_MTAB
-static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
-{
-	int res;
-	char *copy;
-	const char *last;
-	int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
-
-	if (getuid() != 0) {
-		res = may_unmount(mnt, quiet);
-		if (res == -1)
-			return -1;
-	}
-
-	copy = strdup(mnt);
-	if (copy == NULL) {
-		fprintf(stderr, "%s: failed to allocate memory\n", progname);
-		return -1;
-	}
-
-	drop_privs();
-	res = chdir_to_parent(copy, &last);
-	restore_privs();
-	if (res == -1)
-		goto out;
-
-	res = umount2(last, umount_flags);
-	if (res == -1 && !quiet) {
-		fprintf(stderr, "%s: failed to unmount %s: %s\n",
-			progname, mnt, strerror(errno));
-	}
-
-out:
-	free(copy);
-	if (res == -1)
-		return -1;
-
-	res = chdir("/");
-	if (res == -1) {
-		fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
-		return -1;
-	}
-
-	return fuse_mnt_remove_mount(progname, mnt);
-}
-
-static int unmount_fuse(const char *mnt, int quiet, int lazy)
-{
-	int res;
-	int mtablock = lock_umount();
-
-	res = unmount_fuse_locked(mnt, quiet, lazy);
-	unlock_umount(mtablock);
-
-	return res;
-}
-
-static int count_fuse_fs(void)
-{
-	struct mntent *entp;
-	int count = 0;
-	const char *mtab = _PATH_MOUNTED;
-	FILE *fp = setmntent(mtab, "r");
-	if (fp == NULL) {
-		fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
-			strerror(errno));
-		return -1;
-	}
-	while ((entp = getmntent(fp)) != NULL) {
-		if (strcmp(entp->mnt_type, "fuse") == 0 ||
-		    strncmp(entp->mnt_type, "fuse.", 5) == 0)
-			count ++;
-	}
-	endmntent(fp);
-	return count;
-}
-
-
-#else /* IGNORE_MTAB */
-static int count_fuse_fs(void)
-{
-	return 0;
-}
-
-static int add_mount(const char *source, const char *mnt, const char *type,
-		     const char *opts)
-{
-	(void) source;
-	(void) mnt;
-	(void) type;
-	(void) opts;
-	return 0;
-}
-
-static int unmount_fuse(const char *mnt, int quiet, int lazy)
-{
-	(void) quiet;
-	return fuse_mnt_umount(progname, mnt, mnt, lazy);
-}
-#endif /* IGNORE_MTAB */
-
-static void strip_line(char *line)
-{
-	char *s = strchr(line, '#');
-	if (s != NULL)
-		s[0] = '\0';
-	for (s = line + strlen(line) - 1;
-	     s >= line && isspace((unsigned char) *s); s--);
-	s[1] = '\0';
-	for (s = line; isspace((unsigned char) *s); s++);
-	if (s != line)
-		memmove(line, s, strlen(s)+1);
-}
-
-static void parse_line(char *line, int linenum)
-{
-	int tmp;
-	if (strcmp(line, "user_allow_other") == 0)
-		user_allow_other = 1;
-	else if (sscanf(line, "mount_max = %i", &tmp) == 1)
-		mount_max = tmp;
-	else if(line[0])
-		fprintf(stderr,
-			"%s: unknown parameter in %s at line %i: '%s'\n",
-			progname, FUSE_CONF, linenum, line);
-}
-
-static void read_conf(void)
-{
-	FILE *fp = fopen(FUSE_CONF, "r");
-	if (fp != NULL) {
-		int linenum = 1;
-		char line[256];
-		int isnewline = 1;
-		while (fgets(line, sizeof(line), fp) != NULL) {
-			if (isnewline) {
-				if (line[strlen(line)-1] == '\n') {
-					strip_line(line);
-					parse_line(line, linenum);
-				} else {
-					isnewline = 0;
-				}
-			} else if(line[strlen(line)-1] == '\n') {
-				fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
-
-				isnewline = 1;
-			}
-			if (isnewline)
-				linenum ++;
-		}
-		if (!isnewline) {
-			fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
-
-		}
-		if (ferror(fp)) {
-			fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
-			exit(1);
-		}
-		fclose(fp);
-	} else if (errno != ENOENT) {
-		bool fatal = (errno != EACCES && errno != ELOOP &&
-			      errno != ENAMETOOLONG && errno != ENOTDIR &&
-			      errno != EOVERFLOW);
-		fprintf(stderr, "%s: failed to open %s: %s\n",
-			progname, FUSE_CONF, strerror(errno));
-		if (fatal)
-			exit(1);
-	}
-}
-
-static int begins_with(const char *s, const char *beg)
-{
-	if (strncmp(s, beg, strlen(beg)) == 0)
-		return 1;
-	else
-		return 0;
-}
-
-struct mount_flags {
-	const char *opt;
-	unsigned long flag;
-	int on;
-	int safe;
-};
-
-static struct mount_flags mount_flags[] = {
-	{"rw",	    MS_RDONLY,	    0, 1},
-	{"ro",	    MS_RDONLY,	    1, 1},
-	{"suid",    MS_NOSUID,	    0, 0},
-	{"nosuid",  MS_NOSUID,	    1, 1},
-	{"dev",	    MS_NODEV,	    0, 0},
-	{"nodev",   MS_NODEV,	    1, 1},
-	{"exec",    MS_NOEXEC,	    0, 1},
-	{"noexec",  MS_NOEXEC,	    1, 1},
-	{"async",   MS_SYNCHRONOUS, 0, 1},
-	{"sync",    MS_SYNCHRONOUS, 1, 1},
-	{"atime",   MS_NOATIME,	    0, 1},
-	{"noatime", MS_NOATIME,	    1, 1},
-	{"dirsync", MS_DIRSYNC,	    1, 1},
-	{NULL,	    0,		    0, 0}
-};
-
-static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
-{
-	int i;
-
-	for (i = 0; mount_flags[i].opt != NULL; i++) {
-		const char *opt = mount_flags[i].opt;
-		if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
-			*on = mount_flags[i].on;
-			*flag = mount_flags[i].flag;
-			if (!mount_flags[i].safe && getuid() != 0) {
-				*flag = 0;
-				fprintf(stderr,
-					"%s: unsafe option %s ignored\n",
-					progname, opt);
-			}
-			return 1;
-		}
-	}
-	return 0;
-}
-
-static int add_option(char **optsp, const char *opt, unsigned expand)
-{
-	char *newopts;
-	if (*optsp == NULL)
-		newopts = strdup(opt);
-	else {
-		unsigned oldsize = strlen(*optsp);
-		unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
-		newopts = (char *) realloc(*optsp, newsize);
-		if (newopts)
-			sprintf(newopts + oldsize, ",%s", opt);
-	}
-	if (newopts == NULL) {
-		fprintf(stderr, "%s: failed to allocate memory\n", progname);
-		return -1;
-	}
-	*optsp = newopts;
-	return 0;
-}
-
-static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
-{
-	int i;
-	int l;
-
-	if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
-		return -1;
-
-	for (i = 0; mount_flags[i].opt != NULL; i++) {
-		if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
-		    add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
-			return -1;
-	}
-
-	if (add_option(mnt_optsp, opts, 0) == -1)
-		return -1;
-	/* remove comma from end of opts*/
-	l = strlen(*mnt_optsp);
-	if ((*mnt_optsp)[l-1] == ',')
-		(*mnt_optsp)[l-1] = '\0';
-	if (getuid() != 0) {
-		const char *user = get_user_name();
-		if (user == NULL)
-			return -1;
-
-		if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
-			return -1;
-		strcat(*mnt_optsp, user);
-	}
-	return 0;
-}
-
-static int opt_eq(const char *s, unsigned len, const char *opt)
-{
-	if(strlen(opt) == len && strncmp(s, opt, len) == 0)
-		return 1;
-	else
-		return 0;
-}
-
-static int get_string_opt(const char *s, unsigned len, const char *opt,
-			  char **val)
-{
-	int i;
-	unsigned opt_len = strlen(opt);
-	char *d;
-
-	if (*val)
-		free(*val);
-	*val = (char *) malloc(len - opt_len + 1);
-	if (!*val) {
-		fprintf(stderr, "%s: failed to allocate memory\n", progname);
-		return 0;
-	}
-
-	d = *val;
-	s += opt_len;
-	len -= opt_len;
-	for (i = 0; i < len; i++) {
-		if (s[i] == '\\' && i + 1 < len)
-			i++;
-		*d++ = s[i];
-	}
-	*d = '\0';
-	return 1;
-}
-
-/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters.
- * This can be dangerous if it e.g. truncates the option "group_id=1000" to
- * "group_id=1".
- * This wrapper detects this case and bails out with an error.
- */
-static int mount_notrunc(const char *source, const char *target,
-			 const char *filesystemtype, unsigned long mountflags,
-			 const char *data) {
-	if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
-		fprintf(stderr, "%s: mount options too long\n", progname);
-		errno = EINVAL;
-		return -1;
-	}
-	return mount(source, target, filesystemtype, mountflags, data);
-}
-
-
-static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
-		    int fd, const char *opts, const char *dev, char **sourcep,
-		    char **mnt_optsp)
-{
-	int res;
-	int flags = MS_NOSUID | MS_NODEV;
-	char *optbuf;
-	char *mnt_opts = NULL;
-	const char *s;
-	char *d;
-	char *fsname = NULL;
-	char *subtype = NULL;
-	char *source = NULL;
-	char *type = NULL;
-	int blkdev = 0;
-
-	optbuf = (char *) malloc(strlen(opts) + 128);
-	if (!optbuf) {
-		fprintf(stderr, "%s: failed to allocate memory\n", progname);
-		return -1;
-	}
-
-	for (s = opts, d = optbuf; *s;) {
-		unsigned len;
-		const char *fsname_str = "fsname=";
-		const char *subtype_str = "subtype=";
-		bool escape_ok = begins_with(s, fsname_str) ||
-				 begins_with(s, subtype_str);
-		for (len = 0; s[len]; len++) {
-			if (escape_ok && s[len] == '\\' && s[len + 1])
-				len++;
-			else if (s[len] == ',')
-				break;
-		}
-		if (begins_with(s, fsname_str)) {
-			if (!get_string_opt(s, len, fsname_str, &fsname))
-				goto err;
-		} else if (begins_with(s, subtype_str)) {
-			if (!get_string_opt(s, len, subtype_str, &subtype))
-				goto err;
-		} else if (opt_eq(s, len, "blkdev")) {
-			if (getuid() != 0) {
-				fprintf(stderr,
-					"%s: option blkdev is privileged\n",
-					progname);
-				goto err;
-			}
-			blkdev = 1;
-		} else if (opt_eq(s, len, "auto_unmount")) {
-			auto_unmount = 1;
-		} else if (!opt_eq(s, len, "nonempty") &&
-			   !begins_with(s, "fd=") &&
-			   !begins_with(s, "rootmode=") &&
-			   !begins_with(s, "user_id=") &&
-			   !begins_with(s, "group_id=")) {
-			int on;
-			int flag;
-			int skip_option = 0;
-			if (opt_eq(s, len, "large_read")) {
-				struct utsname utsname;
-				unsigned kmaj, kmin;
-				res = uname(&utsname);
-				if (res == 0 &&
-				    sscanf(utsname.release, "%u.%u",
-					   &kmaj, &kmin) == 2 &&
-				    (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
-					fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
-					skip_option = 1;
-				}
-			}
-			if (getuid() != 0 && !user_allow_other &&
-			    (opt_eq(s, len, "allow_other") ||
-			     opt_eq(s, len, "allow_root"))) {
-				fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
-				goto err;
-			}
-			if (!skip_option) {
-				if (find_mount_flag(s, len, &on, &flag)) {
-					if (on)
-						flags |= flag;
-					else
-						flags  &= ~flag;
-				} else if (opt_eq(s, len, "default_permissions") ||
-					   opt_eq(s, len, "allow_other") ||
-					   begins_with(s, "max_read=") ||
-					   begins_with(s, "blksize=")) {
-					memcpy(d, s, len);
-					d += len;
-					*d++ = ',';
-				} else {
-					fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s);
-					exit(1);
-				}
-			}
-		}
-		s += len;
-		if (*s)
-			s++;
-	}
-	*d = '\0';
-	res = get_mnt_opts(flags, optbuf, &mnt_opts);
-	if (res == -1)
-		goto err;
-
-	sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
-		fd, rootmode, getuid(), getgid());
-
-	source = malloc((fsname ? strlen(fsname) : 0) +
-			(subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
-
-	type = malloc((subtype ? strlen(subtype) : 0) + 32);
-	if (!type || !source) {
-		fprintf(stderr, "%s: failed to allocate memory\n", progname);
-		goto err;
-	}
-
-	if (subtype)
-		sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
-	else
-		strcpy(type, blkdev ? "fuseblk" : "fuse");
-
-	if (fsname)
-		strcpy(source, fsname);
-	else
-		strcpy(source, subtype ? subtype : dev);
-
-	res = mount_notrunc(source, mnt, type, flags, optbuf);
-	if (res == -1 && errno == ENODEV && subtype) {
-		/* Probably missing subtype support */
-		strcpy(type, blkdev ? "fuseblk" : "fuse");
-		if (fsname) {
-			if (!blkdev)
-				sprintf(source, "%s#%s", subtype, fsname);
-		} else {
-			strcpy(source, type);
-		}
-
-		res = mount_notrunc(source, mnt, type, flags, optbuf);
-	}
-	if (res == -1 && errno == EINVAL) {
-		/* It could be an old version not supporting group_id */
-		sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
-			fd, rootmode, getuid());
-		res = mount_notrunc(source, mnt, type, flags, optbuf);
-	}
-	if (res == -1) {
-		int errno_save = errno;
-		if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
-			fprintf(stderr, "%s: 'fuseblk' support missing\n",
-				progname);
-		else
-			fprintf(stderr, "%s: mount failed: %s\n", progname,
-				strerror(errno_save));
-		goto err;
-	}
-	*sourcep = source;
-	*typep = type;
-	*mnt_optsp = mnt_opts;
-	free(fsname);
-	free(optbuf);
-
-	return 0;
-
-err:
-	free(fsname);
-	free(subtype);
-	free(source);
-	free(type);
-	free(mnt_opts);
-	free(optbuf);
-	return -1;
-}
-
-static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
-{
-	int res;
-	const char *mnt = *mntp;
-	const char *origmnt = mnt;
-	struct statfs fs_buf;
-	size_t i;
-
-	res = lstat(mnt, stbuf);
-	if (res == -1) {
-		fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
-			progname, mnt, strerror(errno));
-		return -1;
-	}
-
-	/* No permission checking is done for root */
-	if (getuid() == 0)
-		return 0;
-
-	if (S_ISDIR(stbuf->st_mode)) {
-		res = chdir(mnt);
-		if (res == -1) {
-			fprintf(stderr,
-				"%s: failed to chdir to mountpoint: %s\n",
-				progname, strerror(errno));
-			return -1;
-		}
-		mnt = *mntp = ".";
-		res = lstat(mnt, stbuf);
-		if (res == -1) {
-			fprintf(stderr,
-				"%s: failed to access mountpoint %s: %s\n",
-				progname, origmnt, strerror(errno));
-			return -1;
-		}
-
-		if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
-			fprintf(stderr, "%s: mountpoint %s not owned by user\n",
-				progname, origmnt);
-			return -1;
-		}
-
-		res = access(mnt, W_OK);
-		if (res == -1) {
-			fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
-				progname, origmnt);
-			return -1;
-		}
-	} else if (S_ISREG(stbuf->st_mode)) {
-		static char procfile[256];
-		*mountpoint_fd = open(mnt, O_WRONLY);
-		if (*mountpoint_fd == -1) {
-			fprintf(stderr, "%s: failed to open %s: %s\n",
-				progname, mnt, strerror(errno));
-			return -1;
-		}
-		res = fstat(*mountpoint_fd, stbuf);
-		if (res == -1) {
-			fprintf(stderr,
-				"%s: failed to access mountpoint %s: %s\n",
-				progname, mnt, strerror(errno));
-			return -1;
-		}
-		if (!S_ISREG(stbuf->st_mode)) {
-			fprintf(stderr,
-				"%s: mountpoint %s is no longer a regular file\n",
-				progname, mnt);
-			return -1;
-		}
-
-		sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
-		*mntp = procfile;
-	} else {
-		fprintf(stderr,
-			"%s: mountpoint %s is not a directory or a regular file\n",
-			progname, mnt);
-		return -1;
-	}
-
-	/* Do not permit mounting over anything in procfs - it has a couple
-	 * places to which we have "write access" without being supposed to be
-	 * able to just put anything we want there.
-	 * Luckily, without allow_other, we can't get other users to actually
-	 * use any fake information we try to put there anyway.
-	 * Use a whitelist to be safe. */
-	if (statfs(*mntp, &fs_buf)) {
-		fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
-			progname, mnt, strerror(errno));
-		return -1;
-	}
-
-	/* Define permitted filesystems for the mount target. This was
-	 * originally the same list as used by the ecryptfs mount helper
-	 * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
-	 * but got expanded as we found more filesystems that needed to be
-	 * overlayed. */
-	typeof(fs_buf.f_type) f_type_whitelist[] = {
-		0x61756673 /* AUFS_SUPER_MAGIC */,
-		0x00000187 /* AUTOFS_SUPER_MAGIC */,
-		0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
-		0x9123683E /* BTRFS_SUPER_MAGIC */,
-		0x00C36400 /* CEPH_SUPER_MAGIC */,
-		0xFF534D42 /* CIFS_MAGIC_NUMBER */,
-		0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
-		0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
-		0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
-		0xF2F52010 /* F2FS_SUPER_MAGIC */,
-		0x65735546 /* FUSE_SUPER_MAGIC */,
-		0x01161970 /* GFS2_MAGIC */,
-		0x47504653 /* GPFS_SUPER_MAGIC */,
-		0x0000482b /* HFSPLUS_SUPER_MAGIC */,
-		0x000072B6 /* JFFS2_SUPER_MAGIC */,
-		0x3153464A /* JFS_SUPER_MAGIC */,
-		0x0BD00BD0 /* LL_SUPER_MAGIC */,
-		0X00004D44 /* MSDOS_SUPER_MAGIC */,
-		0x0000564C /* NCP_SUPER_MAGIC */,
-		0x00006969 /* NFS_SUPER_MAGIC */,
-		0x00003434 /* NILFS_SUPER_MAGIC */,
-		0x5346544E /* NTFS_SB_MAGIC */,
-		0x5346414f /* OPENAFS_SUPER_MAGIC */,
-		0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
-		0x52654973 /* REISERFS_SUPER_MAGIC */,
-		0xFE534D42 /* SMB2_SUPER_MAGIC */,
-		0x73717368 /* SQUASHFS_MAGIC */,
-		0x01021994 /* TMPFS_MAGIC */,
-		0x24051905 /* UBIFS_SUPER_MAGIC */,
-		0x736675005346544e /* UFSD */,
-		0x58465342 /* XFS_SB_MAGIC */,
-		0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
-	};
-	for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
-		if (f_type_whitelist[i] == fs_buf.f_type)
-			return 0;
-	}
-
-	fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
-		progname, (unsigned long)fs_buf.f_type);
-	return -1;
-}
-
-static int try_open(const char *dev, char **devp, int silent)
-{
-	int fd = open(dev, O_RDWR);
-	if (fd != -1) {
-		*devp = strdup(dev);
-		if (*devp == NULL) {
-			fprintf(stderr, "%s: failed to allocate memory\n",
-				progname);
-			close(fd);
-			fd = -1;
-		}
-	} else if (errno == ENODEV ||
-		   errno == ENOENT)/* check for ENOENT too, for the udev case */
-		return -2;
-	else if (!silent) {
-		fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
-			strerror(errno));
-	}
-	return fd;
-}
-
-static int try_open_fuse_device(char **devp)
-{
-	int fd;
-
-	drop_privs();
-	fd = try_open(FUSE_DEV, devp, 0);
-	restore_privs();
-	return fd;
-}
-
-static int open_fuse_device(char **devp)
-{
-	int fd = try_open_fuse_device(devp);
-	if (fd >= -1)
-		return fd;
-
-	fprintf(stderr,
-		"%s: fuse device not found, try 'modprobe fuse' first\n",
-		progname);
-
-	return -1;
-}
-
-
-static int mount_fuse(const char *mnt, const char *opts, const char **type)
-{
-	int res;
-	int fd;
-	char *dev;
-	struct stat stbuf;
-	char *source = NULL;
-	char *mnt_opts = NULL;
-	const char *real_mnt = mnt;
-	int mountpoint_fd = -1;
-
-	fd = open_fuse_device(&dev);
-	if (fd == -1)
-		return -1;
-
-	drop_privs();
-	read_conf();
-
-	if (getuid() != 0 && mount_max != -1) {
-		int mount_count = count_fuse_fs();
-		if (mount_count >= mount_max) {
-			fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
-			goto fail_close_fd;
-		}
-	}
-
-	res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
-	restore_privs();
-	if (res != -1)
-		res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
-			       fd, opts, dev, &source, &mnt_opts);
-
-	if (mountpoint_fd != -1)
-		close(mountpoint_fd);
-
-	if (res == -1)
-		goto fail_close_fd;
-
-	res = chdir("/");
-	if (res == -1) {
-		fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
-		goto fail_close_fd;
-	}
-
-	if (geteuid() == 0) {
-		res = add_mount(source, mnt, *type, mnt_opts);
-		if (res == -1) {
-			/* Can't clean up mount in a non-racy way */
-			goto fail_close_fd;
-		}
-	}
-
-out_free:
-	free(source);
-	free(mnt_opts);
-	free(dev);
-
-	return fd;
-
-fail_close_fd:
-	close(fd);
-	fd = -1;
-	goto out_free;
-}
-
-static int send_fd(int sock_fd, int fd)
-{
-	int retval;
-	struct msghdr msg;
-	struct cmsghdr *p_cmsg;
-	struct iovec vec;
-	size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
-	int *p_fds;
-	char sendchar = 0;
-
-	msg.msg_control = cmsgbuf;
-	msg.msg_controllen = sizeof(cmsgbuf);
-	p_cmsg = CMSG_FIRSTHDR(&msg);
-	p_cmsg->cmsg_level = SOL_SOCKET;
-	p_cmsg->cmsg_type = SCM_RIGHTS;
-	p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
-	p_fds = (int *) CMSG_DATA(p_cmsg);
-	*p_fds = fd;
-	msg.msg_controllen = p_cmsg->cmsg_len;
-	msg.msg_name = NULL;
-	msg.msg_namelen = 0;
-	msg.msg_iov = &vec;
-	msg.msg_iovlen = 1;
-	msg.msg_flags = 0;
-	/* "To pass file descriptors or credentials you need to send/read at
-	 * least one byte" (man 7 unix) */
-	vec.iov_base = &sendchar;
-	vec.iov_len = sizeof(sendchar);
-	while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
-	if (retval != 1) {
-		perror("sending file descriptor");
-		return -1;
-	}
-	return 0;
-}
-
-/* The parent fuse process has died: decide whether to auto_unmount.
- *
- * In the normal case (umount or fusermount -u), the filesystem
- * has already been unmounted. If we simply unmount again we can
- * cause problems with stacked mounts (e.g. autofs).
- *
- * So we unmount here only in abnormal case where fuse process has
- * died without unmount happening. To detect this, we first look in
- * the mount table to make sure the mountpoint is still mounted and
- * has proper type. If so, we then see if opening the mount dir is
- * returning 'Transport endpoint is not connected'.
- *
- * The order of these is important, because if autofs is in use,
- * opening the dir to check for ENOTCONN will cause a new mount
- * in the normal case where filesystem has been unmounted cleanly.
- */
-static int should_auto_unmount(const char *mnt, const char *type)
-{
-	char *copy;
-	const char *last;
-	int result = 0;
-	int fd;
-
-	copy = strdup(mnt);
-	if (copy == NULL) {
-	fprintf(stderr, "%s: failed to allocate memory\n", progname);
-		return 0;
-	}
-
-	if (chdir_to_parent(copy, &last) == -1)
-		goto out;
-	if (check_is_mount(last, mnt, type) == -1)
-		goto out;
-
-	fd = open(mnt, O_RDONLY);
-	if (fd != -1) {
-		close(fd);
-	} else {
-		result = errno == ENOTCONN;
-	}
-out:
-	free(copy);
-	return result;
-}
-
-static void usage(void)
-{
-	printf("%s: [options] mountpoint\n"
-	       "Options:\n"
-	       " -h		    print help\n"
-	       " -V		    print version\n"
-	       " -o opt[,opt...]    mount options\n"
-	       " -u		    unmount\n"
-	       " -q		    quiet\n"
-	       " -z		    lazy unmount\n",
-	       progname);
-	exit(1);
-}
-
-static void show_version(void)
-{
-	printf("fusermount3 version: %s\n", PACKAGE_VERSION);
-	exit(0);
-}
-
-int main(int argc, char *argv[])
-{
-	sigset_t sigset;
-	int ch;
-	int fd;
-	int res;
-	char *origmnt;
-	char *mnt;
-	static int unmount = 0;
-	static int lazy = 0;
-	static int quiet = 0;
-	char *commfd;
-	int cfd;
-	const char *opts = "";
-	const char *type = NULL;
-
-	static const struct option long_opts[] = {
-		{"unmount", no_argument, NULL, 'u'},
-		{"lazy",    no_argument, NULL, 'z'},
-		{"quiet",   no_argument, NULL, 'q'},
-		{"help",    no_argument, NULL, 'h'},
-		{"version", no_argument, NULL, 'V'},
-		{0, 0, 0, 0}};
-
-	progname = strdup(argc > 0 ? argv[0] : "fusermount");
-	if (progname == NULL) {
-		fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
-		exit(1);
-	}
-
-	while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
-				 NULL)) != -1) {
-		switch (ch) {
-		case 'h':
-			usage();
-			break;
-
-		case 'V':
-			show_version();
-			break;
-
-		case 'o':
-			opts = optarg;
-			break;
-
-		case 'u':
-			unmount = 1;
-			break;
-
-		case 'z':
-			lazy = 1;
-			break;
-
-		case 'q':
-			quiet = 1;
-			break;
-
-		default:
-			exit(1);
-		}
-	}
-
-	if (lazy && !unmount) {
-		fprintf(stderr, "%s: -z can only be used with -u\n", progname);
-		exit(1);
-	}
-
-	if (optind >= argc) {
-		fprintf(stderr, "%s: missing mountpoint argument\n", progname);
-		exit(1);
-	} else if (argc > optind + 1) {
-		fprintf(stderr, "%s: extra arguments after the mountpoint\n",
-			progname);
-		exit(1);
-	}
-
-	origmnt = argv[optind];
-
-	drop_privs();
-	mnt = fuse_mnt_resolve_path(progname, origmnt);
-	if (mnt != NULL) {
-		res = chdir("/");
-		if (res == -1) {
-			fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
-			goto err_out;
-		}
-	}
-	restore_privs();
-	if (mnt == NULL)
-		exit(1);
-
-	umask(033);
-	if (unmount)
-		goto do_unmount;
-
-	commfd = getenv(FUSE_COMMFD_ENV);
-	if (commfd == NULL) {
-		fprintf(stderr, "%s: old style mounting not supported\n",
-			progname);
-		goto err_out;
-	}
-
-	fd = mount_fuse(mnt, opts, &type);
-	if (fd == -1)
-		goto err_out;
-
-	cfd = atoi(commfd);
-	res = send_fd(cfd, fd);
-	if (res == -1)
-		goto err_out;
-	close(fd);
-
-	if (!auto_unmount) {
-		free(mnt);
-		return 0;
-	}
-
-	/* Become a daemon and wait for the parent to exit or die.
-	   ie For the control socket to get closed.
-	   btw We don't want to use daemon() function here because
-	   it forks and messes with the file descriptors. */
-	setsid();
-	res = chdir("/");
-	if (res == -1) {
-		fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
-		goto err_out;
-	}
-
-	sigfillset(&sigset);
-	sigprocmask(SIG_BLOCK, &sigset, NULL);
-
-	lazy  = 1;
-	quiet = 1;
-
-	while (1) {
-		unsigned char buf[16];
-		int n = recv(cfd, buf, sizeof(buf), 0);
-		if (!n)
-			break;
-
-		if (n < 0) {
-			if (errno == EINTR)
-				continue;
-			break;
-		}
-	}
-
-	if (!should_auto_unmount(mnt, type)) {
-		goto success_out;
-	}
-
-do_unmount:
-	if (geteuid() == 0)
-		res = unmount_fuse(mnt, quiet, lazy);
-	else {
-		res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
-		if (res == -1 && !quiet)
-			fprintf(stderr,
-				"%s: failed to unmount %s: %s\n",
-				progname, mnt, strerror(errno));
-	}
-	if (res == -1)
-		goto err_out;
-
-success_out:
-	free(mnt);
-	return 0;
-
-err_out:
-	free(mnt);
-	exit(1);
-}
diff --git a/util/init_script b/util/init_script
deleted file mode 100755
index a4b8e7b..0000000
--- a/util/init_script
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/bin/sh
-### BEGIN INIT INFO
-# Provides:          fuse
-# Required-Start:    
-# Should-Start:      udev
-# Required-Stop:     
-# Default-Start:     S
-# Default-Stop:
-# Short-Description: Start and stop fuse.
-# Description:       Load the fuse module and mount the fuse control
-#	filesystem.
-### END INIT INFO
-
-set -e
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-MOUNTPOINT=/sys/fs/fuse/connections
-
-# Gracefully exit if the package has been removed.
-which fusermount3 &>/dev/null || exit 5
-
-# Define LSB log_* functions.
-. /lib/lsb/init-functions
-
-case "$1" in
-    start|restart|force-reload)
-	if ! grep -qw fuse /proc/filesystems; then
-		echo -n "Loading fuse module"
-		if ! modprobe fuse >/dev/null 2>&1; then
-			echo " failed!"
-			exit 1
-		else
-			echo "."
-		fi
-	else
-		echo "Fuse filesystem already available."
-	fi
-	if grep -qw fusectl /proc/filesystems && \
-	   ! grep -qw $MOUNTPOINT /proc/mounts; then
-		echo -n "Mounting fuse control filesystem"
-		if ! mount -t fusectl fusectl $MOUNTPOINT >/dev/null 2>&1; then
-			echo " failed!"
-			exit 1
-		else
-			echo "."
-		fi
-	else
-		echo "Fuse control filesystem already available."
-	fi
-	;;
-    stop)
-	if ! grep -qw fuse /proc/filesystems; then
-		echo "Fuse filesystem not loaded."
-		exit 7
-	fi
-	if grep -qw $MOUNTPOINT /proc/mounts; then
-		echo -n "Unmounting fuse control filesystem"
-		if ! umount $MOUNTPOINT >/dev/null 2>&1; then
-			echo " failed!"
-		else
-			echo "."
-		fi
-	else
-		echo "Fuse control filesystem not mounted."
-	fi
-	if grep -qw "^fuse" /proc/modules; then
-		echo -n "Unloading fuse module"
-		if ! rmmod fuse >/dev/null 2>&1; then
-			echo " failed!"
-		else
-			echo "."
-		fi
-	else
-		echo "Fuse module not loaded."
-	fi
-	;;
-    status)
-	echo -n "Checking fuse filesystem"
-	if ! grep -qw fuse /proc/filesystems; then
-		echo " not available."
-		exit 3
-	else
-		echo " ok."
-	fi
-	;;
-  *)
-	echo "Usage: $0 {start|stop|restart|force-reload|status}"
-	exit 1
-	;;
-esac
-
-exit 0
diff --git a/util/install_helper.sh b/util/install_helper.sh
deleted file mode 100755
index cb649a7..0000000
--- a/util/install_helper.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/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"
-udevrulesdir="$3"
-useroot="$4"
-
-# Both sysconfdir and bindir are absolute paths (since they are joined
-# with --prefix in meson.build), but need to be interpreted relative
-# to DESTDIR (if specified).
-
-if [ -z "${DESTDIR}" ]; then
-    # Prevent warnings about uninitialized variable
-    DESTDIR=""
-else
-    # Get rid of duplicate slash
-    DESTDIR="${DESTDIR%/}"
-fi
-
-install -D -m 644 "${MESON_SOURCE_ROOT}/util/fuse.conf" \
-	"${DESTDIR}${sysconfdir}/fuse.conf"
-
-if $useroot; then
-    chown root:root "${DESTDIR}${bindir}/fusermount3"
-    chmod u+s "${DESTDIR}${bindir}/fusermount3"
-
-    if test ! -e "${DESTDIR}/dev/fuse"; then
-        mkdir -p "${DESTDIR}/dev"
-        mknod "${DESTDIR}/dev/fuse" -m 0666 c 10 229
-    fi
-fi
-
-install -D -m 644 "${MESON_SOURCE_ROOT}/util/udev.rules" \
-        "${DESTDIR}${udevrulesdir}/99-fuse3.rules"
-
-install -D -m 755 "${MESON_SOURCE_ROOT}/util/init_script" \
-        "${DESTDIR}/etc/init.d/fuse3"
-
-
-if test -x /usr/sbin/update-rc.d && test -z "${DESTDIR}"; then
-    /usr/sbin/update-rc.d fuse3 start 34 S . start 41 0 6 . || /bin/true
-else
-    echo "== FURTHER ACTION REQUIRED =="
-    echo "Make sure that your init system will start the ${DESTDIR}/etc/init.d/fuse3 init script"
-fi
-
-
diff --git a/util/meson.build b/util/meson.build
deleted file mode 100644
index 577668f..0000000
--- a/util/meson.build
+++ /dev/null
@@ -1,29 +0,0 @@
-fuseconf_path = join_paths(get_option('prefix'), get_option('sysconfdir'), 'fuse.conf')
-
-executable('fusermount3', ['fusermount.c', '../lib/mount_util.c'],
-           include_directories: include_dirs,
-           install: true,
-           install_dir: get_option('bindir'),
-           c_args: '-DFUSE_CONF="@0@"'.format(fuseconf_path))
-
-executable('mount.fuse3', ['mount.fuse.c'],
-           include_directories: include_dirs,
-           link_with: [ libfuse ],
-           install: true,
-           install_dir: get_option('sbindir'),
-           c_args: '-DFUSE_USE_VERSION=35')
-
-
-udevrulesdir = get_option('udevrulesdir')
-if udevrulesdir == ''
-  udev = dependency('udev')
-  udevrulesdir = join_paths(udev.get_pkgconfig_variable('udevdir'), 'rules.d')
-endif
-
-meson.add_install_script('install_helper.sh',
-                         join_paths(get_option('prefix'), get_option('sysconfdir')),
-                         join_paths(get_option('prefix'), get_option('bindir')),
-                         udevrulesdir,
-                         '@0@'.format(get_option('useroot')))
-
-
diff --git a/util/mount.fuse.c b/util/mount.fuse.c
deleted file mode 100644
index a6e5629..0000000
--- a/util/mount.fuse.c
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
-  FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
-
-  This program can be distributed under the terms of the GNU GPLv2.
-  See the file COPYING.
-*/
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdint.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <sys/wait.h>
-
-#ifdef linux
-#include <sys/prctl.h>
-#include <sys/syscall.h>
-#include <linux/capability.h>
-#include <linux/securebits.h>
-/* for 2.6 kernels */
-#if !defined(SECBIT_KEEP_CAPS) && defined(SECURE_KEEP_CAPS)
-#define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS))
-#endif
-#if !defined(SECBIT_KEEP_CAPS_LOCKED) && defined(SECURE_KEEP_CAPS_LOCKED)
-#define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED))
-#endif
-#if !defined(SECBIT_NO_SETUID_FIXUP) && defined(SECURE_NO_SETUID_FIXUP)
-#define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP))
-#endif
-#if !defined(SECBIT_NO_SETUID_FIXUP_LOCKED) && defined(SECURE_NO_SETUID_FIXUP_LOCKED)
-#define SECBIT_NO_SETUID_FIXUP_LOCKED (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED))
-#endif
-#if !defined(SECBIT_NOROOT) && defined(SECURE_NOROOT)
-#define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT))
-#endif
-#if !defined(SECBIT_NOROOT_LOCKED) && defined(SECURE_NOROOT_LOCKED)
-#define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED))
-#endif
-#endif
-
-#include "fuse.h"
-
-static char *progname;
-
-static char *xstrdup(const char *s)
-{
-	char *t = strdup(s);
-	if (!t) {
-		fprintf(stderr, "%s: failed to allocate memory\n", progname);
-		exit(1);
-	}
-	return t;
-}
-
-static void *xrealloc(void *oldptr, size_t size)
-{
-	void *ptr = realloc(oldptr, size);
-	if (!ptr) {
-		fprintf(stderr, "%s: failed to allocate memory\n", progname);
-		exit(1);
-	}
-	return ptr;
-}
-
-static void add_arg(char **cmdp, const char *opt)
-{
-	size_t optlen = strlen(opt);
-	size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
-	if (optlen >= (SIZE_MAX - cmdlen - 4)/4) {
-		fprintf(stderr, "%s: argument too long\n", progname);
-		exit(1);
-	}
-	char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
-	char *s;
-	s = cmd + cmdlen;
-	if (*cmdp)
-		*s++ = ' ';
-
-	*s++ = '\'';
-	for (; *opt; opt++) {
-		if (*opt == '\'') {
-			*s++ = '\'';
-			*s++ = '\\';
-			*s++ = '\'';
-			*s++ = '\'';
-		} else
-			*s++ = *opt;
-	}
-	*s++ = '\'';
-	*s = '\0';
-	*cmdp = cmd;
-}
-
-static char *add_option(const char *opt, char *options)
-{
-	int oldlen = options ? strlen(options) : 0;
-
-	options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
-	if (!oldlen)
-		strcpy(options, opt);
-	else {
-		strcat(options, ",");
-		strcat(options, opt);
-	}
-	return options;
-}
-
-static int prepare_fuse_fd(const char *mountpoint, const char* subtype,
-			   const char *options)
-{
-	int fuse_fd = -1;
-	int flags = -1;
-	int subtype_len = strlen(subtype) + 9;
-	char* options_copy = xrealloc(NULL, subtype_len);
-
-	snprintf(options_copy, subtype_len, "subtype=%s", subtype);
-	options_copy = add_option(options, options_copy);
-	fuse_fd = fuse_open_channel(mountpoint, options_copy);
-	if (fuse_fd == -1) {
-		exit(1);
-	}
-
-	flags = fcntl(fuse_fd, F_GETFD);
-	if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) {
-		fprintf(stderr, "%s: Failed to clear CLOEXEC: %s\n",
-			progname, strerror(errno));
-		exit(1);
-	}
-
-	return fuse_fd;
-}
-
-#ifdef linux
-static uint64_t get_capabilities(void)
-{
-	/*
-	 * This invokes the capset syscall directly to avoid the libcap
-	 * dependency, which isn't really justified just for this.
-	 */
-	struct __user_cap_header_struct header = {
-		.version = _LINUX_CAPABILITY_VERSION_3,
-		.pid = 0,
-	};
-	struct __user_cap_data_struct data[2];
-	memset(data, 0, sizeof(data));
-	if (syscall(SYS_capget, &header, data) == -1) {
-		fprintf(stderr, "%s: Failed to get capabilities: %s\n",
-			progname, strerror(errno));
-		exit(1);
-	}
-
-	return data[0].effective | ((uint64_t) data[1].effective << 32);
-}
-
-static void set_capabilities(uint64_t caps)
-{
-	/*
-	 * This invokes the capset syscall directly to avoid the libcap
-	 * dependency, which isn't really justified just for this.
-	 */
-	struct __user_cap_header_struct header = {
-		.version = _LINUX_CAPABILITY_VERSION_3,
-		.pid = 0,
-	};
-	struct __user_cap_data_struct data[2];
-	memset(data, 0, sizeof(data));
-	data[0].effective = data[0].permitted = caps;
-	data[1].effective = data[1].permitted = caps >> 32;
-	if (syscall(SYS_capset, &header, data) == -1) {
-		fprintf(stderr, "%s: Failed to set capabilities: %s\n",
-			progname, strerror(errno));
-		exit(1);
-	}
-}
-
-static void drop_and_lock_capabilities(void)
-{
-	/* Set and lock securebits. */
-	if (prctl(PR_SET_SECUREBITS,
-		  SECBIT_KEEP_CAPS_LOCKED |
-		  SECBIT_NO_SETUID_FIXUP |
-		  SECBIT_NO_SETUID_FIXUP_LOCKED |
-		  SECBIT_NOROOT |
-		  SECBIT_NOROOT_LOCKED) == -1) {
-		fprintf(stderr, "%s: Failed to set securebits %s\n",
-			progname, strerror(errno));
-		exit(1);
-	}
-
-	/* Clear the capability bounding set. */
-	int cap;
-	for (cap = 0; ; cap++) {
-		int cap_status = prctl(PR_CAPBSET_READ, cap);
-		if (cap_status == 0) {
-			continue;
-		}
-		if (cap_status == -1 && errno == EINVAL) {
-			break;
-		}
-
-		if (cap_status != 1) {
-			fprintf(stderr,
-				"%s: Failed to get capability %u: %s\n",
-				progname, cap, strerror(errno));
-			exit(1);
-		}
-		if (prctl(PR_CAPBSET_DROP, cap) == -1) {
-			fprintf(stderr,
-				"%s: Failed to drop capability %u: %s\n",
-				progname, cap, strerror(errno));
-		}
-	}
-
-	/* Drop capabilities. */
-	set_capabilities(0);
-
-	/* Prevent re-acquisition of privileges. */
-	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
-		fprintf(stderr, "%s: Failed to set no_new_privs: %s\n",
-			progname, strerror(errno));
-		exit(1);
-	}
-}
-#endif
-
-int main(int argc, char *argv[])
-{
-	char *type = NULL;
-	char *source;
-	char *dup_source = NULL;
-	const char *mountpoint;
-	char *basename;
-	char *options = NULL;
-	char *command = NULL;
-	char *setuid_name = NULL;
-	int i;
-	int dev = 1;
-	int suid = 1;
-	int pass_fuse_fd = 0;
-	int fuse_fd = 0;
-	int drop_privileges = 0;
-	char *dev_fd_mountpoint = NULL;
-
-	progname = argv[0];
-	basename = strrchr(argv[0], '/');
-	if (basename)
-		basename++;
-	else
-		basename = argv[0];
-
-	if (strncmp(basename, "mount.fuse.", 11) == 0)
-		type = basename + 11;
-	if (strncmp(basename, "mount.fuseblk.", 14) == 0)
-		type = basename + 14;
-
-	if (type && !type[0])
-		type = NULL;
-
-	if (argc < 3) {
-		fprintf(stderr,
-			"usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
-			progname, type ? "source" : "type#[source]");
-		exit(1);
-	}
-
-	source = argv[1];
-	if (!source[0])
-		source = NULL;
-
-	mountpoint = argv[2];
-
-	for (i = 3; i < argc; i++) {
-		if (strcmp(argv[i], "-v") == 0) {
-			continue;
-		} else if (strcmp(argv[i], "-t") == 0) {
-			i++;
-
-			if (i == argc) {
-				fprintf(stderr,
-					"%s: missing argument to option '-t'\n",
-					progname);
-				exit(1);
-			}
-			type = argv[i];
-			if (strncmp(type, "fuse.", 5) == 0)
-				type += 5;
-			else if (strncmp(type, "fuseblk.", 8) == 0)
-				type += 8;
-
-			if (!type[0]) {
-				fprintf(stderr,
-					"%s: empty type given as argument to option '-t'\n",
-					progname);
-				exit(1);
-			}
-		} else	if (strcmp(argv[i], "-o") == 0) {
-			char *opts;
-			char *opt;
-			i++;
-			if (i == argc)
-				break;
-
-			opts = xstrdup(argv[i]);
-			opt = strtok(opts, ",");
-			while (opt) {
-				int j;
-				int ignore = 0;
-				const char *ignore_opts[] = { "",
-							      "user",
-							      "nofail",
-							      "nouser",
-							      "users",
-							      "auto",
-							      "noauto",
-							      "_netdev",
-							      NULL};
-				if (strncmp(opt, "setuid=", 7) == 0) {
-					setuid_name = xstrdup(opt + 7);
-					ignore = 1;
-				} else if (strcmp(opt,
-						  "drop_privileges") == 0) {
-					pass_fuse_fd = 1;
-					drop_privileges = 1;
-					ignore = 1;
-				}
-				for (j = 0; ignore_opts[j]; j++)
-					if (strcmp(opt, ignore_opts[j]) == 0)
-						ignore = 1;
-
-				if (!ignore) {
-					if (strcmp(opt, "nodev") == 0)
-						dev = 0;
-					else if (strcmp(opt, "nosuid") == 0)
-						suid = 0;
-
-					options = add_option(opt, options);
-				}
-				opt = strtok(NULL, ",");
-			}
-			free(opts);
-		}
-	}
-
-	if (drop_privileges) {
-		uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) |
-				CAP_TO_MASK(CAP_SYS_ADMIN);
-		if ((get_capabilities() & required_caps) != required_caps) {
-			fprintf(stderr, "%s: drop_privileges was requested, which launches the FUSE file system fully unprivileged. In order to do so %s must be run with privileges, please invoke with CAP_SYS_ADMIN and CAP_SETPCAP (e.g. as root).\n",
-			progname, progname);
-			exit(1);
-		}
-	}
-
-	if (dev)
-		options = add_option("dev", options);
-	if (suid)
-		options = add_option("suid", options);
-
-	if (!type) {
-		if (source) {
-			dup_source = xstrdup(source);
-			type = dup_source;
-			source = strchr(type, '#');
-			if (source)
-				*source++ = '\0';
-			if (!type[0]) {
-				fprintf(stderr, "%s: empty filesystem type\n",
-					progname);
-				exit(1);
-			}
-		} else {
-			fprintf(stderr, "%s: empty source\n", progname);
-			exit(1);
-		}
-	}
-
-	if (setuid_name && setuid_name[0]) {
-#ifdef linux
-		if (drop_privileges) {
-			/*
-			 * Make securebits more permissive before calling
-			 * setuid(). Specifically, if SECBIT_KEEP_CAPS and
-			 * SECBIT_NO_SETUID_FIXUP weren't set, setuid() would
-			 * have the side effect of dropping all capabilities,
-			 * and we need to retain CAP_SETPCAP in order to drop
-			 * all privileges before exec().
-			 */
-			if (prctl(PR_SET_SECUREBITS,
-				  SECBIT_KEEP_CAPS |
-				  SECBIT_NO_SETUID_FIXUP) == -1) {
-				fprintf(stderr,
-					"%s: Failed to set securebits %s\n",
-					progname, strerror(errno));
-				exit(1);
-			}
-		}
-#endif
-
-		struct passwd *pwd = getpwnam(setuid_name);
-		if (!pwd || setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) {
-			fprintf(stderr, "%s: Failed to setuid to %s: %s\n",
-				progname, setuid_name, strerror(errno));
-			exit(1);
-		}
-	} else if (!getenv("HOME")) {
-		/* Hack to make filesystems work in the boot environment */
-		setenv("HOME", "/root", 0);
-	}
-
-	if (pass_fuse_fd)  {
-		fuse_fd = prepare_fuse_fd(mountpoint, type, options);
-		dev_fd_mountpoint = xrealloc(NULL, 20);
-		snprintf(dev_fd_mountpoint, 20, "/dev/fd/%u", fuse_fd);
-		mountpoint = dev_fd_mountpoint;
-	}
-
-#ifdef linux
-	if (drop_privileges) {
-		drop_and_lock_capabilities();
-	}
-#endif
-	add_arg(&command, type);
-	if (source)
-		add_arg(&command, source);
-	add_arg(&command, mountpoint);
-	if (options) {
-		add_arg(&command, "-o");
-		add_arg(&command, options);
-	}
-
-	free(options);
-	free(dev_fd_mountpoint);
-	free(dup_source);
-	free(setuid_name);
-
-	execl("/bin/sh", "/bin/sh", "-c", command, NULL);
-	fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
-		strerror(errno));
-	
-	if (pass_fuse_fd)
-		close(fuse_fd);
-	free(command);
-	return 1;
-}
diff --git a/util/udev.rules b/util/udev.rules
deleted file mode 100644
index 9585111..0000000
--- a/util/udev.rules
+++ /dev/null
@@ -1 +0,0 @@
-KERNEL=="fuse", MODE="0666"